Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add policy variable support #415

Merged
merged 27 commits into from
Feb 1, 2024
Merged

feat: add policy variable support #415

merged 27 commits into from
Feb 1, 2024

Conversation

sameerzuberi
Copy link
Member

Issue #, if available:

Description of changes:

  • Adding policy variable support for thing names.
  • Updating PermissionEvaluationUtils to use non static methods for future wildcard support

Why is this change necessary:

How was this change tested:
Unit and integration tests

Any additional information or context required to review the change:

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@sameerzuberi sameerzuberi force-pushed the policy-variable-support branch from 5a4cbbb to 1193d04 Compare January 5, 2024 20:22
@sameerzuberi sameerzuberi force-pushed the policy-variable-support branch from 0470133 to c44eef6 Compare January 5, 2024 20:29
return PermissionEvaluationUtils.isAuthorized(request.getOperation(), request.getResource(),
groupManager.getApplicablePolicyPermissions(session));
return permissionEvaluationUtils.isAuthorized(request.getOperation(), request.getResource(),
permissionEvaluationUtils.transformGroupPermissionsWithVariableValue(session,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while we're at it, we can clean up by making groupManager a dependency of PermissionEvaluationUtils, then we can do something like

permissionEvaluationUtils.isAuthorized(request, session)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets also take performance into account, e.g. when evaluating w/ same permissions we shouldn't do variable substitution again

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets also take performance into account, e.g. when evaluating w/ same permissions we shouldn't do variable substitution again

Thinking through this more, variables are substituted based on current session, so a caching solution for this would be a bit more involved, assuming it would even be useful, so lets table for now

@@ -60,7 +66,8 @@ public static boolean isAuthorized(String operation, String resource,
return false;
}

for (Map.Entry<String, Set<Permission>> entry : groupToPermissionsMap.entrySet()) {
for (Map.Entry<String, Set<Permission>> entry :
groupToPermissionsMap.entrySet()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: do we need this formatting change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

return PermissionEvaluationUtils.isAuthorized(request.getOperation(), request.getResource(),
groupManager.getApplicablePolicyPermissions(session));
return permissionEvaluationUtils.isAuthorized(request.getOperation(), request.getResource(),
permissionEvaluationUtils.transformGroupPermissionsWithVariableValue(session,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets also take performance into account, e.g. when evaluating w/ same permissions we shouldn't do variable substitution again

@sameerzuberi sameerzuberi force-pushed the policy-variable-support branch from 027a98b to 87e85b3 Compare January 9, 2024 00:59
@sameerzuberi sameerzuberi force-pushed the policy-variable-support branch from a68376e to 7698ed9 Compare January 9, 2024 01:04
@sameerzuberi sameerzuberi force-pushed the policy-variable-support branch from 3609884 to 6f755b8 Compare January 9, 2024 01:54
Copy link

github-actions bot commented Jan 9, 2024

Code Coverage Report

File Coverage
All files 73%
com.aws.greengrass.clientdevices.auth.PermissionEvaluationUtils 77%
com.aws.greengrass.clientdevices.auth.CertificateManager 88%
com.aws.greengrass.clientdevices.auth.ClientDevicesAuthService 79%
com.aws.greengrass.clientdevices.auth.DeviceAuthClient 73%
com.aws.greengrass.clientdevices.auth.certificate.ClientCertificateGenerator 95%
com.aws.greengrass.clientdevices.auth.certificate.CertificateHelper 74%
com.aws.greengrass.clientdevices.auth.certificate.CertificateStore 72%
com.aws.greengrass.clientdevices.auth.certificate.CertificateExpiryMonitor 77%
com.aws.greengrass.clientdevices.auth.certificate.ServerCertificateGenerator 93%
com.aws.greengrass.clientdevices.auth.certificate.CertificateGenerator 70%
com.aws.greengrass.clientdevices.auth.certificate.CertificateExpiryMonitor$CertRotationDecider 90%
com.aws.greengrass.clientdevices.auth.iot.usecases.VerifyIotCertificate 94%
com.aws.greengrass.clientdevices.auth.iot.usecases.VerifyThingAttachedToCertificate 89%
com.aws.greengrass.clientdevices.auth.iot.usecases.CreateIoTThingSession 67%
com.aws.greengrass.clientdevices.auth.iot.usecases.VerifyCertificateValidityPeriod 88%
com.aws.greengrass.clientdevices.auth.certificate.infra.BackgroundCertificateRefresh 82%
com.aws.greengrass.clientdevices.auth.iot.infra.ThingRegistry 92%
com.aws.greengrass.clientdevices.auth.certificate.usecases.ConfigureManagedCertificateAuthority 85%
com.aws.greengrass.clientdevices.auth.certificate.usecases.ConfigureCustomCertificateAuthority 83%
com.aws.greengrass.clientdevices.auth.certificate.usecases.RegisterCertificateAuthorityUseCase 65%
com.aws.greengrass.clientdevices.auth.configuration.MetricsConfiguration 83%
com.aws.greengrass.clientdevices.auth.configuration.GroupManager 89%
com.aws.greengrass.clientdevices.auth.configuration.CAConfiguration 96%
com.aws.greengrass.clientdevices.auth.configuration.RuntimeConfiguration 83%
com.aws.greengrass.clientdevices.auth.configuration.SecurityConfiguration 80%
com.aws.greengrass.clientdevices.auth.configuration.GroupDefinition 75%
com.aws.greengrass.clientdevices.auth.configuration.ExpressionVisitor 84%
com.aws.greengrass.clientdevices.auth.configuration.GroupConfiguration 92%
com.aws.greengrass.clientdevices.auth.api.ClientDevicesAuthServiceApi 85%
com.aws.greengrass.clientdevices.auth.api.GetCertificateRequestWithGenerator 75%
com.aws.greengrass.clientdevices.auth.api.UseCases 71%
com.aws.greengrass.clientdevices.auth.session.attribute.WildcardSuffixAttribute 96%
com.aws.greengrass.clientdevices.auth.iot.IotAuthClient$Default 56%
com.aws.greengrass.clientdevices.auth.iot.Thing 85%
com.aws.greengrass.clientdevices.auth.iot.Certificate 74%
com.aws.greengrass.clientdevices.auth.iot.GreengrassV2DataClientFactory 18%
com.aws.greengrass.clientdevices.auth.iot.CertificateRegistry 95%
com.aws.greengrass.clientdevices.auth.infra.NetworkStateProvider$Default 83%
com.aws.greengrass.ipc.IPCUtils 83%
com.aws.greengrass.ipc.VerifyClientDeviceIdentityOperationHandler 60%
com.aws.greengrass.ipc.GetClientDeviceAuthTokenOperationHandler 83%
com.aws.greengrass.ipc.AuthorizeClientDeviceActionOperationHandler 79%
com.aws.greengrass.ipc.SubscribeToCertificateUpdatesOperationHandler 81%
com.aws.greengrass.clientdevices.auth.session.SessionConfig 92%
com.aws.greengrass.clientdevices.auth.session.SessionManager 88%
com.aws.greengrass.clientdevices.auth.certificate.handlers.CAConfigurationChangedHandler 93%
com.aws.greengrass.clientdevices.auth.certificate.handlers.CertificateRotationHandler 96%
com.aws.greengrass.clientdevices.auth.metrics.handlers.SessionCreationEventHandler 88%
com.aws.greengrass.clientdevices.auth.metrics.handlers.MetricsConfigurationChangedHandler 70%
com.aws.greengrass.clientdevices.auth.metrics.handlers.AuthorizeClientDeviceActionsMetricHandler 88%
com.aws.greengrass.clientdevices.auth.metrics.handlers.VerifyClientDeviceIdentityEventHandler 88%
com.aws.greengrass.clientdevices.auth.metrics.handlers.CertificateSubscriptionEventHandler 83%
com.aws.greengrass.clientdevices.auth.util.ResizableLinkedBlockingQueue 90%
com.aws.greengrass.clientdevices.auth.util.ParseIPAddress 90%
com.aws.greengrass.clientdevices.auth.connectivity.CISShadowMonitor$SucceedOnceOperation 88%
com.aws.greengrass.clientdevices.auth.connectivity.CISShadowMonitor 79%
com.aws.greengrass.clientdevices.auth.connectivity.CISShadowMonitor$ProcessCISShadowTask 76%
com.aws.greengrass.clientdevices.auth.connectivity.CISShadowMonitor$CISShadowTaskQueue 74%
com.aws.greengrass.clientdevices.auth.connectivity.HostAddress 67%
com.aws.greengrass.clientdevices.auth.connectivity.CISShadowMonitor$CISShadowTaskExecutor 95%
com.aws.greengrass.clientdevices.auth.configuration.parser.TokenMgrError 22%
com.aws.greengrass.clientdevices.auth.configuration.parser.RuleExpressionTokenManager 61%
com.aws.greengrass.clientdevices.auth.configuration.parser.ASTStart 33%
com.aws.greengrass.clientdevices.auth.configuration.parser.ASTAnd 67%
com.aws.greengrass.clientdevices.auth.configuration.parser.Token 58%
com.aws.greengrass.clientdevices.auth.configuration.parser.RuleExpressionDefaultVisitor 0%
com.aws.greengrass.clientdevices.auth.configuration.parser.ASTOr 67%
com.aws.greengrass.clientdevices.auth.configuration.parser.SimpleCharStream 28%
com.aws.greengrass.clientdevices.auth.configuration.parser.RuleExpressionTreeConstants 0%
com.aws.greengrass.clientdevices.auth.configuration.parser.JJTRuleExpressionState 67%
com.aws.greengrass.clientdevices.auth.configuration.parser.ASTThing 67%
com.aws.greengrass.clientdevices.auth.configuration.parser.RuleExpression 63%
com.aws.greengrass.clientdevices.auth.configuration.parser.SimpleNode 27%

Minimum allowed coverage is 50%

Generated by 🐒 cobertura-action against cdfc054

}
} else {
logger.atWarn().kv("policyVariable", policyVariable)
.log("Policy variable detected but could not be parsed");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

policy variable unsupported, and can also guide user w/ next steps (e.g. fix config)

String attributeName = vars[2];

// this supports the ThingName attribute only
if (THING_NAME_VARIABLE.equals(policyVariable)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we'd want to be case insensitive, would need to configure the matcher too

Coerce.toString(session.getSessionAttribute(attributeNamespace, attributeName));

if (policyVariableValue == null) {
logger.atWarn().kv("attributeName", attributeName)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we'd want to error here rather than log, it's a failure mode--not doing substitution would mean the policy is acting differently than intended

@sameerzuberi sameerzuberi force-pushed the policy-variable-support branch from 5665f8b to b7bb2cb Compare January 9, 2024 18:26
@sameerzuberi sameerzuberi force-pushed the policy-variable-support branch from 0ea3f4c to 6135f3e Compare January 16, 2024 18:17
@sameerzuberi sameerzuberi force-pushed the policy-variable-support branch from 0a76038 to f62ea96 Compare January 16, 2024 18:25
@robcmann robcmann marked this pull request as draft January 26, 2024 21:33
private static final Map<String, Pair<String,String>> policyVariableResolver =
new HashMap<String, Pair<String,String>>() {
{
put("iot:Connection.Thing.ThingName", new Pair<>("Thing", "ThingName"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets make the attr namespace and properties constants


private static final Map<String, Pair<String,String>> policyVariableResolver =
new HashMap<String, Pair<String,String>>() {
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: avoid {{}} initializers

}
} else {
logger.atWarn().kv("policyVariable", policyVariable)
.log("Policy variable unsupported. Only thing name variables are supported, please fix the "
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only thing name variables are supported

let's remove, otherwise we'll have to update this every time we add new ones

@jcosentino11
Copy link
Member

jcosentino11 commented Jan 26, 2024

Putting here for later reference:

With regex on the policy eval path, throughput is ~200k ops/s (for variable substitution test case). Before we were hitting 400k.

We talked about identifying variables during config time, which should get those perf numbers back up

@@ -17,4 +20,10 @@ public class Permission {
@NonNull String operation;

@NonNull String resource;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: i think renaming to resourceFormat or similar would make it clearer that this shouldn't be used directly anymore

@@ -17,4 +20,10 @@ public class Permission {
@NonNull String operation;

@NonNull String resource;

List<String> policyVariables;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets name this resourcePolicyVariables, since these don't apply to operation or principal

* @param session current device session
* @return updated resource
*/
public static String resolvePolicyVariables(List<String> policyVariables, String resource, Session session) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: in follow up we'll also need to add more validations to the policy (e.g. bad policy variable name, etc), after #418 is merged too

private static final String THING_NAMESPACE = "Thing";
private static final String THING_NAME_ATTRIBUTE = "ThingName";

static {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Utils.immutableMap

import java.util.Map;

public final class PolicyVariableResolver {
private static final Map<String, Pair<String,String>> policyVariableMap;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could use a more descriptive name

String policyVariableValue = Coerce.toString(session.getSessionAttribute(attributeNamespace,
attributeName));
if (policyVariableValue == null) {
throw new IllegalArgumentException("No attribute found for current session");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets avoid unchecked exceptions, may need to make your own if there's not a suitable one

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

include attribute name/space in message as well

if (policyVariableValue == null) {
throw new IllegalArgumentException("No attribute found for current session");
} else {
substitutedResource = StringUtils.replace(substitutedResource, policyVariable, policyVariableValue);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably worth a quick comment about why this replace impl

@@ -198,19 +198,19 @@ void GIVEN_valid_group_configuration_WHEN_start_service_THEN_instantiated_config

Permission[] tempSensorPermissions =
{Permission.builder().principal("myTemperatureSensors").operation("mqtt" + ":connect")
.resource("mqtt:clientId:foo").build(),
.resource("mqtt:clientId:foo").policyVariables(Collections.emptyList()).build(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would also be ok to default policyVariables to empty list within Permission itself

@robcmann robcmann marked this pull request as ready for review January 31, 2024 22:44

package com.aws.greengrass.clientdevices.auth.exception;

public class AttributeProviderException extends Exception {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: how about PolicyException?

// TODO: Return other possible DeviceAttributes
return null;
// TODO: Support other DeviceAttributes
return new WildcardSuffixAttribute(thingName);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from convo: if we want to still have attributeName passed in we'll need to use it, otherwise would need to be reverted back to just getDeviceAttributes() that returns a map

jcosentino11
jcosentino11 previously approved these changes Feb 1, 2024
@robcmann robcmann requested a review from MikeDombo February 1, 2024 19:24
@robcmann robcmann merged commit 1a7a07a into main Feb 1, 2024
5 checks passed
@robcmann robcmann deleted the policy-variable-support branch February 1, 2024 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants