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 c3e57573d..904f1f990 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 @@ -819,7 +819,7 @@ data class IncomingRateLimitEndpoint( ) enum class PathMatchingType { - PATH, PATH_PREFIX, PATH_REGEX + PATH, PATH_PREFIX, PATH_REGEX, PATHS } enum class CommunicationMode { @@ -846,6 +846,15 @@ interface EndpointBase { val path: String val pathMatchingType: PathMatchingType val methods: Set + + fun extractRule(): String { + return when (pathMatchingType) { + PathMatchingType.PATH -> "path:$path" + PathMatchingType.PATH_PREFIX -> "pathPrefix:$path" + PathMatchingType.PATH_REGEX -> "pathRegex:$path" + PathMatchingType.PATHS -> "paths${paths.joinToString(",")}" + } + } } // We don't distinguish between absence of the field and the field with explicit null value. 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 b5ee3bce7..eba13a7c5 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 @@ -117,66 +117,50 @@ class JwtFilterFactory( return emptySet() } - return if (endpoint.paths.isNotEmpty()) { - endpoint.paths.map { - requirementRuleWithURITemplateMatching(it, endpoint.methods, providers) - }.toSet() - } else { - setOf(requirementRuleWithPathMatching( - endpoint.path, endpoint.pathMatchingType, endpoint.methods, providers)) - } + return requirementRuleWithPathMatching(endpoint, endpoint.methods, providers) } - - private fun requirementRuleWithURITemplateMatching( - pathGlobPattern: String, + private fun requirementRuleWithPathMatching( + endpoint: IncomingEndpoint, 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() + ): Set { + val pathMatching = when (endpoint.pathMatchingType) { + PathMatchingType.PATH -> listOf(RouteMatch.newBuilder().setPath(endpoint.path)) + PathMatchingType.PATH_PREFIX -> listOf(RouteMatch.newBuilder().setPrefix(endpoint.path)) + PathMatchingType.PATH_REGEX -> listOf( + RouteMatch.newBuilder() + .setSafeRegex( + RegexMatcher.newBuilder().setRegex(endpoint.path).setGoogleRe2( + RegexMatcher.GoogleRE2.getDefaultInstance() + ).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, - methods: Set, - providers: MutableSet - ): RequirementRule { - val pathMatching = when (pathMatchingType) { - PathMatchingType.PATH -> RouteMatch.newBuilder().setPath(path) - PathMatchingType.PATH_PREFIX -> RouteMatch.newBuilder().setPrefix(path) - PathMatchingType.PATH_REGEX -> RouteMatch.newBuilder() - .setSafeRegex( - RegexMatcher.newBuilder().setRegex(path).setGoogleRe2( - RegexMatcher.GoogleRE2.getDefaultInstance() - ).build() + PathMatchingType.PATHS -> endpoint.paths.map { path -> + RouteMatch.newBuilder().setPathMatchPolicy( + TypedExtensionConfig.newBuilder() + .setName("envoy.path.match.uri_template.uri_template_matcher") + .setTypedConfig( + Any.pack( + UriTemplateMatchConfig.newBuilder() + .setPathTemplate(path) + .build() + ) + ).build() ) + } } + if (methods.isNotEmpty()) { - pathMatching.addHeaders(createHeaderMatcherBuilder(methods)) + pathMatching.forEach { it.addHeaders(createHeaderMatcherBuilder(methods)) } } - return RequirementRule.newBuilder() - .setMatch(pathMatching) - .setRequires(createJwtRequirement(providers)) - .build() + return pathMatching.map { + RequirementRule.newBuilder() + .setMatch(it) + .setRequires(createJwtRequirement(providers)) + .build() + }.toSet() } private fun createHeaderMatcherBuilder(methods: Set): HeaderMatcher.Builder { diff --git a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/RBACFilterFactory.kt b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/RBACFilterFactory.kt index 3e0cdbd8e..ab26a6788 100644 --- a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/RBACFilterFactory.kt +++ b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/RBACFilterFactory.kt @@ -18,6 +18,7 @@ import pl.allegro.tech.servicemesh.envoycontrol.groups.Group import pl.allegro.tech.servicemesh.envoycontrol.groups.Incoming import pl.allegro.tech.servicemesh.envoycontrol.groups.IncomingEndpoint import pl.allegro.tech.servicemesh.envoycontrol.groups.OAuth +import pl.allegro.tech.servicemesh.envoycontrol.groups.PathMatchingType import pl.allegro.tech.servicemesh.envoycontrol.groups.Role import pl.allegro.tech.servicemesh.envoycontrol.logger import pl.allegro.tech.servicemesh.envoycontrol.snapshot.Client @@ -180,13 +181,13 @@ class RBACFilterFactory( it.endpoint.unlistedClientsPolicy == Incoming.UnlistedPolicy.BLOCKANDLOG || it.endpoint.oauth?.policy != null } - .map { (endpoint, policy) -> "$endpoint" to policy }.toMap() + .map { (endpoint, policy) -> endpoint.extractRule() to policy }.toMap() val loggedEndpointsPolicies = incomingEndpointsPolicies.asSequence() .filter { it.endpoint.unlistedClientsPolicy == Incoming.UnlistedPolicy.LOG && it.endpoint.oauth?.policy == null } - .map { (endpoint, policy) -> "$endpoint" to policy }.toMap() + .map { (endpoint, policy) -> endpoint.extractRule() to policy }.toMap() val allowUnlistedPolicies = unlistedAndLoggedEndpointsPolicies( incomingPermissions, @@ -277,9 +278,16 @@ class RBACFilterFactory( return if (statusRouteProperties.enabled) { val permissions = statusRouteProperties.endpoints .map { - rBACFilterPermissions.createPathPermission( + IncomingEndpoint( + paths = emptySet(), path = it.path, - matchingType = it.matchingType + pathMatchingType = PathMatchingType.PATH_PREFIX + ) + } + .map { + rBACFilterPermissions.createPathPermission( + endpoint = it, + matchingType = it.pathMatchingType ).build() } val policy = Policy.newBuilder() 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 64e54c0bd..f2e77a584 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 @@ -1,6 +1,7 @@ package pl.allegro.tech.servicemesh.envoycontrol.snapshot.resource.listeners.filters import com.google.protobuf.Any +import com.google.protobuf.MessageOrBuilder import io.envoyproxy.envoy.config.core.v3.TypedExtensionConfig import io.envoyproxy.envoy.config.rbac.v3.Permission import io.envoyproxy.envoy.config.route.v3.HeaderMatcher @@ -14,42 +15,33 @@ import pl.allegro.tech.servicemesh.envoycontrol.groups.PathMatchingType class RBACFilterPermissions { fun createCombinedPermissions(incomingEndpoint: IncomingEndpoint): Permission.Builder { val permissions = listOfNotNull( - if (incomingEndpoint.paths.isNotEmpty()) { - createPathTemplatesPermissionForEndpoint(incomingEndpoint) - } else { - createPathPermissionForEndpoint(incomingEndpoint) - }, + createPathPermissionForEndpoint(incomingEndpoint), createMethodPermissions(incomingEndpoint), - ) - .map { it.build() } + ).map { it.build() } return permission().setAndRules( Permission.Set.newBuilder().addAllRules(permissions) ) } - private fun createPathTemplatesPermissionForEndpoint(incomingEndpoint: IncomingEndpoint): Permission.Builder { - return permission() - .setOrRules(Permission.Set.newBuilder().addAllRules( - incomingEndpoint.paths.map(this::createPathTemplate))) - } - - private fun createPathTemplate(path: String): Permission { - return permission().setUriTemplate(TypedExtensionConfig.newBuilder() - .setName("envoy.path.match.uri_template.uri_template_matcher") - .setTypedConfig(Any.pack( - UriTemplateMatchConfig.newBuilder() - .setPathTemplate(path) - .build() - ))).build() - } + fun createPathPermission(endpoint: IncomingEndpoint, matchingType: PathMatchingType): Permission.Builder { + val matcherBuilder = createMatcherBuilder(endpoint, matchingType) + return when (matchingType) { + PathMatchingType.PATHS -> { + val pathTemplates = + matcherBuilder.map { permission().setUriTemplate(it as TypedExtensionConfig.Builder) } + permission().setOrRules(Permission.Set.newBuilder().addAllRules(pathTemplates.map { it.build() })) + } - fun createPathPermission(path: String, matchingType: PathMatchingType): Permission.Builder { - return permission().setUrlPath(createPathMatcher(path, matchingType)) + else -> { + val pathMatcher = PathMatcher.newBuilder().setPath(matcherBuilder.first() as StringMatcher).build() + permission().setUrlPath(pathMatcher) + } + } } private fun createPathPermissionForEndpoint(incomingEndpoint: IncomingEndpoint): Permission.Builder { - return createPathPermission(incomingEndpoint.path, incomingEndpoint.pathMatchingType) + return createPathPermission(incomingEndpoint, incomingEndpoint.pathMatchingType) } private fun createMethodPermissions(incomingEndpoint: IncomingEndpoint): Permission.Builder? { @@ -67,35 +59,39 @@ class RBACFilterPermissions { return permission().setHeader(methodMatch).build() } - private fun createPathMatcher(path: String, matchingType: PathMatchingType): PathMatcher { - val matcher = when (matchingType) { - PathMatchingType.PATH -> StringMatcher.newBuilder().setExact(path).build() - PathMatchingType.PATH_PREFIX -> StringMatcher.newBuilder().setPrefix(path).build() - PathMatchingType.PATH_REGEX -> safeRegexMatcher(path) + private fun createMatcherBuilder( + endpoint: IncomingEndpoint, matchingType: PathMatchingType + ): List { + val builder = when (matchingType) { + PathMatchingType.PATH -> listOf(StringMatcher.newBuilder().setExact(endpoint.path).build()) + PathMatchingType.PATH_PREFIX -> listOf(StringMatcher.newBuilder().setPrefix(endpoint.path).build()) + PathMatchingType.PATH_REGEX -> listOf(safeRegexMatcher(endpoint.path)) + PathMatchingType.PATHS -> endpoint.paths.map { + TypedExtensionConfig.newBuilder().setName("envoy.path.match.uri_template.uri_template_matcher") + .setTypedConfig( + Any.pack( + UriTemplateMatchConfig.newBuilder().setPathTemplate(it).build() + ) + ) + } } - return PathMatcher.newBuilder().setPath(matcher).build() + return builder } } -private fun safeRegexMatcher(regexPattern: String) = StringMatcher.newBuilder() - .setSafeRegex( - RegexMatcher.newBuilder() - .setRegex(regexPattern) - .setGoogleRe2( - RegexMatcher.GoogleRE2.getDefaultInstance() - ) - .build() - ) - .build() +private fun safeRegexMatcher(regexPattern: String) = StringMatcher.newBuilder().setSafeRegex( + RegexMatcher.newBuilder().setRegex(regexPattern).setGoogleRe2( + RegexMatcher.GoogleRE2.getDefaultInstance() + ).build() +).build() private fun permission() = Permission.newBuilder() private fun not(permission: Permission.Builder) = permission().setNotRule(permission) -fun anyOf(permissions: Iterable): Permission.Builder = permission() - .setOrRules(Permission.Set.newBuilder().addAllRules(permissions)) +fun anyOf(permissions: Iterable): Permission.Builder = + permission().setOrRules(Permission.Set.newBuilder().addAllRules(permissions)) -fun noneOf(permissions: List): Permission.Builder = - if (permissions.isNotEmpty()) { - not(anyOf(permissions)) - } else { - permission().setAny(true) - } +fun noneOf(permissions: List): Permission.Builder = if (permissions.isNotEmpty()) { + not(anyOf(permissions)) +} else { + permission().setAny(true) +} diff --git a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/routes/EnvoyIngressRoutesFactory.kt b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/routes/EnvoyIngressRoutesFactory.kt index 52468d9d9..a9e43a20f 100644 --- a/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/routes/EnvoyIngressRoutesFactory.kt +++ b/envoy-control-core/src/main/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/routes/EnvoyIngressRoutesFactory.kt @@ -128,6 +128,7 @@ class EnvoyIngressRoutesFactory( PathMatchingType.PATH_PREFIX -> HeaderMatcher.newBuilder().setName(":path").setPrefixMatch(path).build() PathMatchingType.PATH -> HeaderMatcher.newBuilder().setName(":path").setExactMatch(path).build() PathMatchingType.PATH_REGEX -> HeaderMatcher.newBuilder().setName(":path").setRe2Match(path).build() + PathMatchingType.PATHS -> throw IllegalArgumentException("PathMatchingType.PATHS is not supported") } private fun HeaderMatcher.Builder.setRe2Match(regexPattern: String) = this diff --git a/envoy-control-core/src/main/resources/lua/ingress_rbac_logging.lua b/envoy-control-core/src/main/resources/lua/ingress_rbac_logging.lua index 9478adf9f..1d3238bf1 100644 --- a/envoy-control-core/src/main/resources/lua/ingress_rbac_logging.lua +++ b/envoy-control-core/src/main/resources/lua/ingress_rbac_logging.lua @@ -76,6 +76,7 @@ function envoy_on_response(handle) local dynamic_metadata = handle:streamInfo():dynamicMetadata() local rbacMetadata = dynamic_metadata:get('envoy.filters.http.rbac') or {} local is_shadow_denied = (rbacMetadata['shadow_engine_result'] or '') == 'denied' + local rule = rbacMetadata['shadow_effective_policy_id'] or '' if is_shadow_denied then local headers = handle:headers() @@ -92,11 +93,11 @@ function envoy_on_response(handle) if upstream_request_time == nil and status_code == '403' then rbac_action = 'denied' end - log_request(handle, lua_metadata, jwt_status, rbac_action) + log_request(handle, rule, lua_metadata, jwt_status, rbac_action) end end -function log_request(handle, lua_metadata, jwt_status, rbac_action) +function log_request(handle, rule, lua_metadata, jwt_status, rbac_action) local client_name = lua_metadata['request.info.client_name'] or '' local trusted_client = lua_metadata['request.info.trusted_client'] or false local path = lua_metadata['request.info.path'] or '' @@ -112,6 +113,7 @@ function log_request(handle, lua_metadata, jwt_status, rbac_action) local message = { '\nINCOMING_PERMISSIONS {"method":"', method, + '","rule":"', rule, '","path":"', path, '","clientIp":"', source_ip, '","clientName":"', escape(client_name), diff --git a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/RBACFilterPermissionsTest.kt b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/RBACFilterPermissionsTest.kt index 600e91bbe..29796ca29 100644 --- a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/RBACFilterPermissionsTest.kt +++ b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/RBACFilterPermissionsTest.kt @@ -2,6 +2,7 @@ package pl.allegro.tech.servicemesh.envoycontrol.snapshot.resource.listeners.fil import com.google.protobuf.util.JsonFormat import io.envoyproxy.envoy.config.rbac.v3.Permission +import io.envoyproxy.envoy.extensions.path.match.uri_template.v3.UriTemplateMatchConfig import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import pl.allegro.tech.servicemesh.envoycontrol.groups.IncomingEndpoint @@ -105,6 +106,71 @@ internal class RBACFilterPermissionsTest { assertThat(permissions).isEqualTo(expectedPermission) } + @Test + fun `should create permissions with uri template matchers`() { + // given + val endpoint = IncomingEndpoint( + paths = setOf("/example", "/example/*"), + pathMatchingType = PathMatchingType.PATHS, + methods = setOf("PUT", "DELETE") + ) + // language=json + val expectedPermission = """{ + "and_rules": { + "rules": [ + { + "or_rules": { + "rules": [ + { + "uri_template": { + "name": "envoy.path.match.uri_template.uri_template_matcher", + "typed_config": { + "@type": "type.googleapis.com/envoy.extensions.path.match.uri_template.v3.UriTemplateMatchConfig", + "path_template": "/example" + } + } + }, + { + "uri_template": { + "name": "envoy.path.match.uri_template.uri_template_matcher", + "typed_config": { + "@type": "type.googleapis.com/envoy.extensions.path.match.uri_template.v3.UriTemplateMatchConfig", + "path_template": "/example/*" + } + } + } + ] + } + }, + { + "or_rules": { + "rules": [ + { + "header": { + "name": ":method", + "exact_match": "PUT" + } + }, + { + "header": { + "name": ":method", + "exact_match": "DELETE" + } + } + ] + } + } + ] + } + }""".asPermission() + + // when + val permissions = rbacFilterPermissions.createCombinedPermissions(endpoint).build() + + // then + assertThat(permissions).isEqualTo(expectedPermission) + } + @Test fun `should create permissions with method matchers`() { // given @@ -155,7 +221,8 @@ internal class RBACFilterPermissionsTest { private fun String.asPermission(): Permission { val builder = Permission.newBuilder() - JsonFormat.parser().merge(this, builder) + val typeRegistry = JsonFormat.TypeRegistry.newBuilder().add(UriTemplateMatchConfig.getDescriptor()).build() + JsonFormat.parser().usingTypeRegistry(typeRegistry).merge(this, builder) return builder.build() } } 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 30fb45451..ad998cbca 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 @@ -57,13 +57,9 @@ internal class RBACFilterFactoryJwtTest : RBACFilterFactoryTestUtils { val expectedRbacBuilder = getRBACFilterWithShadowRules( expectedPoliciesForOAuth( oAuthPolicyPrincipal, - "ClientWithSelector(name=$client, selector=null, negated=false)", - "OAuth(provider=oauth-provider, verification=OFFLINE, policy=$policy)" ), expectedPoliciesForOAuth( oAuthPolicyPrincipal, - "ClientWithSelector(name=$client, selector=null, negated=false)", - "OAuth(provider=oauth-provider, verification=OFFLINE, policy=$policy)" ) ) @@ -98,13 +94,9 @@ internal class RBACFilterFactoryJwtTest : RBACFilterFactoryTestUtils { val expectedRbacBuilder = getRBACFilterWithShadowRules( expectedPoliciesForOAuth( oAuthPrincipal, - "ClientWithSelector(name=$client, selector=$selector, negated=false)", - "OAuth(provider=oauth-provider, verification=OFFLINE, policy=ALLOW_MISSING_OR_FAILED)" ), expectedPoliciesForOAuth( oAuthPrincipal, - "ClientWithSelector(name=$client, selector=$selector, negated=false)", - "OAuth(provider=oauth-provider, verification=OFFLINE, policy=ALLOW_MISSING_OR_FAILED)" ) ) val incomingPermission = Incoming( @@ -141,13 +133,9 @@ internal class RBACFilterFactoryJwtTest : RBACFilterFactoryTestUtils { val expectedRbacBuilder = getRBACFilterWithShadowRules( expectedPoliciesForOAuth( conjunctionOfPrincipals(oAuthPrincipal, negatedOAuthPrincipal), - "ClientWithSelector(name=$client, selector=$selector, negated=false), ClientWithSelector(name=$client, selector=$negatedSelector, negated=true)", - "OAuth(provider=oauth-provider, verification=OFFLINE, policy=ALLOW_MISSING_OR_FAILED)" ), expectedPoliciesForOAuth( conjunctionOfPrincipals(oAuthPrincipal, negatedOAuthPrincipal), - "ClientWithSelector(name=$client, selector=$selector, negated=false), ClientWithSelector(name=$client, selector=$negatedSelector, negated=true)", - "OAuth(provider=oauth-provider, verification=OFFLINE, policy=ALLOW_MISSING_OR_FAILED)" ) ) val incomingPermission = Incoming( @@ -197,13 +185,9 @@ internal class RBACFilterFactoryJwtTest : RBACFilterFactoryTestUtils { val expectedRbacBuilder = getRBACFilterWithShadowRules( expectedPoliciesForOAuth( originalAndAuthenticatedPrincipal("client1"), - "ClientWithSelector(name=client1, selector=null, negated=false)", - "OAuth(provider=oauth-provider, verification=OFFLINE, policy=ALLOW_MISSING_OR_FAILED)" ), expectedPoliciesForOAuth( originalAndAuthenticatedPrincipal("client1"), - "ClientWithSelector(name=client1, selector=null, negated=false)", - "OAuth(provider=oauth-provider, verification=OFFLINE, policy=ALLOW_MISSING_OR_FAILED)" ) ) @@ -241,15 +225,9 @@ internal class RBACFilterFactoryJwtTest : RBACFilterFactoryTestUtils { val expectedRbacBuilder = getRBACFilterWithShadowRules( expectedPoliciesForOAuth( oAuthPrincipalsNoClients(policy), - "", - "OAuth(provider=oauth-provider, verification=OFFLINE, policy=$policy)", - "$unlistedClientsPolicy" ), expectedPoliciesForOAuth( oAuthPrincipalsNoClients(policy), - "", - "OAuth(provider=oauth-provider, verification=OFFLINE, policy=$policy)", - "$unlistedClientsPolicy" ) ) @@ -287,15 +265,9 @@ internal class RBACFilterFactoryJwtTest : RBACFilterFactoryTestUtils { val expectedRbacBuilder = getRBACFilterWithShadowRules( expectedPoliciesForOAuth( """{ "not_id": {"any": "true"} }""", - "", - "OAuth(provider=oauth-provider, verification=OFFLINE, policy=$policy)", - "$unlistedClientsPolicy" ), expectedPoliciesForOAuth( """{ "not_id": {"any": "true"} }""", - "", - "OAuth(provider=oauth-provider, verification=OFFLINE, policy=$policy)", - "$unlistedClientsPolicy" ) ) @@ -336,13 +308,10 @@ internal class RBACFilterFactoryJwtTest : RBACFilterFactoryTestUtils { private fun expectedPoliciesForOAuth( principals: String, - clientsWithSelector: String, - oauth: String, - unlistedClientsPolicy: String = "BLOCKANDLOG" ) = """ { "policies": { - "IncomingEndpoint(paths=[], path=/oauth-protected, pathMatchingType=PATH, methods=[GET], clients=[$clientsWithSelector], unlistedClientsPolicy=$unlistedClientsPolicy, oauth=$oauth)": { + "path:/oauth-protected": { "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 251d271bf..fd6985f83 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 @@ -27,41 +27,37 @@ import pl.allegro.tech.servicemesh.envoycontrol.snapshot.resource.listeners.filt @Suppress("LargeClass") // TODO: https://github.com/allegro/envoy-control/issues/121 internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val rbacFilterFactory = RBACFilterFactory( - IncomingPermissionsProperties().also { - it.enabled = true - it.overlappingPathsFix = true - }, - StatusRouteProperties() + IncomingPermissionsProperties().also { + it.enabled = true + it.overlappingPathsFix = true + }, StatusRouteProperties() ) private val rbacFilterFactoryWithSourceIpAuth = RBACFilterFactory( - IncomingPermissionsProperties().also { - it.enabled = true - it.sourceIpAuthentication = SourceIpAuthenticationProperties().also { ipProperties -> - ipProperties.ipFromServiceDiscovery.enabledForIncomingServices = listOf("client1") - } - }, - StatusRouteProperties() + IncomingPermissionsProperties().also { + it.enabled = true + it.sourceIpAuthentication = SourceIpAuthenticationProperties().also { ipProperties -> + ipProperties.ipFromServiceDiscovery.enabledForIncomingServices = listOf("client1") + } + }, StatusRouteProperties() ) private val rbacFilterFactoryWithStaticRange = RBACFilterFactory( - IncomingPermissionsProperties().also { - it.enabled = true - it.sourceIpAuthentication = SourceIpAuthenticationProperties().also { ipProperties -> - ipProperties.ipFromRange = mutableMapOf( - "client1" to setOf("192.168.1.0/24", "192.168.2.0/28") - ) - } - }, - StatusRouteProperties() + IncomingPermissionsProperties().also { + it.enabled = true + it.sourceIpAuthentication = SourceIpAuthenticationProperties().also { ipProperties -> + ipProperties.ipFromRange = mutableMapOf( + "client1" to setOf("192.168.1.0/24", "192.168.2.0/28") + ) + } + }, StatusRouteProperties() ) private val rbacFilterFactoryWithStaticRangeAndSourceIpAuth = RBACFilterFactory( - IncomingPermissionsProperties().also { - it.enabled = true - it.sourceIpAuthentication = SourceIpAuthenticationProperties().also { ipProperties -> - ipProperties.ipFromServiceDiscovery.enabledForIncomingServices = listOf("client1") - ipProperties.ipFromRange = mutableMapOf("client2" to setOf("192.168.1.0/24", "192.168.2.0/28")) - } - }, - StatusRouteProperties() + IncomingPermissionsProperties().also { + it.enabled = true + it.sourceIpAuthentication = SourceIpAuthenticationProperties().also { ipProperties -> + ipProperties.ipFromServiceDiscovery.enabledForIncomingServices = listOf("client1") + ipProperties.ipFromRange = mutableMapOf("client2" to setOf("192.168.1.0/24", "192.168.2.0/28")) + } + }, StatusRouteProperties() ) private val rbacFilterFactoryWithSourceIpWithSelectorAuth = RBACFilterFactory( IncomingPermissionsProperties().also { @@ -72,19 +68,15 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { "client2" to setOf("192.168.1.0/24", "192.168.2.0/28") ) } - it.selectorMatching = mutableMapOf( - "client1" to SelectorMatching().also { it.header = "x-secret-header" }, - "client2" to SelectorMatching().also { it.header = "x-secret-header" } - ) - }, - StatusRouteProperties() + it.selectorMatching = mutableMapOf("client1" to SelectorMatching().also { it.header = "x-secret-header" }, + "client2" to SelectorMatching().also { it.header = "x-secret-header" }) + }, StatusRouteProperties() ) private val rbacFilterFactoryWithAllowAllEndpointsForClient = RBACFilterFactory( IncomingPermissionsProperties().also { it.enabled = true it.clientsAllowedToAllEndpoints = mutableListOf("allowed-client") - }, - StatusRouteProperties() + }, StatusRouteProperties() ) private val rbacFilterFactoryWithDefaultAndCustomClientsLists = RBACFilterFactory( @@ -93,12 +85,10 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { it.clientsLists = ClientsListsProperties().also { it.defaultClientsList = listOf("default-client", "xyz") it.customClientsLists = mapOf( - "custom1" to listOf("custom1-client", "xyz"), - "ad:custom2" to listOf("ad:custom2-client", "xyz") + "custom1" to listOf("custom1-client", "xyz"), "ad:custom2" to listOf("ad:custom2-client", "xyz") ) } - }, - StatusRouteProperties() + }, StatusRouteProperties() ) val snapshot = GlobalSnapshot( @@ -109,19 +99,17 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { SnapshotResources.create(listOf(), "").resources() ) - val clusterLoadAssignment = ClusterLoadAssignment.newBuilder() - .setClusterName("client1") - .addEndpoints(LocalityLbEndpoints.newBuilder() - .addLbEndpoints(LbEndpoint.newBuilder() - .setEndpoint(Endpoint.newBuilder() - .setAddress(Address.newBuilder() - .setSocketAddress(SocketAddress.newBuilder() - .setAddress("127.0.0.1") - ) - ) - ) + val clusterLoadAssignment = ClusterLoadAssignment.newBuilder().setClusterName("client1").addEndpoints( + LocalityLbEndpoints.newBuilder().addLbEndpoints( + LbEndpoint.newBuilder().setEndpoint( + Endpoint.newBuilder().setAddress( + Address.newBuilder().setSocketAddress( + SocketAddress.newBuilder().setAddress("127.0.0.1") ) - ).build() + ) + ) + ) + ).build() val snapshotForSourceIpAuth = GlobalSnapshot( SnapshotResources.create(listOf(), "").resources(), @@ -134,10 +122,9 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { @Test fun `should create RBAC filter with status route permissions when no incoming permissions are defined`() { // given - val rbacFilterFactoryWithStatusRoute = RBACFilterFactory( - IncomingPermissionsProperties().also { it.enabled = true }, - StatusRouteProperties().also { it.enabled = true; it.endpoints = mutableListOf(EndpointMatch()) } - ) + val rbacFilterFactoryWithStatusRoute = + RBACFilterFactory(IncomingPermissionsProperties().also { it.enabled = true }, + StatusRouteProperties().also { it.enabled = true; it.endpoints = mutableListOf(EndpointMatch()) }) val incomingPermission = Incoming(permissionsEnabled = true) val expectedRbacBuilder = getRBACFilter(expectedStatusRoutePermissionsJson) @@ -151,15 +138,12 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { @Test fun `should create RBAC filter with two status routes permissions when no incoming permissions are defined`() { // given - val rbacFilterFactoryWithStatusRoute = RBACFilterFactory( - IncomingPermissionsProperties().also { it.enabled = true }, - StatusRouteProperties().also { it.enabled = true; it.endpoints = - mutableListOf( - EndpointMatch(), - EndpointMatch().also { endpoint -> endpoint.path = "/example-endpoint/"; endpoint.matchingType = PathMatchingType.PATH } - ) - } - ) + val rbacFilterFactoryWithStatusRoute = + RBACFilterFactory(IncomingPermissionsProperties().also { it.enabled = true }, StatusRouteProperties().also { + it.enabled = true; it.endpoints = mutableListOf(EndpointMatch(), EndpointMatch().also { endpoint -> + endpoint.path = "/example-endpoint/"; endpoint.matchingType = PathMatchingType.PATH + }) + }) val incomingPermission = Incoming(permissionsEnabled = true) val expectedRbacBuilder = getRBACFilter(expectedTwoStatusRoutesPermissionsJson) @@ -211,8 +195,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { // given val expectedRbacBuilder = getRBACFilter(expectedEmptyEndpointPermissions) val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf() + permissionsEnabled = true, endpoints = listOf() ) // when @@ -225,28 +208,23 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { @Test fun `should generate RBAC rules for incoming permissions with log unlisted clients and endpoints`() { // given - 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 policyName = "path:/example" val expectedShadowRules = expectedSimpleEndpointPermissionsJson(policyName) val expectedRbacBuilder = getRBACFilterWithShadowRules( - expectedAnyPermissionJson, - expectedShadowRules + expectedAnyPermissionJson, expectedShadowRules ) val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf( + permissionsEnabled = true, endpoints = listOf( IncomingEndpoint( emptySet(), - "/example", + "/example", PathMatchingType.PATH, setOf("GET", "POST"), setOf(ClientWithSelector.create("client1"), ClientWithSelector.create("client2")), Incoming.UnlistedPolicy.LOG ) - ), - unlistedEndpointsPolicy = Incoming.UnlistedPolicy.LOG + ), unlistedEndpointsPolicy = Incoming.UnlistedPolicy.LOG ) // when @@ -259,28 +237,23 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { @Test fun `should generate RBAC rules for incoming permissions with block unlisted endpoints and log clients`() { // given - 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 policyName = "path:/example" val expectedShadowRules = expectedSimpleEndpointPermissionsJson(policyName) val expectedRbacBuilder = getRBACFilterWithShadowRules( - expectedUnlistedClientsPermissions, - expectedShadowRules + expectedUnlistedClientsPermissions, expectedShadowRules ) val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf( - IncomingEndpoint( - emptySet(), - "/example", - PathMatchingType.PATH, - setOf("GET", "POST"), - setOf(ClientWithSelector.create("client1"), ClientWithSelector.create("client2")), - Incoming.UnlistedPolicy.LOG - ) - ), - unlistedEndpointsPolicy = Incoming.UnlistedPolicy.BLOCKANDLOG + permissionsEnabled = true, endpoints = listOf( + IncomingEndpoint( + emptySet(), + "/example", + PathMatchingType.PATH, + setOf("GET", "POST"), + setOf(ClientWithSelector.create("client1"), ClientWithSelector.create("client2")), + Incoming.UnlistedPolicy.LOG + ) + ), unlistedEndpointsPolicy = Incoming.UnlistedPolicy.BLOCKANDLOG ) // when @@ -293,28 +266,23 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { @Test fun `should generate RBAC rules for incoming permissions with log unlisted endpoints and block clients`() { // given - 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 policyName = "path:/example" val expectedShadowRules = expectedSimpleEndpointPermissionsJson(policyName) val expectedRbacBuilder = getRBACFilterWithShadowRules( - expectedEndpointPermissionsLogUnlistedEndpointsAndBlockUnlistedClients, - expectedShadowRules + expectedEndpointPermissionsLogUnlistedEndpointsAndBlockUnlistedClients, expectedShadowRules ) val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf( - IncomingEndpoint( - emptySet(), - "/example", - PathMatchingType.PATH, - setOf("GET", "POST"), - setOf(ClientWithSelector.create("client1"), ClientWithSelector.create("client2")), - Incoming.UnlistedPolicy.BLOCKANDLOG - ) - ), - unlistedEndpointsPolicy = Incoming.UnlistedPolicy.LOG + permissionsEnabled = true, endpoints = listOf( + IncomingEndpoint( + emptySet(), + "/example", + PathMatchingType.PATH, + setOf("GET", "POST"), + setOf(ClientWithSelector.create("client1"), ClientWithSelector.create("client2")), + Incoming.UnlistedPolicy.BLOCKANDLOG + ) + ), unlistedEndpointsPolicy = Incoming.UnlistedPolicy.LOG ) // when @@ -327,18 +295,22 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { @Test fun `should generate RBAC rules for incoming permissions with roles`() { // given - 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 policyName = "path:/example" val expectedRbacBuilder = getRBACFilter(expectedSimpleEndpointPermissionsJson(policyName)) val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf(IncomingEndpoint( - emptySet(), - "/example", - PathMatchingType.PATH, - setOf("GET", "POST"), - setOf(ClientWithSelector.create("role-1")) - )), roles = listOf(Role("role-1", setOf(ClientWithSelector.create("client1"), ClientWithSelector.create("client2")))) + permissionsEnabled = true, endpoints = listOf( + IncomingEndpoint( + emptySet(), + "/example", + PathMatchingType.PATH, + setOf("GET", "POST"), + setOf(ClientWithSelector.create("role-1")) + ) + ), roles = listOf( + Role( + "role-1", setOf(ClientWithSelector.create("client1"), ClientWithSelector.create("client2")) + ) + ) ) // when @@ -353,25 +325,26 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { // given val expectedRbacBuilder = getRBACFilter(expectedDuplicatedRole) val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf(IncomingEndpoint( - emptySet(), - "/example", - PathMatchingType.PATH, - setOf("GET", "POST"), - setOf( - ClientWithSelector.create("client1"), - ClientWithSelector.create("client1"), - ClientWithSelector.create("client1", "selector"), - ClientWithSelector.create("client1-duplicated", "selector"), - ClientWithSelector.create("client1-duplicated"), - ClientWithSelector.create("role-1") - ) - )), roles = listOf(Role("role-1", setOf( + permissionsEnabled = true, endpoints = listOf( + IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf("GET", "POST"), setOf( + ClientWithSelector.create("client1"), + ClientWithSelector.create("client1"), + ClientWithSelector.create("client1", "selector"), + ClientWithSelector.create("client1-duplicated", "selector"), ClientWithSelector.create("client1-duplicated"), + ClientWithSelector.create("role-1") + ) + ) + ), roles = listOf( + Role( + "role-1", setOf( ClientWithSelector.create("client1-duplicated"), - ClientWithSelector.create("client2")) - )) + ClientWithSelector.create("client1-duplicated"), + ClientWithSelector.create("client2") + ) + ) + ) ) // when @@ -386,20 +359,21 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { // given val expectedRbacBuilder = getRBACFilter(expectedEndpointPermissionsWithDifferentRulesForDifferentClientsJson) val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf(IncomingEndpoint( - emptySet(), - "/example", - PathMatchingType.PATH, - setOf("GET"), - setOf(ClientWithSelector.create("client1")) + permissionsEnabled = true, endpoints = listOf( + IncomingEndpoint( + emptySet(), + "/example", + PathMatchingType.PATH, + setOf("GET"), + setOf(ClientWithSelector.create("client1")) ), IncomingEndpoint( - emptySet(), - "/example2", - PathMatchingType.PATH, - setOf("POST"), - setOf(ClientWithSelector.create("client2")) - )) + emptySet(), + "/example2", + PathMatchingType.PATH, + setOf("POST"), + setOf(ClientWithSelector.create("client2")) + ) + ) ) // when @@ -413,24 +387,31 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { fun `should generate minimal RBAC rules for incoming permissions with roles and clients`() { // given val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf(IncomingEndpoint( - emptySet(), - "/example", - PathMatchingType.PATH, - setOf("GET", "POST"), - setOf(ClientWithSelector.create("role-1")) + 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"), - setOf(ClientWithSelector.create("client2"), ClientWithSelector.create("client1")) - )), roles = listOf(Role("role-1", setOf(ClientWithSelector.create("client1"), ClientWithSelector.create("client2")))) + emptySet(), + "/example2", + PathMatchingType.PATH, + setOf("GET", "POST"), + setOf(ClientWithSelector.create("client2"), ClientWithSelector.create("client1")) + ) + ), roles = listOf( + Role( + "role-1", setOf(ClientWithSelector.create("client1"), ClientWithSelector.create("client2")) + ) + ) + ) + val expectedRbacBuilder = getRBACFilter( + expectedTwoClientsSimpleEndpointPermissionsJson( + incomingPermission.endpoints[0].extractRule(), incomingPermission.endpoints[1].extractRule() + ) ) - val expectedRbacBuilder = getRBACFilter(expectedTwoClientsSimpleEndpointPermissionsJson( - "${incomingPermission.endpoints[0]}", "${incomingPermission.endpoints[1]}" - )) // when val generated = rbacFilterFactory.createHttpFilter(createGroup(incomingPermission), snapshot) @@ -443,24 +424,30 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { fun `should generate minimal RBAC rules for incoming permissions with roles and single client`() { // given val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf(IncomingEndpoint( - emptySet(), - "/example", - PathMatchingType.PATH, - setOf("GET", "POST"), - setOf(ClientWithSelector.create("client2"), ClientWithSelector.create("role-1")) + 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"), - setOf(ClientWithSelector.create("role-2"), ClientWithSelector.create("client1")) - )), roles = listOf(Role("role-1", setOf(ClientWithSelector.create("client1"))), Role("role-2", setOf(ClientWithSelector.create("client2")))) + emptySet(), + "/example2", + PathMatchingType.PATH, + setOf("GET", "POST"), + setOf(ClientWithSelector.create("role-2"), ClientWithSelector.create("client1")) + ) + ), roles = listOf( + Role("role-1", setOf(ClientWithSelector.create("client1"))), + Role("role-2", setOf(ClientWithSelector.create("client2"))) + ) + ) + val expectedRbacBuilder = getRBACFilter( + expectedTwoClientsSimpleEndpointPermissionsJson( + incomingPermission.endpoints[0].extractRule(), incomingPermission.endpoints[1].extractRule() + ) ) - val expectedRbacBuilder = getRBACFilter(expectedTwoClientsSimpleEndpointPermissionsJson( - "${incomingPermission.endpoints[0]}", "${incomingPermission.endpoints[1]}" - )) // when val generated = rbacFilterFactory.createHttpFilter(createGroup(incomingPermission), snapshot) @@ -474,18 +461,20 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { // given val expectedRbacBuilder = getRBACFilter(expectedSourceIpAuthPermissionsJson) val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf(IncomingEndpoint( - emptySet(), - "/example", - PathMatchingType.PATH, - setOf("GET", "POST"), - setOf(ClientWithSelector.create("client1"), ClientWithSelector.create("client2")) - )) + permissionsEnabled = true, endpoints = listOf( + IncomingEndpoint( + emptySet(), + "/example", + PathMatchingType.PATH, + setOf("GET", "POST"), + setOf(ClientWithSelector.create("client1"), ClientWithSelector.create("client2")) + ) + ) ) // when - val generated = rbacFilterFactoryWithSourceIpAuth.createHttpFilter(createGroup(incomingPermission), snapshotForSourceIpAuth) + val generated = + rbacFilterFactoryWithSourceIpAuth.createHttpFilter(createGroup(incomingPermission), snapshotForSourceIpAuth) // then assertThat(generated).isEqualTo(expectedRbacBuilder) @@ -495,15 +484,13 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { fun `should generate RBAC rules for incoming permissions without clients`() { // given val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf(IncomingEndpoint( - emptySet(), - "/example", - PathMatchingType.PATH, - setOf() - )) + permissionsEnabled = true, endpoints = listOf( + IncomingEndpoint( + emptySet(), "/example", PathMatchingType.PATH, setOf() + ) + ) ) - val expectedPolicies = expectedDenyForAllEndpointPermissions(policyName = "${incomingPermission.endpoints[0]}") + val expectedPolicies = expectedDenyForAllEndpointPermissions(policyName = incomingPermission.endpoints[0].extractRule()) val expectedRbacBuilder = getRBACFilter(expectedPolicies) // when @@ -519,7 +506,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val expectedActual = """ { "policies": { - "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[], clients=[], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "path:/example": { "permissions": [{ "and_rules": { "rules": [ ${pathRule("/example")} ] @@ -546,16 +533,14 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { """ val incomingPermissions = Incoming( - permissionsEnabled = true, - unlistedEndpointsPolicy = Incoming.UnlistedPolicy.LOG, - endpoints = listOf(IncomingEndpoint( - path = "/example", - clients = setOf(), - unlistedClientsPolicy = Incoming.UnlistedPolicy.BLOCKANDLOG - )) + permissionsEnabled = true, unlistedEndpointsPolicy = Incoming.UnlistedPolicy.LOG, endpoints = listOf( + IncomingEndpoint( + path = "/example", clients = setOf(), unlistedClientsPolicy = Incoming.UnlistedPolicy.BLOCKANDLOG + ) + ) ) - val expectedShadow = expectedDenyForAllEndpointPermissions(policyName = "${incomingPermissions.endpoints[0]}") + val expectedShadow = expectedDenyForAllEndpointPermissions(policyName = incomingPermissions.endpoints[0].extractRule()) val expectedRbacBuilder = getRBACFilterWithShadowRules(expectedActual, expectedShadow) // when @@ -590,14 +575,14 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { val incomingPermissions = Incoming( permissionsEnabled = true, unlistedEndpointsPolicy = Incoming.UnlistedPolicy.BLOCKANDLOG, - endpoints = listOf(IncomingEndpoint( - path = "/example", - clients = setOf(), - unlistedClientsPolicy = Incoming.UnlistedPolicy.LOG - )) + endpoints = listOf( + IncomingEndpoint( + path = "/example", clients = setOf(), unlistedClientsPolicy = Incoming.UnlistedPolicy.LOG + ) + ) ) - val expectedShadow = expectedDenyForAllEndpointPermissions(policyName = "${incomingPermissions.endpoints[0]}") + val expectedShadow = expectedDenyForAllEndpointPermissions(policyName = incomingPermissions.endpoints[0].extractRule()) val expectedRbacBuilder = getRBACFilterWithShadowRules(expectedActual, expectedShadow) // when @@ -612,14 +597,15 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { // given val expectedRbacBuilder = getRBACFilter(expectedSourceIpAuthWithStaticRangeJson) val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf(IncomingEndpoint( - emptySet(), - "/example", - PathMatchingType.PATH, - setOf("GET"), - setOf(ClientWithSelector.create("client1")) - )) + permissionsEnabled = true, endpoints = listOf( + IncomingEndpoint( + emptySet(), + "/example", + PathMatchingType.PATH, + setOf("GET"), + setOf(ClientWithSelector.create("client1")) + ) + ) ) // when @@ -634,20 +620,20 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { // given val expectedRbacBuilder = getRBACFilter(expectedSourceIpAuthWithStaticRangeAndSourceIpJson) val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf(IncomingEndpoint( - emptySet(), - "/example", - PathMatchingType.PATH, - setOf("GET"), - setOf(ClientWithSelector.create("client1"), ClientWithSelector.create("client2")) - )) + permissionsEnabled = true, endpoints = listOf( + IncomingEndpoint( + emptySet(), + "/example", + PathMatchingType.PATH, + setOf("GET"), + setOf(ClientWithSelector.create("client1"), ClientWithSelector.create("client2")) + ) + ) ) // when val generated = rbacFilterFactoryWithStaticRangeAndSourceIpAuth.createHttpFilter( - createGroup(incomingPermission), - snapshotForSourceIpAuth + createGroup(incomingPermission), snapshotForSourceIpAuth ) // then @@ -659,20 +645,20 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { // given val expectedRbacBuilder = getRBACFilter(expectedSourceIpWithSelectorAuthPermissionsJson) val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf(IncomingEndpoint( - emptySet(), - "/example", - PathMatchingType.PATH, - setOf("GET"), - setOf(ClientWithSelector.create("client2", "selector")) - )) + permissionsEnabled = true, endpoints = listOf( + IncomingEndpoint( + emptySet(), + "/example", + PathMatchingType.PATH, + setOf("GET"), + setOf(ClientWithSelector.create("client2", "selector")) + ) + ) ) // when val generated = rbacFilterFactoryWithSourceIpWithSelectorAuth.createHttpFilter( - createGroup(incomingPermission), - snapshotForSourceIpAuth + createGroup(incomingPermission), snapshotForSourceIpAuth ) // then @@ -684,20 +670,20 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { // given val expectedRbacBuilder = getRBACFilter(expectedSourceIpFromDiscoveryWithSelectorAuthPermissionsJson) val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf(IncomingEndpoint( - emptySet(), - "/example", - PathMatchingType.PATH, - setOf("GET"), - setOf(ClientWithSelector.create("client1", "selector")) - )) + permissionsEnabled = true, endpoints = listOf( + IncomingEndpoint( + emptySet(), + "/example", + PathMatchingType.PATH, + setOf("GET"), + setOf(ClientWithSelector.create("client1", "selector")) + ) + ) ) // when val generated = rbacFilterFactoryWithSourceIpWithSelectorAuth.createHttpFilter( - createGroup(incomingPermission), - snapshotForSourceIpAuth + createGroup(incomingPermission), snapshotForSourceIpAuth ) // then @@ -709,24 +695,27 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { // given val expectedRbacBuilder = getRBACFilter(expectedSourceIpWithStaticRangeAndSelectorAuthPermissionsAndRolesJson) val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf(IncomingEndpoint( - emptySet(), - "/example", - PathMatchingType.PATH, - setOf("GET"), - setOf(ClientWithSelector.create("role1")) - )), - roles = listOf(Role("role1", setOf( + permissionsEnabled = true, endpoints = listOf( + IncomingEndpoint( + emptySet(), + "/example", + PathMatchingType.PATH, + setOf("GET"), + setOf(ClientWithSelector.create("role1")) + ) + ), roles = listOf( + Role( + "role1", setOf( ClientWithSelector.create("client1", "selector1"), - ClientWithSelector.create("client2", "selector2")) - )) + ClientWithSelector.create("client2", "selector2") + ) + ) + ) ) // when val generated = rbacFilterFactoryWithSourceIpWithSelectorAuth.createHttpFilter( - createGroup(incomingPermission), - snapshotForSourceIpAuth + createGroup(incomingPermission), snapshotForSourceIpAuth ) // then @@ -736,22 +725,23 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { @Test fun `should generate RBAC rules for incoming permissions with client allowed to all endpoints`() { // given - val expectedRbacBuilder = getRBACFilterWithShadowRules(expectedRulesForAllowedClient, expectedShadowRulesForAllowedClient) + val expectedRbacBuilder = + getRBACFilterWithShadowRules(expectedRulesForAllowedClient, expectedShadowRulesForAllowedClient) val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf(IncomingEndpoint( - emptySet(), - "/example", - PathMatchingType.PATH, - setOf("GET"), - setOf(ClientWithSelector.create("client1")) - )) + permissionsEnabled = true, endpoints = listOf( + IncomingEndpoint( + emptySet(), + "/example", + PathMatchingType.PATH, + setOf("GET"), + setOf(ClientWithSelector.create("client1")) + ) + ) ) // when val generated = rbacFilterFactoryWithAllowAllEndpointsForClient.createHttpFilter( - createGroup(incomingPermission), - snapshotForSourceIpAuth + createGroup(incomingPermission), snapshotForSourceIpAuth ) // then @@ -762,21 +752,26 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { fun `should generate RBAC rules for incoming permissions with default client list`() { // given val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf(IncomingEndpoint( - emptySet(), - "/default", - PathMatchingType.PATH, - setOf("GET"), - setOf(ClientWithSelector.create("client1")) - )) + permissionsEnabled = true, endpoints = listOf( + IncomingEndpoint( + emptySet(), + "/default", + PathMatchingType.PATH, + setOf("GET"), + setOf(ClientWithSelector.create("client1")) + ) + ) ) - val rules = expectedPoliciesForDefaultAndCustomLists(listOf("client1"), listOf("client1", "default-client", "xyz"), "/default") + val rules = expectedPoliciesForDefaultAndCustomLists( + listOf("client1", "default-client", "xyz"), "/default" + ) val expectedDefaultRbacBuilder = getRBACFilterWithShadowRules(rules, rules) // when - val generated = rbacFilterFactoryWithDefaultAndCustomClientsLists.createHttpFilter(createGroup(incomingPermission), snapshot) + val generated = rbacFilterFactoryWithDefaultAndCustomClientsLists.createHttpFilter( + createGroup(incomingPermission), snapshot + ) // then assertThat(generated).isEqualTo(expectedDefaultRbacBuilder) @@ -786,21 +781,26 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { fun `should generate RBAC rules for incoming permissions with custom client list`() { // given val incomingPermission = Incoming( - permissionsEnabled = true, - endpoints = listOf(IncomingEndpoint( - emptySet(), - "/custom", - PathMatchingType.PATH, - setOf("GET"), - setOf(ClientWithSelector.create("client1"), ClientWithSelector.create("custom1")) - )) + permissionsEnabled = true, endpoints = listOf( + IncomingEndpoint( + emptySet(), + "/custom", + PathMatchingType.PATH, + setOf("GET"), + setOf(ClientWithSelector.create("client1"), ClientWithSelector.create("custom1")) + ) + ) ) - val rules = expectedPoliciesForDefaultAndCustomLists(listOf("client1", "custom1"), listOf("client1", "custom1-client", "xyz"), "/custom") + val rules = expectedPoliciesForDefaultAndCustomLists( + listOf("client1", "custom1-client", "xyz"), "/custom" + ) val expectedDefaultRbacBuilder = getRBACFilterWithShadowRules(rules, rules) // when - val generated = rbacFilterFactoryWithDefaultAndCustomClientsLists.createHttpFilter(createGroup(incomingPermission), snapshot) + val generated = rbacFilterFactoryWithDefaultAndCustomClientsLists.createHttpFilter( + createGroup(incomingPermission), snapshot + ) // then assertThat(generated).isEqualTo(expectedDefaultRbacBuilder) @@ -809,7 +809,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedEndpointPermissionsWithDifferentRulesForDifferentClientsJson = """ { "policies": { - "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client1, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "path:/example": { "permissions": [ { "and_rules": { @@ -829,7 +829,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { ${originalAndAuthenticatedPrincipal("client1")} ] }, - "IncomingEndpoint(paths=[], path=/example2, pathMatchingType=PATH, methods=[POST], clients=[ClientWithSelector(name=client2, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "path:/example2": { "permissions": [ { "and_rules": { @@ -856,7 +856,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedSourceIpFromDiscoveryWithSelectorAuthPermissionsJson = """ { "policies": { - "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client1, selector=selector, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "path:/example": { "permissions": [ { "and_rules": { @@ -891,7 +891,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedSourceIpWithSelectorAuthPermissionsJson = """ { "policies": { - "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client2, selector=selector, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "path:/example": { "permissions": [ { "and_rules": { @@ -926,7 +926,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedSourceIpWithStaticRangeAndSelectorAuthPermissionsAndRolesJson = """ { "policies": { - "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=role1, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "path:/example": { "permissions": [ { "and_rules": { @@ -970,7 +970,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedSourceIpAuthPermissionsJson = """ { "policies": { - "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)": { + "path:/example": { "permissions": [ { "and_rules": { @@ -1029,7 +1029,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { { "policies": { """ /* notice that duplicated clients occurs only once here */ + """ - "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)": { + "path:/example": { "permissions": [ { "and_rules": { @@ -1249,7 +1249,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedSourceIpAuthWithStaticRangeJson = """ { "policies": { - "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client1, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "path:/example": { "permissions": [ { "and_rules": { @@ -1266,7 +1266,11 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { } } ], "principals": [ - { "orIds": { "ids": [${principalSourceIp("192.168.1.0", 24)}, ${principalSourceIp("192.168.2.0", 28)}] } } + { "orIds": { "ids": [${principalSourceIp("192.168.1.0", 24)}, ${ + principalSourceIp( + "192.168.2.0", 28 + ) + }] } } ] } } @@ -1276,7 +1280,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedSourceIpAuthWithStaticRangeAndSourceIpJson = """ { "policies": { - "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)": { + "path:/example": { "permissions": [ { "and_rules": { @@ -1313,7 +1317,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private val expectedEndpointPermissionsLogUnlistedEndpointsAndBlockUnlistedClients = """ { "policies": { - "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)": { + "path:/example": { "permissions": [ { "and_rules": { @@ -1369,7 +1373,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { private fun expectedPoliciesForAllowedClient(principals: String) = """ { "policies": { - "IncomingEndpoint(paths=[], path=/example, pathMatchingType=PATH, methods=[GET], clients=[ClientWithSelector(name=client1, selector=null, negated=false)], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "path:/example": { "permissions": [ { "and_rules": { @@ -1391,10 +1395,12 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { } """ - private fun expectedPoliciesForDefaultAndCustomLists(clients: List, principals: List, path: String) = """ + private fun expectedPoliciesForDefaultAndCustomLists( + principals: List, path: String + ) = """ { "policies": { - "IncomingEndpoint(paths=[], path=$path, pathMatchingType=PATH, methods=[GET], clients=[${clients.joinToString(", ") { "ClientWithSelector(name=$it, selector=null, negated=false)" }}], unlistedClientsPolicy=BLOCKANDLOG, oauth=null)": { + "path:$path": { "permissions": [ { "and_rules": { @@ -1415,6 +1421,7 @@ internal class RBACFilterFactoryTest : RBACFilterFactoryTestUtils { } } """ + private val expectedRulesForAllowedClient = expectedPoliciesForAllowedClient( "${originalAndAuthenticatedPrincipal("client1")}, ${originalAndAuthenticatedPrincipal("allowed-client")}" ) diff --git a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/rbac/RBACFilterFactoryTestUtils.kt b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/rbac/RBACFilterFactoryTestUtils.kt index 3b84eca33..c3caac8f3 100644 --- a/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/rbac/RBACFilterFactoryTestUtils.kt +++ b/envoy-control-core/src/test/kotlin/pl/allegro/tech/servicemesh/envoycontrol/snapshot/resource/listeners/filters/rbac/RBACFilterFactoryTestUtils.kt @@ -67,6 +67,7 @@ interface RBACFilterFactoryTestUtils { } """ } + fun authenticatedPrincipal(value: String): String { return """{ "authenticated": { @@ -76,6 +77,7 @@ interface RBACFilterFactoryTestUtils { } }""" } + fun originalDestinationPrincipal(value: String): String { return """{ "and_ids": { @@ -119,7 +121,7 @@ interface RBACFilterFactoryTestUtils { fun getRBACFilterWithShadowRules(rules: String, shadowRules: String): HttpFilter { val rbacFilter = RBAC.newBuilder() - JsonFormat.parser().merge(wrapInFilter(rules), rbacFilter) + JsonFormat.parser().merge(wrapInFilter(rules), rbacFilter) JsonFormat.parser().merge(wrapInFilterShadow(shadowRules), rbacFilter) return HttpFilter.newBuilder() .setName("envoy.filters.http.rbac") diff --git a/envoy-control-tests/src/main/resources/lua_spec/ingress_rbac_logging_spec.lua b/envoy-control-tests/src/main/resources/lua_spec/ingress_rbac_logging_spec.lua index 0e51552d0..f0edc7daa 100644 --- a/envoy-control-tests/src/main/resources/lua_spec/ingress_rbac_logging_spec.lua +++ b/envoy-control-tests/src/main/resources/lua_spec/ingress_rbac_logging_spec.lua @@ -4,8 +4,9 @@ local _ = match._ local contains = function(substring) return match.matches(substring, nil, true) end -local function formatLog(method, path, source_ip, client_name, protocol, request_id, status_code, trusted_client, allowed_client, rbac_action, authority, lua_authority, jwt_token_status, headers_to_log) +local function formatLog(method, rule, path, source_ip, client_name, protocol, request_id, status_code, trusted_client, allowed_client, rbac_action, authority, lua_authority, jwt_token_status, headers_to_log) local message = "\nINCOMING_PERMISSIONS {\"method\":\"" .. method .. + "\",\"rule\":\"" .. rule .. "\",\"path\":\"" .. path .. "\",\"clientIp\":\"" .. source_ip .. "\",\"clientName\":\"" .. escape(client_name) .. @@ -397,7 +398,8 @@ describe("envoy_on_response:", function() } }, ['envoy.filters.http.rbac'] = { - ['shadow_engine_result'] = 'denied' + ['shadow_engine_result'] = 'denied', + ['shadow_effective_policy_id'] = 'pathPrefix:/path' }, ['envoy.filters.http.lua'] = { ['service_name'] = "service", @@ -425,6 +427,7 @@ describe("envoy_on_response:", function() -- then assert.spy(handle.logInfo).was_called_with(_, formatLog( "POST", + "pathPrefix:/path", "/path?query=val", "127.1.1.3", "service-first", @@ -452,6 +455,7 @@ describe("envoy_on_response:", function() -- then assert.spy(handle.logInfo).was_called_with(_, formatLog( "POST", + "pathPrefix:/path", "/path?query=val", "127.1.1.3", "service-first", @@ -479,6 +483,7 @@ describe("envoy_on_response:", function() -- then assert.spy(handle.logInfo).was_called_with(_, formatLog( "POST", + "pathPrefix:/path", "/path?query=val", "127.1.1.3", "service-first", @@ -507,6 +512,7 @@ describe("envoy_on_response:", function() -- then assert.spy(handle.logInfo).was_called_with(_, formatLog( "POST", + "pathPrefix:/path", "/path?query=val", "127.1.1.3", "service-first", @@ -535,6 +541,7 @@ describe("envoy_on_response:", function() -- then assert.spy(handle.logInfo).was_called_with(_, formatLog( "", + "pathPrefix:/path", "", "", "", @@ -563,6 +570,7 @@ describe("envoy_on_response:", function() -- then assert.spy(handle.logInfo).was_called_with(_, formatLog( "", + "pathPrefix:/path", "", "", "", @@ -590,6 +598,7 @@ describe("envoy_on_response:", function() -- then assert.spy(handle.logInfo).was_called_with(_, formatLog( "POST", + "pathPrefix:/path", "", "127.1.1.3", "service-first", @@ -618,6 +627,7 @@ describe("envoy_on_response:", function() -- then assert.spy(handle.logInfo).was_called_with(_, formatLog( "POST", + "pathPrefix:/path", "/path?query=val", "127.1.1.3", "service-first", @@ -651,6 +661,7 @@ describe("envoy_on_response:", function() -- then assert.spy(handle.logInfo).was_called_with(_, formatLog( "POST", + "pathPrefix:/path", "/path?query=val", "127.1.1.3", "service-first",