Skip to content

Commit

Permalink
feat: support mqtt wildcard resource matching
Browse files Browse the repository at this point in the history
  • Loading branch information
jcosentino11 committed Sep 11, 2024
1 parent 1d2958e commit 6b156de
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ public static Stream<Arguments> authzRequests() {
.resource("mqtt:topic:myThing/world")
.expectedResult(false)
.build(),
// mqtt wildcards eval not supported
// mqtt wildcards eval not supported by default
AuthZRequest.builder()
.thingName("myThing")
.operation("mqtt:subscribe")
Expand All @@ -289,6 +289,27 @@ public static Stream<Arguments> authzRequests() {
.build()
)),

Arguments.of("mqtt-wildcards-in-resource.yaml", Arrays.asList(
AuthZRequest.builder()
.thingName("myThing")
.operation("mqtt:publish")
.resource("mqtt:topic:*/myThing/*")
.expectedResult(true)
.build(),
AuthZRequest.builder()
.thingName("myThing")
.operation("mqtt:publish")
.resource("mqtt:topic:hello/myThing/world")
.expectedResult(true)
.build(),
AuthZRequest.builder()
.thingName("myThing")
.operation("mqtt:subscribe")
.resource("mqtt:topic:myThing/test/test/test/test")
.expectedResult(true)
.build()
)),

Arguments.of("variables-and-wildcards.yaml", Arrays.asList(
AuthZRequest.builder()
.thingName("myThing")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
services:
aws.greengrass.Nucleus:
configuration:
runWithDefault:
posixUser: nobody
windowsUser: integ-tester
logging:
level: "DEBUG"
aws.greengrass.clientdevices.Auth:
configuration:
enableMqttWildcardEvaluation: true
deviceGroups:
formatVersion: "2021-03-05"
definitions:
myThing:
selectionRule: "thingName: myThing"
policyName: "thingAccessPolicy"
policies:
thingAccessPolicy:
publish:
statementDescription: "mqtt publish"
operations:
- "mqtt:publish"
resources:
- "mqtt:topic:*/myThing/*"
subscribe:
statementDescription: "mqtt subscribe"
operations:
- "mqtt:subscribe"
resources:
- "mqtt:topic:${iot:Connection.Thing.ThingName}/+/test/#"
main:
dependencies:
- aws.greengrass.clientdevices.Auth
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ private void subscribeToConfigChanges() {
private void onConfigurationChanged() {
try {
cdaConfiguration = CDAConfiguration.from(cdaConfiguration, getConfig());
// TODO decouple
context.get(PermissionEvaluationUtils.class).setCdaConfiguration(cdaConfiguration);
} catch (URISyntaxException e) {
serviceErrored(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package com.aws.greengrass.clientdevices.auth;

import com.aws.greengrass.authorization.WildcardTrie;
import com.aws.greengrass.clientdevices.auth.configuration.CDAConfiguration;
import com.aws.greengrass.clientdevices.auth.configuration.GroupManager;
import com.aws.greengrass.clientdevices.auth.configuration.Permission;
import com.aws.greengrass.clientdevices.auth.exception.PolicyException;
Expand All @@ -14,6 +15,7 @@
import com.aws.greengrass.logging.impl.LogManager;
import com.aws.greengrass.util.Utils;
import lombok.Builder;
import lombok.Setter;
import lombok.Value;
import org.apache.commons.lang3.StringUtils;

Expand All @@ -36,6 +38,8 @@ public final class PermissionEvaluationUtils {
"Resource is malformed, must be of the form: "
+ "([a-zA-Z]+):([a-zA-Z]+):" + RESOURCE_NAME_PATTERN.pattern();
private final GroupManager groupManager;
@Setter
private volatile CDAConfiguration cdaConfiguration;

/**
* Constructor for PermissionEvaluationUtils.
Expand Down Expand Up @@ -134,9 +138,27 @@ private boolean compareResource(Resource requestResource, String policyResource)
return true;
}

WildcardTrie wildcardTrie = new WildcardTrie();
wildcardTrie.add(policyResource);
return wildcardTrie.matchesStandard(requestResource.getResourceStr());
if (matchMqttWildcards()) {
String name = extractResourceName(policyResource);
WildcardTrie trie = new WildcardTrie();
trie.add(name);
return trie.matchesMQTT(requestResource.getResourceName());
} else {
WildcardTrie trie = new WildcardTrie();
trie.add(policyResource);
return trie.matchesStandard(requestResource.getResourceStr());
}
}

private String extractResourceName(String resource) {
// resource is considered valid at this point, so don't need to duplicate validation from parseResource
String typeAndName = resource.substring(resource.indexOf(':') + 1);
return typeAndName.substring(typeAndName.indexOf(':') + 1);
}

private boolean matchMqttWildcards() {
CDAConfiguration config = cdaConfiguration;
return config != null && config.isEnableMqttWildcardEvaluation();
}

private Operation parseOperation(String operationStr) throws PolicyException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import com.aws.greengrass.clientdevices.auth.configuration.events.MetricsConfigurationChanged;
import com.aws.greengrass.clientdevices.auth.configuration.events.SecurityConfigurationChanged;
import com.aws.greengrass.config.Topics;
import com.aws.greengrass.util.Coerce;
import lombok.Builder;
import lombok.Getter;

import java.net.URISyntaxException;
Expand Down Expand Up @@ -48,23 +50,19 @@
* |
* </p>
*/
@Builder

Check notice

Code scanning / CodeQL

Use of default toString() Note

Default toString(): MetricsConfiguration inherits toString() from Object, and so is not suitable for printing.
Default toString(): SecurityConfiguration inherits toString() from Object, and so is not suitable for printing.
Default toString(): CAConfiguration inherits toString() from Object, and so is not suitable for printing.
Default toString(): RuntimeConfiguration inherits toString() from Object, and so is not suitable for printing.
Default toString(): DomainEvents inherits toString() from Object, and so is not suitable for printing.
public final class CDAConfiguration {

public static final String ENABLE_MQTT_WILDCARD_EVALUATION = "enableMqttWildcardEvaluation";

private final DomainEvents domainEvents;
private final RuntimeConfiguration runtime;
private final SecurityConfiguration security;
@Getter
private final CAConfiguration certificateAuthorityConfiguration;
private final SecurityConfiguration security;
private final MetricsConfiguration metricsConfiguration;
private final DomainEvents domainEvents;

private CDAConfiguration(DomainEvents domainEvents, RuntimeConfiguration runtime, CAConfiguration ca,
SecurityConfiguration security, MetricsConfiguration metricsConfiguration) {
this.domainEvents = domainEvents;
this.runtime = runtime;
this.security = security;
this.certificateAuthorityConfiguration = ca;
this.metricsConfiguration = metricsConfiguration;
}
@Getter
private final boolean enableMqttWildcardEvaluation;

/**
* Creates the CDA (Client Device Auth) Service configuration. And allows it to be available in the context with the
Expand All @@ -80,9 +78,15 @@ public static CDAConfiguration from(CDAConfiguration existingConfig, Topics topi

DomainEvents domainEvents = topics.getContext().get(DomainEvents.class);

CDAConfiguration newConfig = new CDAConfiguration(domainEvents, RuntimeConfiguration.from(runtimeTopics),
CAConfiguration.from(serviceConfiguration), SecurityConfiguration.from(serviceConfiguration),
MetricsConfiguration.from(serviceConfiguration));
CDAConfiguration newConfig = CDAConfiguration.builder()
.domainEvents(domainEvents)
.runtime(RuntimeConfiguration.from(runtimeTopics))
.certificateAuthorityConfiguration(CAConfiguration.from(serviceConfiguration))
.security(SecurityConfiguration.from(serviceConfiguration))
.metricsConfiguration(MetricsConfiguration.from(serviceConfiguration))
.enableMqttWildcardEvaluation(
Coerce.toBoolean(serviceConfiguration.find(ENABLE_MQTT_WILDCARD_EVALUATION)))
.build();

newConfig.triggerChanges(newConfig, existingConfig);

Expand Down

0 comments on commit 6b156de

Please sign in to comment.