-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add policy variable support (#415)
* feat: utility method for policy variable support (#339) * feat: utility method for updating permissions for device (#344) * fix: add support for replacing multiple policy variables (#346) * feat: allow device actions to be evaluated based on policy variable permissions (#349) * feat: add integration test for policy variable authorization (#355) * feat: make PermissionEvaluationUtils non static * fix: check style * fix: pmd * fix: refactor PermissionEvaluationUtils * chore: check style * fix: remove device auth client mocks * fix: remove permission caching * fix: case insensitive matching * fix: pmd * fix: update benchmark * fix: update benchmark * fix: typo * fix: exception handling * fix: remove ipc from integration tests and add benchmark for policy vars * chore: parameterize integration tests * feat: create foundation to support more policy variables * feat: identify policy variables during config time * feat: performance improvements * chore: renamed attribute provider exception to policy exception * chore: use set instead of list to store user policy variables * chore: fix policy exception logging and use coerce to string to avoid npe --------- Co-authored-by: Cynthia Yan <[email protected]> Co-authored-by: Robert Manning <[email protected]>
- Loading branch information
1 parent
4c7a61a
commit 1a7a07a
Showing
19 changed files
with
765 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
158 changes: 158 additions & 0 deletions
158
...grass/integrationtests/deviceauth/EvaluateClientDeviceActionsWithPolicyVariablesTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package com.aws.greengrass.integrationtests.deviceauth; | ||
|
||
import com.aws.greengrass.clientdevices.auth.AuthorizationRequest; | ||
import com.aws.greengrass.clientdevices.auth.ClientDevicesAuthService; | ||
import com.aws.greengrass.clientdevices.auth.api.ClientDevicesAuthServiceApi; | ||
import com.aws.greengrass.clientdevices.auth.certificate.CertificateHelper; | ||
import com.aws.greengrass.clientdevices.auth.helpers.CertificateTestHelpers; | ||
import com.aws.greengrass.clientdevices.auth.iot.Certificate; | ||
import com.aws.greengrass.clientdevices.auth.iot.CertificateRegistry; | ||
import com.aws.greengrass.clientdevices.auth.iot.IotAuthClient; | ||
import com.aws.greengrass.clientdevices.auth.iot.IotAuthClientFake; | ||
import com.aws.greengrass.clientdevices.auth.iot.Thing; | ||
import com.aws.greengrass.clientdevices.auth.iot.infra.ThingRegistry; | ||
import com.aws.greengrass.dependency.State; | ||
import com.aws.greengrass.lifecyclemanager.Kernel; | ||
import com.aws.greengrass.logging.impl.config.LogConfig; | ||
import com.aws.greengrass.mqttclient.spool.SpoolerStoreException; | ||
import com.aws.greengrass.testcommons.testutilities.GGExtension; | ||
import com.aws.greengrass.testcommons.testutilities.UniqueRootPathExtension; | ||
import org.junit.jupiter.api.AfterEach; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.junit.jupiter.api.extension.ExtensionContext; | ||
import org.junit.jupiter.api.io.TempDir; | ||
import org.junit.jupiter.params.ParameterizedTest; | ||
import org.junit.jupiter.params.provider.Arguments; | ||
import org.junit.jupiter.params.provider.MethodSource; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
|
||
import java.nio.file.NoSuchFileException; | ||
import java.nio.file.Path; | ||
import java.security.cert.X509Certificate; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.stream.Stream; | ||
|
||
import static com.aws.greengrass.testcommons.testutilities.ExceptionLogProtector.ignoreExceptionOfType; | ||
import static com.aws.greengrass.testcommons.testutilities.TestUtils.createServiceStateChangeWaiter; | ||
import static org.hamcrest.MatcherAssert.assertThat; | ||
import static org.hamcrest.Matchers.is; | ||
|
||
@SuppressWarnings("PMD.UnusedPrivateMethod") | ||
@ExtendWith({GGExtension.class, UniqueRootPathExtension.class, MockitoExtension.class}) | ||
public class EvaluateClientDeviceActionsWithPolicyVariablesTest { | ||
@TempDir | ||
Path rootDir; | ||
private Kernel kernel; | ||
|
||
private Certificate certificate; | ||
|
||
private String clientPem; | ||
private ClientDevicesAuthServiceApi api; | ||
|
||
@BeforeEach | ||
void beforeEach(ExtensionContext context) throws Exception { | ||
ignoreExceptionOfType(context, SpoolerStoreException.class); | ||
ignoreExceptionOfType(context, NoSuchFileException.class); // Loading CA keystore | ||
|
||
// Set this property for kernel to scan its own classpath to find plugins | ||
System.setProperty("aws.greengrass.scanSelfClasspath", "true"); | ||
kernel = new Kernel(); | ||
|
||
// Set up Iot auth client | ||
IotAuthClientFake iotAuthClientFake = new IotAuthClientFake(); | ||
kernel.getContext().put(IotAuthClient.class, iotAuthClientFake); | ||
|
||
// start CDA service with configuration | ||
startNucleusWithConfig("config.yaml"); | ||
|
||
// create certificate that client devices can use | ||
setClientDeviceCertificatePem(); | ||
|
||
this.api = kernel.getContext().get(ClientDevicesAuthServiceApi.class); | ||
} | ||
|
||
private void setClientDeviceCertificatePem() throws Exception{ | ||
// create certificate to attach to thing | ||
List<X509Certificate> clientCertificates = CertificateTestHelpers.createClientCertificates(1); | ||
String clientPem = CertificateHelper.toPem(clientCertificates.get(0)); | ||
CertificateRegistry certificateRegistry = kernel.getContext().get(CertificateRegistry.class); | ||
Certificate cert = certificateRegistry.getOrCreateCertificate(clientPem); | ||
cert.setStatus(Certificate.Status.ACTIVE); | ||
|
||
// activate certificate | ||
certificateRegistry.updateCertificate(cert); | ||
this.certificate = cert; | ||
this.clientPem = clientPem; | ||
} | ||
|
||
private String getClientDeviceSessionAuthToken(String thingName, String clientPem) throws Exception { | ||
// create thing - needed for api call to validate thing + certificate | ||
ThingRegistry thingRegistry = kernel.getContext().get(ThingRegistry.class); | ||
Thing MyThing = thingRegistry.createThing(thingName); | ||
MyThing.attachCertificate(certificate.getCertificateId()); | ||
thingRegistry.updateThing(MyThing); | ||
|
||
// create client device session and get token | ||
return api.getClientDeviceAuthToken("mqtt", new HashMap<String, String>() {{ | ||
put("clientId", thingName); | ||
put("certificatePem", clientPem); | ||
put("username", "foo"); | ||
put("password", "bar"); | ||
}}); | ||
} | ||
|
||
private void startNucleusWithConfig(String configFileName) { | ||
kernel.parseArgs("-r", rootDir.toAbsolutePath().toString(), "-i", | ||
getClass().getResource(configFileName).toString()); | ||
Runnable mainRunning = createServiceStateChangeWaiter(kernel, | ||
ClientDevicesAuthService.CLIENT_DEVICES_AUTH_SERVICE_NAME, 30, State.RUNNING); | ||
kernel.launch(); | ||
mainRunning.run(); | ||
} | ||
|
||
private void authzClientDeviceAction(AuthorizationRequest request, Boolean authorized) throws Exception { | ||
assertThat(api.authorizeClientDeviceAction(request), is(authorized)); | ||
} | ||
|
||
@AfterEach | ||
void afterEach() { | ||
LogConfig.getRootLogConfig().reset(); | ||
kernel.shutdown(); | ||
} | ||
|
||
private static Stream<Arguments> authzRequests () { | ||
return Stream.of( | ||
// GIVEN_permissiveGroupPolicyWithThingNameVariable_WHEN_ClientAuthorizesWithThingNameValidResource_THEN_ClientAuthorized | ||
Arguments.of("myThing", "mqtt:connect", "mqtt:myThing:foo", true), | ||
Arguments.of("myThing", "mqtt:publish", "mqtt:topic:myThing", true), | ||
// GIVEN_permissiveGroupPolicyWithThingNameVariable_WHEN_ClientAuthorizesWithThingNameInvalidResource_THEN_ClientNotAuthorized | ||
Arguments.of("myThing", "mqtt:connect", "mqtt:MyCoolThing:foo", false), | ||
Arguments.of("myThing", "mqtt:publish", "mqtt:topic:SomeThing", false), | ||
// GIVEN_permissiveGroupPolicyWithThingNameVariable_WHEN_ClientAuthorizesWithThingNameResourceInvalidAction_THEN_ClientNotAuthorized | ||
Arguments.of("myThing", "mqtt:connect", "mqtt:topic:myThing", false), | ||
Arguments.of("myThing", "mqtt:publish", "mqtt:myThing:foo", false), | ||
// GIVEN_permissiveGroupPolicyWithThingNameVariable_WHEN_ClientAuthorizesWithInvalidThingNameResource_THEN_ClientNotAuthorized | ||
Arguments.of("SomeThing", "mqtt:connect", "mqtt:myThing:foo", false), | ||
Arguments.of("SomeThing", "mqtt:publish", "mqtt:topic:myThing", false) | ||
); | ||
} | ||
|
||
@ParameterizedTest | ||
@MethodSource("authzRequests") | ||
void GIVEN_permissiveGroupPolicyWithThingNameVariable_WHEN_ClientAuthorizesWithThingName_THEN_ClientAuthorized( | ||
String thingName, String operation, String resource, Boolean result) throws Exception { | ||
String deviceToken = getClientDeviceSessionAuthToken(thingName, clientPem); | ||
|
||
AuthorizationRequest request = AuthorizationRequest.builder().sessionId(deviceToken) | ||
.operation(operation).resource(resource).build(); | ||
|
||
authzClientDeviceAction(request, result); | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
src/integrationtests/resources/com/aws/greengrass/integrationtests/deviceauth/config.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
--- | ||
services: | ||
aws.greengrass.Nucleus: | ||
configuration: | ||
runWithDefault: | ||
posixUser: nobody | ||
windowsUser: integ-tester | ||
logging: | ||
level: "DEBUG" | ||
aws.greengrass.clientdevices.Auth: | ||
configuration: | ||
deviceGroups: | ||
formatVersion: "2021-03-05" | ||
definitions: | ||
myThing: | ||
selectionRule: "thingName:myThing" | ||
policyName: "thingAccessPolicy" | ||
policies: | ||
thingAccessPolicy: | ||
policyStatement1: | ||
statementDescription: "mqtt connect" | ||
effect: ALLOW | ||
operations: | ||
- "mqtt:connect" | ||
resources: | ||
- "mqtt:${iot:Connection.Thing.ThingName}:foo" | ||
policyStatement2: | ||
statementDescription: "mqtt publish" | ||
operations: | ||
- "mqtt:publish" | ||
resources: | ||
- "mqtt:topic:${iot:Connection.Thing.ThingName}" | ||
main: | ||
dependencies: | ||
- BrokerWithGetClientDeviceAuthTokenPermission | ||
- BrokerWithAuthorizeClientDeviceActionPermission | ||
BrokerWithGetClientDeviceAuthTokenPermission: | ||
dependencies: | ||
- aws.greengrass.clientdevices.Auth | ||
configuration: | ||
accessControl: | ||
aws.greengrass.clientdevices.Auth: | ||
GetClientDeviceAuthTokenPolicy: | ||
policyDescription: access to certificate updates | ||
operations: | ||
- 'aws.greengrass#GetClientDeviceAuthToken' | ||
resources: | ||
- '*' | ||
BrokerWithAuthorizeClientDeviceActionPermission: | ||
dependencies: | ||
- aws.greengrass.clientdevices.Auth | ||
configuration: | ||
accessControl: | ||
aws.greengrass.clientdevices.Auth: | ||
BrokerWithAuthorizeClientDeviceActionPermission: | ||
policyDescription: access to certificate updates | ||
operations: | ||
- 'aws.greengrass#AuthorizeClientDeviceAction' | ||
resources: | ||
- '*' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.