From b5970657a77004c07d67657606f6462d7584c448 Mon Sep 17 00:00:00 2001 From: Marco Schwab <92920200+mschwab12@users.noreply.github.com> Date: Mon, 27 Nov 2023 23:41:27 +0100 Subject: [PATCH] feat: thingName wildcard prefix in selectionRule (#412) --- .../configuration/parser/RuleExpression.jj | 2 +- .../parser/RuleExpressionTokenManager.java | 74 ++++++++++--------- .../attribute/WildcardSuffixAttribute.java | 6 +- src/main/javacc/RuleExpression.jjt | 2 +- .../configuration/GroupDefinitionTest.java | 22 +++++- .../parser/BasicRuleExpressionTest.java | 25 ++++++- .../parser/RuleExpressionEvaluationTest.java | 50 ++++++++++++- 7 files changed, 141 insertions(+), 40 deletions(-) diff --git a/src/main/java/com/aws/greengrass/clientdevices/auth/configuration/parser/RuleExpression.jj b/src/main/java/com/aws/greengrass/clientdevices/auth/configuration/parser/RuleExpression.jj index c5d52eefc..5059075fc 100644 --- a/src/main/java/com/aws/greengrass/clientdevices/auth/configuration/parser/RuleExpression.jj +++ b/src/main/java/com/aws/greengrass/clientdevices/auth/configuration/parser/RuleExpression.jj @@ -38,7 +38,7 @@ TOKEN: { < OR: "OR" > | < AND: "AND" > -| < THINGNAME: ( | "-" | "_" | "\\:")+("*")? | "*" > // Only allow escaped colons +| < THINGNAME: ("*")?( | "-" | "_" | "\\:")+("*")? | "*" > // Only allow escaped colons | < ALPHANUMERIC: [ "a"-"z" ] | [ "A"-"Z" ] | [ "0"-"9" ] > } diff --git a/src/main/java/com/aws/greengrass/clientdevices/auth/configuration/parser/RuleExpressionTokenManager.java b/src/main/java/com/aws/greengrass/clientdevices/auth/configuration/parser/RuleExpressionTokenManager.java index 7cedbecb4..5c07241a6 100644 --- a/src/main/java/com/aws/greengrass/clientdevices/auth/configuration/parser/RuleExpressionTokenManager.java +++ b/src/main/java/com/aws/greengrass/clientdevices/auth/configuration/parser/RuleExpressionTokenManager.java @@ -21,7 +21,7 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0){ if ((active0 & 0x130L) != 0L) { jjmatchedKind = 6; - return 5; + return 6; } return -1; case 1: @@ -29,27 +29,27 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0){ { jjmatchedKind = 6; jjmatchedPos = 1; - return 5; + return 6; } if ((active0 & 0x10L) != 0L) - return 5; + return 6; return -1; case 2: if ((active0 & 0x100L) != 0L) { jjmatchedKind = 6; jjmatchedPos = 2; - return 5; + return 6; } if ((active0 & 0x20L) != 0L) - return 5; + return 6; return -1; case 3: if ((active0 & 0x100L) != 0L) { jjmatchedKind = 6; jjmatchedPos = 3; - return 5; + return 6; } return -1; case 4: @@ -57,7 +57,7 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0){ { jjmatchedKind = 6; jjmatchedPos = 4; - return 5; + return 6; } return -1; case 5: @@ -65,7 +65,7 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0){ { jjmatchedKind = 6; jjmatchedPos = 5; - return 5; + return 6; } return -1; case 6: @@ -73,7 +73,7 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0){ { jjmatchedKind = 6; jjmatchedPos = 6; - return 5; + return 6; } return -1; case 7: @@ -81,7 +81,7 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0){ { jjmatchedKind = 6; jjmatchedPos = 7; - return 5; + return 6; } return -1; case 8: @@ -89,7 +89,7 @@ private final int jjStopStringLiteralDfa_0(int pos, long active0){ { jjmatchedKind = 6; jjmatchedPos = 8; - return 5; + return 6; } return -1; default : @@ -130,7 +130,7 @@ private int jjMoveStringLiteralDfa1_0(long active0){ return jjMoveStringLiteralDfa2_0(active0, 0x20L); case 82: if ((active0 & 0x10L) != 0L) - return jjStartNfaWithStates_0(1, 4, 5); + return jjStartNfaWithStates_0(1, 4, 6); break; case 104: return jjMoveStringLiteralDfa2_0(active0, 0x100L); @@ -151,7 +151,7 @@ private int jjMoveStringLiteralDfa2_0(long old0, long active0){ { case 68: if ((active0 & 0x20L) != 0L) - return jjStartNfaWithStates_0(2, 5, 5); + return jjStartNfaWithStates_0(2, 5, 6); break; case 105: return jjMoveStringLiteralDfa3_0(active0, 0x100L); @@ -292,7 +292,7 @@ private int jjStartNfaWithStates_0(int pos, int kind, int state) private int jjMoveNfa_0(int startState, int curPos) { int startsAt = 0; - jjnewStateCnt = 5; + jjnewStateCnt = 6; int i = 1; jjstateSet[0] = startState; int kind = 0x7fffffff; @@ -307,7 +307,7 @@ private int jjMoveNfa_0(int startState, int curPos) { switch(jjstateSet[--i]) { - case 4: + case 6: if ((0x3ff200000000000L & l) != 0L) { if (kind > 6) @@ -319,13 +319,8 @@ else if (curChar == 42) if (kind > 6) kind = 6; } - if ((0x3ff000000000000L & l) != 0L) - { - if (kind > 7) - kind = 7; - } break; - case 5: + case 4: if ((0x3ff200000000000L & l) != 0L) { if (kind > 6) @@ -336,6 +331,12 @@ else if (curChar == 42) { if (kind > 6) kind = 6; + { jjCheckNAddTwoStates(0, 2); } + } + if ((0x3ff000000000000L & l) != 0L) + { + if (kind > 7) + kind = 7; } break; case 0: @@ -352,9 +353,16 @@ else if (curChar == 42) { jjCheckNAddStates(0, 2); } break; case 3: - if (curChar == 42) + if (curChar == 42 && kind > 6) kind = 6; break; + case 5: + if (curChar != 42) + break; + if (kind > 6) + kind = 6; + { jjCheckNAddTwoStates(0, 2); } + break; default : break; } } while(i != startsAt); @@ -366,7 +374,7 @@ else if (curChar < 128) { switch(jjstateSet[--i]) { - case 4: + case 6: if ((0x7fffffe87fffffeL & l) != 0L) { if (kind > 6) @@ -375,13 +383,8 @@ else if (curChar < 128) } else if (curChar == 92) jjstateSet[jjnewStateCnt++] = 1; - if ((0x7fffffe07fffffeL & l) != 0L) - { - if (kind > 7) - kind = 7; - } break; - case 5: + case 4: if ((0x7fffffe87fffffeL & l) != 0L) { if (kind > 6) @@ -390,6 +393,11 @@ else if (curChar == 92) } else if (curChar == 92) jjstateSet[jjnewStateCnt++] = 1; + if ((0x7fffffe07fffffeL & l) != 0L) + { + if (kind > 7) + kind = 7; + } break; case 0: if ((0x7fffffe87fffffeL & l) == 0L) @@ -425,7 +433,7 @@ else if (curChar == 92) kind = 0x7fffffff; } ++curPos; - if ((i = jjnewStateCnt) == (startsAt = 5 - (jjnewStateCnt = startsAt))) + if ((i = jjnewStateCnt) == (startsAt = 6 - (jjnewStateCnt = startsAt))) return curPos; try { curChar = input_stream.readChar(); } catch(java.io.IOException e) { return curPos; } @@ -621,7 +629,7 @@ private void ReInitRounds() { int i; jjround = 0x80000001; - for (i = 5; i-- > 0;) + for (i = 6; i-- > 0;) jjrounds[i] = 0x80000000; } @@ -666,8 +674,8 @@ public void SwitchTo(int lexState) }; protected SimpleCharStream input_stream; - private final int[] jjrounds = new int[5]; - private final int[] jjstateSet = new int[2 * 5]; + private final int[] jjrounds = new int[6]; + private final int[] jjstateSet = new int[2 * 6]; private final StringBuilder jjimage = new StringBuilder(); private StringBuilder image = jjimage; private int jjimageLen; diff --git a/src/main/java/com/aws/greengrass/clientdevices/auth/session/attribute/WildcardSuffixAttribute.java b/src/main/java/com/aws/greengrass/clientdevices/auth/session/attribute/WildcardSuffixAttribute.java index 2ce3e2499..01cf880f1 100644 --- a/src/main/java/com/aws/greengrass/clientdevices/auth/session/attribute/WildcardSuffixAttribute.java +++ b/src/main/java/com/aws/greengrass/clientdevices/auth/session/attribute/WildcardSuffixAttribute.java @@ -16,7 +16,11 @@ public WildcardSuffixAttribute(String attributeValue) { @Override public boolean matches(@NonNull String expr) { - if (expr.endsWith("*")) { + if (expr.length() > 1 && expr.startsWith("*") && expr.endsWith("*")) { + return value.contains(expr.substring(1, expr.length() - 1)); + } else if (expr.startsWith("*")) { + return value.endsWith(expr.substring(1)); + } else if (expr.endsWith("*")) { return value.startsWith(expr.substring(0, expr.length() - 1)); } else { return value.equals(expr); diff --git a/src/main/javacc/RuleExpression.jjt b/src/main/javacc/RuleExpression.jjt index 5f8437473..d0f197b1c 100644 --- a/src/main/javacc/RuleExpression.jjt +++ b/src/main/javacc/RuleExpression.jjt @@ -34,7 +34,7 @@ TOKEN: { < OR: "OR" > | < AND: "AND" > -| < THINGNAME: ( | "-" | "_" | "\\:")+("*")? | "*" > // Only allow escaped colons +| < THINGNAME: ("*")?( | "-" | "_" | "\\:")+("*")? | "*" > // Only allow escaped colons | < ALPHANUMERIC: [ "a"-"z" ] | [ "A"-"Z" ] | [ "0"-"9" ] > } diff --git a/src/test/java/com/aws/greengrass/clientdevices/auth/configuration/GroupDefinitionTest.java b/src/test/java/com/aws/greengrass/clientdevices/auth/configuration/GroupDefinitionTest.java index fca2e23d6..82ab28bec 100644 --- a/src/test/java/com/aws/greengrass/clientdevices/auth/configuration/GroupDefinitionTest.java +++ b/src/test/java/com/aws/greengrass/clientdevices/auth/configuration/GroupDefinitionTest.java @@ -33,7 +33,7 @@ void GIVEN_groupDefinitionAndMatchingSession_WHEN_containsSession_THEN_returnsTr } @Test - void GIVEN_groupDefinitionWithWildcardAndMatchingSession_WHEN_containsSession_THEN_returnsTrue() + void GIVEN_groupDefinitionWithTrailingWildcardAndMatchingSession_WHEN_containsSession_THEN_returnsTrue() throws ParseException { GroupDefinition groupDefinition = new GroupDefinition("thingName: thing*", "Policy1"); Session session = Mockito.mock(Session.class); @@ -42,6 +42,26 @@ void GIVEN_groupDefinitionWithWildcardAndMatchingSession_WHEN_containsSession_TH assertThat(groupDefinition.containsClientDevice(session), is(true)); } + @Test + void GIVEN_groupDefinitionWithLeadingWildcardAndMatchingSession_WHEN_containsSession_THEN_returnsTrue() + throws ParseException { + GroupDefinition groupDefinition = new GroupDefinition("thingName: *thing", "Policy1"); + Session session = Mockito.mock(Session.class); + DeviceAttribute attribute = new WildcardSuffixAttribute("A-thing"); + Mockito.when(session.getSessionAttribute(any(), any())).thenReturn(attribute); + assertThat(groupDefinition.containsClientDevice(session), is(true)); + } + + @Test + void GIVEN_groupDefinitionWithLeadingAndTrailingWildcardAndMatchingSession_WHEN_containsSession_THEN_returnsTrue() + throws ParseException { + GroupDefinition groupDefinition = new GroupDefinition("thingName: *thing*", "Policy1"); + Session session = Mockito.mock(Session.class); + DeviceAttribute attribute = new WildcardSuffixAttribute("A-thing-B"); + Mockito.when(session.getSessionAttribute(any(), any())).thenReturn(attribute); + assertThat(groupDefinition.containsClientDevice(session), is(true)); + } + @Test void GIVEN_groupDefinitionAndNonMatchingSession_WHEN_containsSession_THEN_returnsFalse() throws ParseException { GroupDefinition groupDefinition = new GroupDefinition("thingName: thing", "Policy1"); diff --git a/src/test/java/com/aws/greengrass/clientdevices/auth/configuration/parser/BasicRuleExpressionTest.java b/src/test/java/com/aws/greengrass/clientdevices/auth/configuration/parser/BasicRuleExpressionTest.java index d05a5daaf..ebbebe9db 100644 --- a/src/test/java/com/aws/greengrass/clientdevices/auth/configuration/parser/BasicRuleExpressionTest.java +++ b/src/test/java/com/aws/greengrass/clientdevices/auth/configuration/parser/BasicRuleExpressionTest.java @@ -95,6 +95,30 @@ void GIVEN_thingNameWithNoSpace_WHEN_RuleExpression_THEN_ruleIsParsed() throws P void GIVEN_wildcardAsThingName_WHEN_RuleExpression_THEN_ruleIsParsed() throws ParseException { expectValidExpression("thingName: *"); } + @Test + void GIVEN_thingNameWithLeadingWildcard_WHEN_RuleExpression_THEN_ruleIsParsed() throws ParseException { + expectValidExpression("thingName: *Thing"); + } + + @Test + void GIVEN_thingNameWithTrailingWildcard_WHEN_RuleExpression_THEN_ruleIsParsed() throws ParseException { + expectValidExpression("thingName: Thing*"); + } + + @Test + void GIVEN_thingNameWithLeadingAndTrailingWildcard_WHEN_RuleExpression_THEN_ruleIsParsed() throws ParseException { + expectValidExpression("thingName: *Thing*"); + } + + @Test + void GIVEN_LogicalORExpressionWithWildcards_WHEN_RuleExpression_THEN_ruleIsParsed() throws ParseException { + expectValidExpression("thingName: Thing* OR thingName: *Thing"); + } + + @Test + void GIVEN_LogicalANDExpressionWithWildcard_WHEN_RuleExpression_THEN_ruleIsParsed() throws ParseException { + expectValidExpression("thingName: Thing* AND thingName: *Thing"); + } @Test void GIVEN_basicLogicalORExpression_WHEN_RuleExpression_THEN_ruleIsParsed() throws ParseException { @@ -128,7 +152,6 @@ void GIVEN_thingNameWithUnescapedColon_WHEN_RuleExpression_THEN_exceptionIsThrow @Test void GIVEN_thingNameWithNonTrailingWildcard_WHEN_RuleExpression_THEN_exceptionIsThrown() { - expectParseException("thingName: *thing"); expectParseException("thingName: thing*2"); } } diff --git a/src/test/java/com/aws/greengrass/clientdevices/auth/configuration/parser/RuleExpressionEvaluationTest.java b/src/test/java/com/aws/greengrass/clientdevices/auth/configuration/parser/RuleExpressionEvaluationTest.java index e2f351b52..c91355ded 100644 --- a/src/test/java/com/aws/greengrass/clientdevices/auth/configuration/parser/RuleExpressionEvaluationTest.java +++ b/src/test/java/com/aws/greengrass/clientdevices/auth/configuration/parser/RuleExpressionEvaluationTest.java @@ -84,7 +84,7 @@ void GIVEN_logicalExpressionWithAndOr_WHEN_RuleExpressionEvaluatedWithSessionCon } @Test - void GIVEN_unaryExpressionWithWildcard_WHEN_RuleExpressionEvaluated_THEN_EvaluatesTrue() throws ParseException { + void GIVEN_unaryExpressionWithTrailingWildcard_WHEN_RuleExpressionEvaluated_THEN_EvaluatesTrue() throws ParseException { ASTStart tree = getTree("thingName: Thing*"); Session session = getSessionWithThing("Thing1"); RuleExpressionVisitor visitor = new ExpressionVisitor(); @@ -94,6 +94,17 @@ void GIVEN_unaryExpressionWithWildcard_WHEN_RuleExpressionEvaluated_THEN_Evaluat Assertions.assertTrue((Boolean) visitor.visit(tree, session)); } + @Test + void GIVEN_unaryExpressionWithLeadingWildcard_WHEN_RuleExpressionEvaluated_THEN_EvaluatesTrue() throws ParseException { + ASTStart tree = getTree("thingName: *Thing"); + Session session = getSessionWithThing("FirstThing"); + RuleExpressionVisitor visitor = new ExpressionVisitor(); + Assertions.assertTrue((Boolean) visitor.visit(tree, session)); + + session = getSessionWithThing("SecondThing"); + Assertions.assertTrue((Boolean) visitor.visit(tree, session)); + } + @Test void GIVEN_unaryExpressionWithWildcardThingName_WHEN_RuleExpressionEvaluated_THEN_EvaluatesTrue() throws ParseException { ASTStart tree = getTree("thingName: *"); @@ -106,10 +117,45 @@ void GIVEN_unaryExpressionWithWildcardThingName_WHEN_RuleExpressionEvaluated_THE } @Test - void GIVEN_unaryExpressionWithWildcard_WHEN_RuleExpressionEvaluatedWithSessionNotContainingThing_THEN_EvaluatesFalse() throws ParseException { + void GIVEN_unaryExpressionWithTrailingWildcard_WHEN_RuleExpressionEvaluatedWithSessionNotContainingThing_THEN_EvaluatesFalse() throws ParseException { ASTStart tree = getTree("thingName: Thing*"); Session session = getSessionWithThing("FirstThing"); RuleExpressionVisitor visitor = new ExpressionVisitor(); Assertions.assertFalse((Boolean) visitor.visit(tree, session)); } + + @Test + void GIVEN_unaryExpressionWithLeadingWildcard_WHEN_RuleExpressionEvaluatedWithSessionNotContainingThing_THEN_EvaluatesFalse() throws ParseException { + ASTStart tree = getTree("thingName: *Thing"); + Session session = getSessionWithThing("ThingExample"); + RuleExpressionVisitor visitor = new ExpressionVisitor(); + Assertions.assertFalse((Boolean) visitor.visit(tree, session)); + } + @Test + void GIVEN_unaryExpressionWithMultipleWildcards_WHEN_RuleExpressionEvaluatedWithSessionContainingThing_THEN_EvaluatesFalse() throws ParseException { + ASTStart tree = getTree("thingName: *Thing*"); + Session session = getSessionWithThing("FirstThingExample"); + RuleExpressionVisitor visitor = new ExpressionVisitor(); + Assertions.assertTrue((Boolean) visitor.visit(tree, session)); + + session = getSessionWithThing("FirstThing"); + Assertions.assertTrue((Boolean) visitor.visit(tree, session)); + + session = getSessionWithThing("ThingTwo"); + Assertions.assertTrue((Boolean) visitor.visit(tree, session)); + + session = getSessionWithThing("FirstOrSecondThingTwo"); + Assertions.assertTrue((Boolean) visitor.visit(tree, session)); + } + + @Test + void GIVEN_unaryExpressionWithMultipleWildcards_WHEN_RuleExpressionEvaluatedWithSessionNotContainingThing_THEN_EvaluatesFalse() throws ParseException { + ASTStart tree = getTree("thingName: *Thing*"); + Session session = getSessionWithThing("FirstExample"); + RuleExpressionVisitor visitor = new ExpressionVisitor(); + Assertions.assertFalse((Boolean) visitor.visit(tree, session)); + + session = getSessionWithThing("FirstThBreakingThwo"); + Assertions.assertFalse((Boolean) visitor.visit(tree, session)); + } }