diff --git a/envoy-control-core/src/main/java/pl/allegro/tech/servicemesh/envoycontrol/SimpleCache.java b/envoy-control-core/src/main/java/pl/allegro/tech/servicemesh/envoycontrol/SimpleCache.java index 0dbabcc33..d75493edc 100644 --- a/envoy-control-core/src/main/java/pl/allegro/tech/servicemesh/envoycontrol/SimpleCache.java +++ b/envoy-control-core/src/main/java/pl/allegro/tech/servicemesh/envoycontrol/SimpleCache.java @@ -94,7 +94,7 @@ public Watch createWatch( XdsRequest request, Set knownResourceNames, Consumer responseConsumer) { - return createWatch(ads, request, knownResourceNames, responseConsumer, false); + return createWatch(ads, request, knownResourceNames, responseConsumer, false, false); } /** @@ -106,7 +106,8 @@ public Watch createWatch( XdsRequest request, Set knownResourceNames, Consumer responseConsumer, - boolean hasClusterChanged) { + boolean hasClusterChanged, + boolean allowDefaultEmptyEdsUpdate) { Resources.ResourceType requestResourceType = request.getResourceType(); Preconditions.checkNotNull(requestResourceType, "unsupported type URL %s", request.getTypeUrl()); @@ -123,7 +124,7 @@ public Watch createWatch( U snapshot = snapshots.get(group); String version = snapshot == null ? "" : snapshot.version(requestResourceType, request.getResourceNamesList()); - Watch watch = new Watch(ads, request, responseConsumer); + Watch watch = new Watch(ads, allowDefaultEmptyEdsUpdate, request, responseConsumer); if (snapshot != null) { Set requestedResources = ImmutableSet.copyOf(request.getResourceNamesList()); diff --git a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/groups/GroupChangeWatcher.kt b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/groups/GroupChangeWatcher.kt index e95c897b5..84ae766e8 100644 --- a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/groups/GroupChangeWatcher.kt +++ b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/groups/GroupChangeWatcher.kt @@ -44,14 +44,16 @@ internal class GroupChangeWatcher( override fun createWatch( ads: Boolean, - request: XdsRequest, - knownResourceNames: MutableSet, - responseConsumer: Consumer, - hasClusterChanged: Boolean + request: XdsRequest?, + knownResourceNames: MutableSet?, + responseConsumer: Consumer?, + hasClusterChanged: Boolean, + allowDefaultEmptyEdsUpdate: Boolean ): Watch { val oldGroups = cache.groups() - val watch = cache.createWatch(ads, request, knownResourceNames, responseConsumer, hasClusterChanged) + val watch = cache.createWatch(ads, request, knownResourceNames, responseConsumer, hasClusterChanged, + allowDefaultEmptyEdsUpdate) val groups = cache.groups() metrics.setCacheGroupsCount(groups.size) if (oldGroups != groups) { diff --git a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/groups/NodeMetadata.kt b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/groups/NodeMetadata.kt index 025211d7d..7f39887c2 100644 --- a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/groups/NodeMetadata.kt +++ b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/groups/NodeMetadata.kt @@ -381,7 +381,7 @@ fun Value.toIncomingEndpoint(properties: SnapshotProperties): IncomingEndpoint { val oauth = properties.let { this.field("oauth")?.toOAuth(it) } return when { - paths.isNotEmpty() -> IncomingEndpoint(paths, "", PathMatchingType.PATHS, methods, clients, unlistedClientsPolicy, oauth) + paths.isNotEmpty() -> IncomingEndpoint(paths, "", PathMatchingType.PATH, methods, clients, unlistedClientsPolicy, oauth) path != null -> IncomingEndpoint(paths, path, PathMatchingType.PATH, methods, clients, unlistedClientsPolicy, oauth) pathPrefix != null -> IncomingEndpoint( paths, pathPrefix, PathMatchingType.PATH_PREFIX, methods, clients, unlistedClientsPolicy, oauth @@ -422,15 +422,15 @@ fun Value.toIncomingRateLimitEndpoint(): IncomingRateLimitEndpoint { } } -fun isMoreThanOnePropertyDefined(vararg properties: Any?): Boolean = properties.filter { - if (it != null) { - if (it is Set<*>) { - return it.isNotEmpty() - } - return true +fun isMoreThanOnePropertyDefined(vararg properties: Any?): Boolean = countNonNullAndNotEmptyProperties(properties.toList()) > 1 + +private fun countNonNullAndNotEmptyProperties(props: List): Int = props.filterNotNull().count { + if (it is Set<*>) { + it.isNotEmpty() + } else { + true } - return false -}.count() > 1 +} private fun Value?.toIncomingTimeoutPolicy(): Incoming.TimeoutPolicy { val idleTimeout: Duration? = this?.field("idleTimeout")?.toDuration() diff --git a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/JwtFilterFactory.kt b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/JwtFilterFactory.kt index 0e08e1915..e6b8ea8f8 100644 --- a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/JwtFilterFactory.kt +++ b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/JwtFilterFactory.kt @@ -84,10 +84,10 @@ class JwtFilterFactory( .build() private fun createRules(endpoints: List): Set { - return endpoints.flatMap(this::createRuleForEndpoint).toSet() + return endpoints.flatMap(this::createRulesForEndpoint).toSet() } - private fun createRuleForEndpoint(endpoint: IncomingEndpoint): List { + private fun createRulesForEndpoint(endpoint: IncomingEndpoint): Set { val providers = mutableSetOf() if (endpoint.oauth != null) { @@ -97,15 +97,45 @@ class JwtFilterFactory( providers.addAll(endpoint.clients.filter { it.name in clientToOAuthProviderName.keys } .mapNotNull { clientToOAuthProviderName[it.name] }) - return if (providers.isNotEmpty()) { + if (providers.isEmpty()) { + return emptySet() + } + + return if (endpoint.paths.isNotEmpty()) { endpoint.paths.map { - requirementRuleWithPathMatching(it, endpoint.pathMatchingType, endpoint.methods, providers) - } + requirementRuleWithPathMatching(endpoint.path, endpoint.pathMatchingType, endpoint.methods, providers) + requirementRuleWithURITemplateMatching(it, endpoint.methods, providers) + }.toSet() } else { - emptyList() + setOf(requirementRuleWithPathMatching(endpoint.path, endpoint.pathMatchingType, endpoint.methods, providers)) } } + private fun requirementRuleWithURITemplateMatching( + pathGlobPattern: String, + methods: Set, + providers: MutableSet + ): RequirementRule { + val pathMatching = RouteMatch.newBuilder().setPathMatchPolicy( + TypedExtensionConfig.newBuilder() + .setName("envoy.path.match.uri_template.uri_template_matcher") + .setTypedConfig( + Any.pack( + UriTemplateMatchConfig.newBuilder() + .setPathTemplate(pathGlobPattern) + .build() + ) + ).build() + ) + if (methods.isNotEmpty()) { + pathMatching.addHeaders(createHeaderMatcherBuilder(methods)) + } + + return RequirementRule.newBuilder() + .setMatch(pathMatching) + .setRequires(createJwtRequirement(providers)) + .build() + } + private fun requirementRuleWithPathMatching( path: String, pathMatchingType: PathMatchingType, @@ -122,36 +152,26 @@ class JwtFilterFactory( ).build() ) } - pathMatching.setPathMatchPolicy( - TypedExtensionConfig.newBuilder() - .setName("envoy.path.match.uri_template.uri_template_matcher") - .setTypedConfig( - Any.pack( - UriTemplateMatchConfig.newBuilder() - .setPathTemplate(path) - .build() - ) - ).build() - ) if (methods.isNotEmpty()) { - val methodsRegexp = methods.joinToString("|") - val headerMatcher = HeaderMatcher.newBuilder() - .setName(":method") - .setSafeRegexMatch( - RegexMatcher.newBuilder().setRegex(methodsRegexp).setGoogleRe2( - RegexMatcher.GoogleRE2.getDefaultInstance() - ).build() - ) - pathMatching.addHeaders(headerMatcher) + pathMatching.addHeaders(createHeaderMatcherBuilder(methods)) } return RequirementRule.newBuilder() .setMatch(pathMatching) - .setMatch() .setRequires(createJwtRequirement(providers)) .build() } + private fun createHeaderMatcherBuilder(methods: Set): HeaderMatcher.Builder { + return HeaderMatcher.newBuilder() + .setName(":method") + .setSafeRegexMatch( + RegexMatcher.newBuilder().setRegex(methods.joinToString("|")).setGoogleRe2( + RegexMatcher.GoogleRE2.getDefaultInstance() + ).build() + ) + } + private val requirementsForProviders: Map = jwtProviders.keys.associateWith { JwtRequirement.newBuilder().setProviderName(it).build() } diff --git a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/RBACFilterPermissions.kt b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/RBACFilterPermissions.kt index 8877c424f..26990a52e 100644 --- a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/RBACFilterPermissions.kt +++ b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/RBACFilterPermissions.kt @@ -14,11 +14,11 @@ import pl.allegro.tech.servicemesh.envoycontrol.groups.PathMatchingType class RBACFilterPermissions { fun createCombinedPermissions(incomingEndpoint: IncomingEndpoint): Permission.Builder { val permissions = listOfNotNull( - createMethodPermissions(incomingEndpoint), if (incomingEndpoint.paths.isNotEmpty()) createPathTemplatesPermissionForEndpoint(incomingEndpoint) else createPathPermissionForEndpoint(incomingEndpoint), + createMethodPermissions(incomingEndpoint), ) .map { it.build() } @@ -27,7 +27,7 @@ class RBACFilterPermissions { ) } - private fun createPathTemplatesPermissionForEndpoint(incomingEndpoint: IncomingEndpoint): Permission.Builder { + private fun createPathTemplatesPermissionForEndpoint(incomingEndpoint: IncomingEndpoint): Permission.Builder { return permission() .setOrRules(Permission.Set.newBuilder().addAllRules( incomingEndpoint.paths.map(this::createPathTemplate))) diff --git a/envoy-control-core/src/test/java/pl/allegro/tech/servicemesh/envoycontrol/v3/SimpleCacheTest.java b/envoy-control-core/src/test/java/pl/allegro/tech/servicemesh/envoycontrol/v3/SimpleCacheTest.java index 1a70af568..444b0bd38 100644 --- a/envoy-control-core/src/test/java/pl/allegro/tech/servicemesh/envoycontrol/v3/SimpleCacheTest.java +++ b/envoy-control-core/src/test/java/pl/allegro/tech/servicemesh/envoycontrol/v3/SimpleCacheTest.java @@ -92,7 +92,9 @@ public void invalidNamesListShouldReturnWatcherWithNoResponseInAdsMode() { .build()), Collections.emptySet(), responseTracker, - false); + false, + false + ); assertThatWatchIsOpenWithNoResponses(new WatchAndTracker(watch, responseTracker)); } @@ -114,7 +116,9 @@ public void invalidNamesListShouldReturnWatcherWithResponseInXdsMode() { .build()), Collections.emptySet(), responseTracker, - false); + false, + false + ); assertThat(watch.isCancelled()).isFalse(); assertThat(responseTracker.responses).isNotEmpty(); @@ -138,7 +142,9 @@ public void successfullyWatchAllResourceTypesWithSetBeforeWatch() { .build()), Collections.emptySet(), responseTracker, - false); + false, + false + ); assertThat(watch.request().getTypeUrl()).isEqualTo(typeUrl); assertThat(watch.request().getResourceNamesList()).containsExactlyElementsOf( @@ -166,7 +172,9 @@ public void shouldSendEdsWhenClusterChangedButEdsVersionDidnt() { .build()), Sets.newHashSet(""), responseTracker, - true); + true, + false + ); assertThat(watch.request().getTypeUrl()).isEqualTo(Resources.V3.ENDPOINT_TYPE_URL); assertThat(watch.request().getResourceNamesList()).containsExactlyElementsOf( @@ -194,7 +202,9 @@ public void successfullyWatchAllResourceTypesWithSetAfterWatch() { .build()), Collections.emptySet(), responseTracker, - false); + false, + false + ); return new WatchAndTracker(watch, responseTracker); })); @@ -236,7 +246,9 @@ public void successfullyWatchAllResourceTypesWithSetBeforeWatchWithRequestVersio responseTracker.accept(r); responseOrderTracker.accept(r); }, - false); + false, + false + ); return new WatchAndTracker(watch, responseTracker); })) @@ -288,7 +300,9 @@ public void successfullyWatchAllResourceTypesWithSetBeforeWatchWithSameRequestVe .build()), SNAPSHOT2.resources(typeUrl).keySet(), responseTracker, - false); + false, + false + ); return new WatchAndTracker(watch, responseTracker); })); @@ -333,7 +347,9 @@ public void successfullyWatchAllResourceTypesWithSetBeforeWatchWithSameRequestVe .build()), SNAPSHOT2.resources(typeUrl).keySet(), responseTracker, - false); + false, + false + ); return new WatchAndTracker(watch, responseTracker); })); @@ -366,7 +382,9 @@ public void setSnapshotWithVersionMatchingRequestShouldLeaveWatchOpenWithoutAddi .build()), SNAPSHOT1.resources(typeUrl).keySet(), responseTracker, - false); + false, + false + ); return new WatchAndTracker(watch, responseTracker); })); @@ -403,7 +421,9 @@ public void watchesAreReleasedAfterCancel() { .build()), Collections.emptySet(), responseTracker, - false); + false, + false + ); return new WatchAndTracker(watch, responseTracker); })); @@ -435,7 +455,9 @@ public void watchIsLeftOpenIfNotRespondedImmediately() { .build()), Collections.singleton(ROUTE_NAME), responseTracker, - false); + false, + false + ); assertThatWatchIsOpenWithNoResponses(new WatchAndTracker(watch, responseTracker)); } @@ -475,7 +497,9 @@ public void clearSnapshotWithWatches() { .build()), Collections.emptySet(), r -> { }, - false); + false, + false + ); // clearSnapshot should fail and the snapshot should be left untouched assertThat(cache.clearSnapshot(SingleNodeGroup.GROUP)).isFalse(); @@ -502,7 +526,9 @@ public void groups() { .build()), Collections.emptySet(), r -> { }, - false); + false, + false + ); assertThat(cache.groups()).containsExactly(SingleNodeGroup.GROUP); } diff --git a/envoy-control-core/src/test/java/pl/allegro/tech/servicemesh/envoycontrol/v3/SimpleCacheWithMissingEndpointsTest.java b/envoy-control-core/src/test/java/pl/allegro/tech/servicemesh/envoycontrol/v3/SimpleCacheWithMissingEndpointsTest.java index 77d9ee749..df40d6dcf 100644 --- a/envoy-control-core/src/test/java/pl/allegro/tech/servicemesh/envoycontrol/v3/SimpleCacheWithMissingEndpointsTest.java +++ b/envoy-control-core/src/test/java/pl/allegro/tech/servicemesh/envoycontrol/v3/SimpleCacheWithMissingEndpointsTest.java @@ -61,7 +61,9 @@ public void missingNamesListShouldReturnWatcherWithResponseInAdsMode() { .build()), Collections.emptySet(), responseTracker, - false); + false, + false + ); assertThatWatchReceivesSnapshotWithMissingResources(new WatchAndTracker(watch, responseTracker), SNAPSHOT_WITH_MISSING_RESOURCES); } diff --git a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/groups/NodeMetadataTest.kt b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/groups/NodeMetadataTest.kt index 6a92d5336..e63f3f85c 100644 --- a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/groups/NodeMetadataTest.kt +++ b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/groups/NodeMetadataTest.kt @@ -119,7 +119,7 @@ class NodeMetadataTest { // expects val exception = assertThrows { proto.toIncomingEndpoint(snapshotProperties()) } - assertThat(exception.status.description).isEqualTo("Precisely one of 'path', 'pathPrefix' or 'pathRegex' field is allowed") + assertThat(exception.status.description).isEqualTo("Precisely one of 'paths', 'path', 'pathPrefix' or 'pathRegex' field is allowed") assertThat(exception.status.code).isEqualTo(Status.Code.INVALID_ARGUMENT) } @@ -135,18 +135,18 @@ class NodeMetadataTest { // expects val exception = assertThrows { proto.toIncomingEndpoint(snapshotProperties()) } - assertThat(exception.status.description).isEqualTo("Precisely one of 'path', 'pathPrefix' or 'pathRegex' field is allowed") + assertThat(exception.status.description).isEqualTo("Precisely one of 'paths', 'path', 'pathPrefix' or 'pathRegex' field is allowed") assertThat(exception.status.code).isEqualTo(Status.Code.INVALID_ARGUMENT) } @Test - fun `should reject endpoint with no path or pathPrefix or pathRegex defined`() { + fun `should reject endpoint with empty paths or without path or pathPrefix or pathRegex defined`() { // given val proto = incomingEndpointProto(path = null, pathPrefix = null, pathRegex = null) // expects val exception = assertThrows { proto.toIncomingEndpoint(snapshotProperties()) } - assertThat(exception.status.description).isEqualTo("One of 'path', 'pathPrefix' or 'pathRegex' field is required") + assertThat(exception.status.description).isEqualTo("One of 'paths', 'path', 'pathPrefix' or 'pathRegex' field is required") assertThat(exception.status.code).isEqualTo(Status.Code.INVALID_ARGUMENT) } diff --git a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/SnapshotUpdaterTest.kt b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/SnapshotUpdaterTest.kt index 247f879e3..0cba50150 100644 --- a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/SnapshotUpdaterTest.kt +++ b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/SnapshotUpdaterTest.kt @@ -1121,7 +1121,8 @@ class SnapshotUpdaterTest { request: XdsRequest, knownResourceNames: MutableSet, responseConsumer: Consumer, - hasClusterChanged: Boolean + hasClusterChanged: Boolean, + allowDefaultEmptyEdsUpdate: Boolean ): Watch { throw UnsupportedOperationException("not used in testing") } diff --git a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/JwtFilterFactoryTest.kt b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/JwtFilterFactoryTest.kt index b5e9178b7..60807924d 100644 --- a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/JwtFilterFactoryTest.kt +++ b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/JwtFilterFactoryTest.kt @@ -164,7 +164,7 @@ internal class JwtFilterFactoryTest { Incoming( pathToProvider.map { (path, provider) -> IncomingEndpoint( - path, + path = path, oauth = OAuth(provider, policy = policy) ) } @@ -182,7 +182,7 @@ internal class JwtFilterFactoryTest { Incoming( pathToProvider.map { (path, _) -> IncomingEndpoint( - path, + path = path, clients = setOf(ClientWithSelector.create("oauth", "client")), oauth = null ) diff --git a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/rbac/RBACFilterFactoryJwtTest.kt b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/rbac/RBACFilterFactoryJwtTest.kt index aebc9238c..30fb45451 100644 --- a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/rbac/RBACFilterFactoryJwtTest.kt +++ b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/rbac/RBACFilterFactoryJwtTest.kt @@ -71,6 +71,7 @@ internal class RBACFilterFactoryJwtTest : RBACFilterFactoryTestUtils { permissionsEnabled = true, endpoints = listOf( IncomingEndpoint( + emptySet(), "/oauth-protected", PathMatchingType.PATH, setOf("GET"), @@ -110,6 +111,7 @@ internal class RBACFilterFactoryJwtTest : RBACFilterFactoryTestUtils { permissionsEnabled = true, endpoints = listOf( IncomingEndpoint( + emptySet(), "/oauth-protected", PathMatchingType.PATH, setOf("GET"), @@ -152,6 +154,7 @@ internal class RBACFilterFactoryJwtTest : RBACFilterFactoryTestUtils { permissionsEnabled = true, endpoints = listOf( IncomingEndpoint( + emptySet(), "/oauth-protected", PathMatchingType.PATH, setOf("GET"), @@ -178,6 +181,7 @@ internal class RBACFilterFactoryJwtTest : RBACFilterFactoryTestUtils { permissionsEnabled = true, endpoints = listOf( IncomingEndpoint( + emptySet(), "/oauth-protected", PathMatchingType.PATH, setOf("GET"), @@ -219,6 +223,7 @@ internal class RBACFilterFactoryJwtTest : RBACFilterFactoryTestUtils { permissionsEnabled = true, endpoints = listOf( IncomingEndpoint( + emptySet(), "/oauth-protected", PathMatchingType.PATH, setOf("GET"), @@ -264,6 +269,7 @@ internal class RBACFilterFactoryJwtTest : RBACFilterFactoryTestUtils { permissionsEnabled = true, endpoints = listOf( IncomingEndpoint( + emptySet(), "/oauth-protected", PathMatchingType.PATH, setOf("GET"), @@ -336,7 +342,7 @@ internal class RBACFilterFactoryJwtTest : RBACFilterFactoryTestUtils { ) = """ { "policies": { - "IncomingEndpoint(path=/oauth-protected, pathMatchingType=PATH, methods=[GET], clients=[$clientsWithSelector], unlistedClientsPolicy=$unlistedClientsPolicy, oauth=$oauth)": { + "IncomingEndpoint(paths=[], path=/oauth-protected, pathMatchingType=PATH, methods=[GET], clients=[$clientsWithSelector], unlistedClientsPolicy=$unlistedClientsPolicy, oauth=$oauth)": { "permissions": [ { "and_rules": { diff --git a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/rbac/RBACFilterFactoryTest.kt b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/rbac/RBACFilterFactoryTest.kt index e8ab1d68b..251d271bf 100644 --- a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/rbac/RBACFilterFactoryTest.kt +++ b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/rbac/RBACFilterFactoryTest.kt @@ -225,7 +225,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { @Test fun `should generate RBAC rules for incoming permissions with log unlisted clients and endpoints`() { // given - val policyName = "IncomingEndpoint(path=/example, pathMatchingType=PATH, methods=[GET, POST], " + + val policyName = "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET, POST], " + "clients=[ClientWithSelector(name=client1, selector=null, negated=false), ClientWithSelector(name=client2, selector=null, negated=false)], " + "unlistedClientsPolicy=LOG, oauth=null)" val expectedShadowRules = expectedSimpleEndpointPermissionsJson(policyName) @@ -238,6 +238,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { permissionsEnabled = true, endpoints = listOf( IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET", "POST"), @@ -258,7 +259,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { @Test fun `should generate RBAC rules for incoming permissions with block unlisted endpoints and log clients`() { // given - val policyName = "IncomingEndpoint(path=/example, pathMatchingType=PATH, methods=[GET, POST], " + + val policyName = "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET, POST], " + "clients=[ClientWithSelector(name=client1, selector=null, negated=false), ClientWithSelector(name=client2, selector=null, negated=false)], " + "unlistedClientsPolicy=LOG, oauth=null)" val expectedShadowRules = expectedSimpleEndpointPermissionsJson(policyName) @@ -271,6 +272,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { permissionsEnabled = true, endpoints = listOf( IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET", "POST"), @@ -291,7 +293,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { @Test fun `should generate RBAC rules for incoming permissions with log unlisted endpoints and block clients`() { // given - val policyName = "IncomingEndpoint(path=/example, pathMatchingType=PATH, methods=[GET, POST], " + + val policyName = "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET, POST], " + "clients=[ClientWithSelector(name=client1, selector=null, negated=false), ClientWithSelector(name=client2, selector=null, negated=false)], " + "unlistedClientsPolicy=BLOCKANDLOG, oauth=null)" val expectedShadowRules = expectedSimpleEndpointPermissionsJson(policyName) @@ -304,6 +306,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { permissionsEnabled = true, endpoints = listOf( IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET", "POST"), @@ -324,12 +327,13 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { @Test fun `should generate RBAC rules for incoming permissions with roles`() { // given - val policyName = "IncomingEndpoint(path=/example, pathMatchingType=PATH, methods=[GET, POST], " + + val policyName = "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET, POST], " + "clients=[ClientWithSelector(name=role-1, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)" val expectedRbacBuilder = getRBACFilter(expectedSimpleEndpointPermissionsJson(policyName)) val incomingPermission = Incoming( permissionsEnabled = true, endpoints = listOf(IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET", "POST"), @@ -351,6 +355,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val incomingPermission = Incoming( permissionsEnabled = true, endpoints = listOf(IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET", "POST"), @@ -383,11 +388,13 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val incomingPermission = Incoming( permissionsEnabled = true, endpoints = listOf(IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET"), setOf(ClientWithSelector.create("client1")) ), IncomingEndpoint( + emptySet(), "/example2", PathMatchingType.PATH, setOf("POST"), @@ -408,11 +415,13 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val incomingPermission = Incoming( permissionsEnabled = true, endpoints = listOf(IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET", "POST"), setOf(ClientWithSelector.create("role-1")) ), IncomingEndpoint( + emptySet(), "/example2", PathMatchingType.PATH, setOf("GET", "POST"), @@ -436,11 +445,13 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val incomingPermission = Incoming( permissionsEnabled = true, endpoints = listOf(IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET", "POST"), setOf(ClientWithSelector.create("client2"), ClientWithSelector.create("role-1")) ), IncomingEndpoint( + emptySet(), "/example2", PathMatchingType.PATH, setOf("GET", "POST"), @@ -465,6 +476,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val incomingPermission = Incoming( permissionsEnabled = true, endpoints = listOf(IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET", "POST"), @@ -485,6 +497,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val incomingPermission = Incoming( permissionsEnabled = true, endpoints = listOf(IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf() @@ -506,7 +519,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val expectedActual = """ { "policies": { - "IncomingEndpoint(path=/example, pathMatchingType=PATH, methods=[], clients=[], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[], clients=[], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { "permissions": [{ "and_rules": { "rules": [ ${pathRule("/example")} ] @@ -601,6 +614,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val incomingPermission = Incoming( permissionsEnabled = true, endpoints = listOf(IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET"), @@ -622,6 +636,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val incomingPermission = Incoming( permissionsEnabled = true, endpoints = listOf(IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET"), @@ -646,6 +661,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val incomingPermission = Incoming( permissionsEnabled = true, endpoints = listOf(IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET"), @@ -670,6 +686,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val incomingPermission = Incoming( permissionsEnabled = true, endpoints = listOf(IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET"), @@ -694,6 +711,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val incomingPermission = Incoming( permissionsEnabled = true, endpoints = listOf(IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET"), @@ -722,6 +740,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val incomingPermission = Incoming( permissionsEnabled = true, endpoints = listOf(IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET"), @@ -745,6 +764,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val incomingPermission = Incoming( permissionsEnabled = true, endpoints = listOf(IncomingEndpoint( + emptySet(), "/default", PathMatchingType.PATH, setOf("GET"), @@ -768,6 +788,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val incomingPermission = Incoming( permissionsEnabled = true, endpoints = listOf(IncomingEndpoint( + emptySet(), "/custom", PathMatchingType.PATH, setOf("GET"), @@ -788,7 +809,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedEndpointPermissionsWithDifferentRulesForDifferentClientsJson = """ { "policies": { - "IncomingEndpoint(path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client1, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client1, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { "permissions": [ { "and_rules": { @@ -808,7 +829,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { ${originalAndAuthenticatedPrincipal("client1")} ] }, - "IncomingEndpoint(path=/example2, pathMatchingType=PATH, methods=[POST], clients=[ClientWithSelector(name=client2, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "IncomingEndpoint(paths=[], path=/example2, pathMatchingType=PATH, methods=[POST], clients=[ClientWithSelector(name=client2, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { "permissions": [ { "and_rules": { @@ -835,7 +856,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedSourceIpFromDiscoveryWithSelectorAuthPermissionsJson = """ { "policies": { - "IncomingEndpoint(path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client1, selector=selector, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client1, selector=selector, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { "permissions": [ { "and_rules": { @@ -870,7 +891,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedSourceIpWithSelectorAuthPermissionsJson = """ { "policies": { - "IncomingEndpoint(path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client2, selector=selector, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client2, selector=selector, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { "permissions": [ { "and_rules": { @@ -905,7 +926,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedSourceIpWithStaticRangeAndSelectorAuthPermissionsAndRolesJson = """ { "policies": { - "IncomingEndpoint(path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=role1, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=role1, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { "permissions": [ { "and_rules": { @@ -949,7 +970,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedSourceIpAuthPermissionsJson = """ { "policies": { - "IncomingEndpoint(path=/example, pathMatchingType=PATH, methods=[GET, POST], clients=[ClientWithSelector(name=client1, selector=null, negated=false), ClientWithSelector(name=client2, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET, POST], clients=[ClientWithSelector(name=client1, selector=null, negated=false), ClientWithSelector(name=client2, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { "permissions": [ { "and_rules": { @@ -1008,7 +1029,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { { "policies": { """ /* notice that duplicated clients occurs only once here */ + """ - "IncomingEndpoint(path=/example, pathMatchingType=PATH, methods=[GET, POST], clients=[ClientWithSelector(name=client1, selector=null, negated=false), ClientWithSelector(name=client1, selector=selector, negated=false), ClientWithSelector(name=client1-duplicated, selector=selector, negated=false), ClientWithSelector(name=client1-duplicated, selector=null, negated=false), ClientWithSelector(name=role-1, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET, POST], clients=[ClientWithSelector(name=client1, selector=null, negated=false), ClientWithSelector(name=client1, selector=selector, negated=false), ClientWithSelector(name=client1-duplicated, selector=selector, negated=false), ClientWithSelector(name=client1-duplicated, selector=null, negated=false), ClientWithSelector(name=role-1, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { "permissions": [ { "and_rules": { @@ -1228,7 +1249,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedSourceIpAuthWithStaticRangeJson = """ { "policies": { - "IncomingEndpoint(path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client1, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client1, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { "permissions": [ { "and_rules": { @@ -1255,7 +1276,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedSourceIpAuthWithStaticRangeAndSourceIpJson = """ { "policies": { - "IncomingEndpoint(path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client1, selector=null, negated=false), ClientWithSelector(name=client2, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client1, selector=null, negated=false), ClientWithSelector(name=client2, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { "permissions": [ { "and_rules": { @@ -1292,7 +1313,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedEndpointPermissionsLogUnlistedEndpointsAndBlockUnlistedClients = """ { "policies": { - "IncomingEndpoint(path=/example, pathMatchingType=PATH, methods=[GET, POST], clients=[ClientWithSelector(name=client1, selector=null, negated=false), ClientWithSelector(name=client2, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET, POST], clients=[ClientWithSelector(name=client1, selector=null, negated=false), ClientWithSelector(name=client2, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { "permissions": [ { "and_rules": { @@ -1348,7 +1369,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private fun expectedPoliciesForAllowedClient(principals: String) = """ { "policies": { - "IncomingEndpoint(path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client1, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client1, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { "permissions": [ { "and_rules": { @@ -1373,7 +1394,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private fun expectedPoliciesForDefaultAndCustomLists(clients: List, principals: List, path: String) = """ { "policies": { - "IncomingEndpoint(path=$path, pathMatchingType=PATH, methods=[GET], clients=[${clients.joinToString(", ") { "ClientWithSelector(name=$it, selector=null, negated=false)" }}], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "IncomingEndpoint(paths=[], path=$path, pathMatchingType=PATH, methods=[GET], clients=[${clients.joinToString(", ") { "ClientWithSelector(name=$it, selector=null, negated=false)" }}], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { "permissions": [ { "and_rules": {