-
Notifications
You must be signed in to change notification settings - Fork 870
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Capture enduser attributes in Spring Security
Adds library and javaagent instrumentation for spring-security-config to capture `enduser.*` semantic attributes. The library instrumentation provides: * a Servlet `Filter` and a WebFlux `WebFilter` to capture `enduser.*` semantic attributes from Spring Security `Authentication` objects. * `Customizer` implementations to insert those filters into the security filter chains created by `HttpSecurity` and `ServerHttpSecurity`, respectively. The javaagent instrumentation applies the `Customizer` implementations in the `build()` methods of `HttpSecurity` and `ServerHttpSecurity`. The automatic instrumentation is disabled by default, due to the following requirement in the specification: > Given the sensitive nature of this information, SDKs and exporters > SHOULD drop these attributes by default and then provide a > configuration parameter to turn on retention for use cases where the > information is required and would not violate any policies or > regulations. Since this requirement is common to any automatic instrumentation that captures `enduser.*` attributes, the following new common configuration properties are introduced: * `otel.instrumentation.common.enduser.enabled` - default false. Whether to capture `enduser.*` semantic attributes. Must be set to true to capture any `enduser.*` attributes. * `otel.instrumentation.common.enduser.id.enabled` - default true. Whether to capture `enduser.id` semantic attribute. Only takes effect if `otel.instrumentation.common.enduser.enabled` is true. * `otel.instrumentation.common.enduser.role.enabled` - default true. Whether to capture `enduser.role` semantic attribute. Only takes effect if `otel.instrumentation.common.enduser.enabled` is true. * `otel.instrumentation.common.enduser.scope.enabled` - default true. Whether to capture `enduser.scope` semantic attribute. Only takes effect if `otel.instrumentation.common.enduser.enabled` is true. In addition, the following new spring-security specific configuration properties are introduced: * `otel.instrumentation.spring-security.enduser.role.granted-authority-prefix` default `ROLE_`. Prefix of granted authorities identifying roles to capture in the `enduser.role` semantic attribute. * `otel.instrumentation.spring-security.enduser.scope.granted-authority-prefix` default `SCOPE_` Prefix of granted authorities identifying scopes to capture in the `enduser.scopes` semantic attribute.
- Loading branch information
Showing
26 changed files
with
1,412 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
instrumentation/spring/spring-security-config-6.0/javaagent/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# OpenTelemetry Javaagent Instrumentation: Spring Security Config | ||
|
||
Javaagent automatic instrumentation to capture `enduser.*` semantic attributes | ||
from Spring Security `Authentication` objects. | ||
|
||
## Settings | ||
|
||
| Property | Type | Default | Description | | ||
|-------------------------------------------------------------------------------|---------|----------|-------------------------------------------------------------------------------------------------------------------------------------| | ||
| `otel.instrumentation.common.enduser.enabled` | Boolean | `false` | Whether to capture `enduser.*` semantic attributes. Must be set to true to capture any `enduser.*` attributes. | | ||
| `otel.instrumentation.common.enduser.id.enabled` | Boolean | `true` | Whether to capture `enduser.id` semantic attribute. Only takes effect if `otel.instrumentation.common.enduser.enabled` is true. | | ||
| `otel.instrumentation.common.enduser.role.enabled` | Boolean | `true` | Whether to capture `enduser.role` semantic attribute. Only takes effect if `otel.instrumentation.common.enduser.enabled` is true. | | ||
| `otel.instrumentation.common.enduser.scope.enabled` | Boolean | `true` | Whether to capture `enduser.scope` semantic attribute. Only takes effect if `otel.instrumentation.common.enduser.enabled` is true. | | ||
| `otel.instrumentation.spring-security.enduser.role.granted-authority-prefix` | String | `ROLE_` | Prefix of granted authorities identifying roles to capture in the `enduser.role` semantic attribute. | | ||
| `otel.instrumentation.spring-security.enduser.scope.granted-authority-prefix` | String | `SCOPE_` | Prefix of granted authorities identifying scopes to capture in the `enduser.scopes` semantic attribute. | |
39 changes: 39 additions & 0 deletions
39
instrumentation/spring/spring-security-config-6.0/javaagent/build.gradle.kts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
plugins { | ||
id("otel.javaagent-instrumentation") | ||
} | ||
|
||
muzzle { | ||
pass { | ||
group.set("org.springframework.security") | ||
module.set("spring-security-config") | ||
versions.set("[6.0.0,]") | ||
|
||
extraDependency("jakarta.servlet:jakarta.servlet-api:6.0.0") | ||
extraDependency("org.springframework.security:spring-security-web:6.0.0") | ||
extraDependency("io.projectreactor:reactor-core:3.5.0") | ||
} | ||
} | ||
|
||
dependencies { | ||
bootstrap(project(":instrumentation:executors:bootstrap")) | ||
|
||
implementation(project(":instrumentation:spring:spring-security-config-6.0:library")) | ||
implementation("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api") | ||
|
||
library("org.springframework.security:spring-security-config:6.0.0") | ||
library("org.springframework.security:spring-security-web:6.0.0") | ||
library("io.projectreactor:reactor-core:3.5.0") | ||
|
||
testImplementation("org.springframework:spring-test:6.0.0") | ||
testImplementation("jakarta.servlet:jakarta.servlet-api:6.0.0") | ||
} | ||
|
||
otelJava { | ||
minJavaVersionSupported.set(JavaVersion.VERSION_17) | ||
} | ||
|
||
tasks { | ||
test { | ||
systemProperty("otel.instrumentation.common.enduser.enabled", "true") | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
...gent/instrumentation/spring/security/config/v6_0/EnduserAttributesCapturerSingletons.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.spring.security.config.v6_0; | ||
|
||
import io.opentelemetry.instrumentation.spring.security.config.v6_0.EnduserAttributesCapturer; | ||
import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; | ||
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; | ||
|
||
public class EnduserAttributesCapturerSingletons { | ||
|
||
private static final EnduserAttributesCapturer ENDUSER_ATTRIBUTES_CAPTURER = | ||
createEndUserAttributesCapturerFromConfig(); | ||
|
||
private EnduserAttributesCapturerSingletons() {} | ||
|
||
public static EnduserAttributesCapturer enduserAttributesCapturer() { | ||
return ENDUSER_ATTRIBUTES_CAPTURER; | ||
} | ||
|
||
private static EnduserAttributesCapturer createEndUserAttributesCapturerFromConfig() { | ||
EnduserAttributesCapturer capturer = new EnduserAttributesCapturer(); | ||
capturer.setEnduserIdEnabled(CommonConfig.get().getEnduserConfig().isIdEnabled()); | ||
capturer.setEnduserRoleEnabled(CommonConfig.get().getEnduserConfig().isRoleEnabled()); | ||
capturer.setEnduserScopeEnabled(CommonConfig.get().getEnduserConfig().isScopeEnabled()); | ||
|
||
String rolePrefix = | ||
InstrumentationConfig.get() | ||
.getString( | ||
"otel.instrumentation.spring-security.enduser.role.granted-authority-prefix"); | ||
if (rolePrefix != null) { | ||
capturer.setRoleGrantedAuthorityPrefix(rolePrefix); | ||
} | ||
|
||
String scopePrefix = | ||
InstrumentationConfig.get() | ||
.getString( | ||
"otel.instrumentation.spring-security.enduser.scope.granted-authority-prefix"); | ||
if (scopePrefix != null) { | ||
capturer.setScopeGrantedAuthorityPrefix(rolePrefix); | ||
} | ||
return capturer; | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
...gent/instrumentation/spring/security/config/v6_0/servlet/HttpSecurityInstrumentation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.spring.security.config.v6_0.servlet; | ||
|
||
import static io.opentelemetry.javaagent.instrumentation.spring.security.config.v6_0.EnduserAttributesCapturerSingletons.enduserAttributesCapturer; | ||
import static net.bytebuddy.matcher.ElementMatchers.isMethod; | ||
import static net.bytebuddy.matcher.ElementMatchers.isProtected; | ||
import static net.bytebuddy.matcher.ElementMatchers.named; | ||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; | ||
|
||
import io.opentelemetry.instrumentation.spring.security.config.v6_0.servlet.EnduserAttributesHttpSecurityCustomizer; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; | ||
import net.bytebuddy.asm.Advice; | ||
import net.bytebuddy.description.type.TypeDescription; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
|
||
/** Instrumentation for {@link HttpSecurity}. */ | ||
public class HttpSecurityInstrumentation implements TypeInstrumentation { | ||
|
||
@Override | ||
public ElementMatcher<TypeDescription> typeMatcher() { | ||
return named("org.springframework.security.config.annotation.web.builders.HttpSecurity"); | ||
} | ||
|
||
@Override | ||
public void transform(TypeTransformer transformer) { | ||
transformer.applyAdviceToMethod( | ||
isMethod().and(isProtected()).and(named("performBuild")).and(takesArguments(0)), | ||
getClass().getName() + "$PerformBuildAdvice"); | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
public static class PerformBuildAdvice { | ||
|
||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
public static void onEnter(@Advice.This HttpSecurity httpSecurity) { | ||
new EnduserAttributesHttpSecurityCustomizer(enduserAttributesCapturer()) | ||
.customize(httpSecurity); | ||
} | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
...spring/security/config/v6_0/servlet/SpringSecurityConfigServletInstrumentationModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.spring.security.config.v6_0.servlet; | ||
|
||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; | ||
import static java.util.Collections.singletonList; | ||
|
||
import com.google.auto.service.AutoService; | ||
import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; | ||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; | ||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; | ||
import java.util.List; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
|
||
/** Instrumentation module for servlet-based applications that use spring-security-config. */ | ||
@AutoService(InstrumentationModule.class) | ||
public class SpringSecurityConfigServletInstrumentationModule extends InstrumentationModule { | ||
public SpringSecurityConfigServletInstrumentationModule() { | ||
super("spring-security-config-servlet", "spring-security-config-servlet-6.0"); | ||
} | ||
|
||
@Override | ||
public boolean defaultEnabled(ConfigProperties config) { | ||
/* | ||
* Since the only thing this module currently does is capture enduser attributes, | ||
* the module can be completely disabled if enduser attributes are disabled. | ||
* | ||
* If any functionality not related to enduser attributes is added to this module, | ||
* then this check will need to move elsewhere to only guard the enduser attributes logic. | ||
*/ | ||
return CommonConfig.get().getEnduserConfig().isEnabled(); | ||
} | ||
|
||
@Override | ||
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() { | ||
return hasClassesNamed( | ||
"org.springframework.security.config.annotation.web.builders.HttpSecurity") | ||
.and( | ||
hasClassesNamed( | ||
"org.springframework.security.web.access.intercept.AuthorizationFilter")) | ||
.and(hasClassesNamed("jakarta.servlet.Servlet")); | ||
} | ||
|
||
@Override | ||
public List<TypeInstrumentation> typeInstrumentations() { | ||
return singletonList(new HttpSecurityInstrumentation()); | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
...nstrumentation/spring/security/config/v6_0/webflux/ServerHttpSecurityInstrumentation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.spring.security.config.v6_0.webflux; | ||
|
||
import static io.opentelemetry.javaagent.instrumentation.spring.security.config.v6_0.EnduserAttributesCapturerSingletons.enduserAttributesCapturer; | ||
import static net.bytebuddy.matcher.ElementMatchers.isMethod; | ||
import static net.bytebuddy.matcher.ElementMatchers.isPublic; | ||
import static net.bytebuddy.matcher.ElementMatchers.named; | ||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; | ||
|
||
import io.opentelemetry.instrumentation.spring.security.config.v6_0.webflux.EnduserAttributesServerHttpSecurityCustomizer; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; | ||
import net.bytebuddy.asm.Advice; | ||
import net.bytebuddy.description.type.TypeDescription; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
import org.springframework.security.config.web.server.ServerHttpSecurity; | ||
|
||
/** Instrumentation for {@link ServerHttpSecurity}. */ | ||
public class ServerHttpSecurityInstrumentation implements TypeInstrumentation { | ||
|
||
@Override | ||
public ElementMatcher<TypeDescription> typeMatcher() { | ||
return named("org.springframework.security.config.web.server.ServerHttpSecurity"); | ||
} | ||
|
||
@Override | ||
public void transform(TypeTransformer transformer) { | ||
transformer.applyAdviceToMethod( | ||
isMethod().and(isPublic()).and(named("build")).and(takesArguments(0)), | ||
getClass().getName() + "$BuildAdvice"); | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
public static class BuildAdvice { | ||
|
||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
public static void onEnter(@Advice.This ServerHttpSecurity serverHttpSecurity) { | ||
new EnduserAttributesServerHttpSecurityCustomizer(enduserAttributesCapturer()) | ||
.customize(serverHttpSecurity); | ||
} | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
...spring/security/config/v6_0/webflux/SpringSecurityConfigWebFluxInstrumentationModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.spring.security.config.v6_0.webflux; | ||
|
||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; | ||
import static java.util.Collections.singletonList; | ||
|
||
import com.google.auto.service.AutoService; | ||
import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; | ||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; | ||
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; | ||
import java.util.List; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
|
||
/** Instrumentation module for webflux-based applications that use spring-security-config. */ | ||
@AutoService(InstrumentationModule.class) | ||
public class SpringSecurityConfigWebFluxInstrumentationModule extends InstrumentationModule { | ||
|
||
public SpringSecurityConfigWebFluxInstrumentationModule() { | ||
super("spring-security-config-webflux", "spring-security-config-webflux-6.0"); | ||
} | ||
|
||
@Override | ||
public boolean defaultEnabled(ConfigProperties config) { | ||
/* | ||
* Since the only thing this module currently does is capture enduser attributes, | ||
* the module can be completely disabled if enduser attributes are disabled. | ||
* | ||
* If any functionality not related to enduser attributes is added to this module, | ||
* then this check will need to move elsewhere to only guard the enduser attributes logic. | ||
*/ | ||
return CommonConfig.get().getEnduserConfig().isEnabled(); | ||
} | ||
|
||
@Override | ||
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() { | ||
return hasClassesNamed("org.springframework.security.config.web.server.ServerHttpSecurity"); | ||
} | ||
|
||
@Override | ||
public List<TypeInstrumentation> typeInstrumentations() { | ||
return singletonList(new ServerHttpSecurityInstrumentation()); | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
.../instrumentation/spring/security/config/v6_0/servlet/HttpSecurityInstrumentationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.spring.security.config.v6_0.servlet; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import io.opentelemetry.instrumentation.spring.security.config.v6_0.servlet.EnduserAttributesCapturingServletFilter; | ||
import java.util.Collections; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.Mock; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.context.ApplicationContext; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.security.config.annotation.ObjectPostProcessor; | ||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.web.DefaultSecurityFilterChain; | ||
import org.springframework.test.context.junit.jupiter.SpringExtension; | ||
|
||
@ExtendWith(MockitoExtension.class) | ||
@ExtendWith(SpringExtension.class) | ||
class HttpSecurityInstrumentationTest { | ||
|
||
@Configuration | ||
static class TestConfiguration {} | ||
|
||
@Mock ObjectPostProcessor<Object> objectPostProcessor; | ||
|
||
/** | ||
* Ensures that {@link HttpSecurityInstrumentation} registers a {@link | ||
* EnduserAttributesCapturingServletFilter} in the filter chain. | ||
* | ||
* <p>Usage of the filter is covered in other unit tests. | ||
*/ | ||
@Test | ||
void ensureFilterRegistered(@Autowired ApplicationContext applicationContext) throws Exception { | ||
|
||
AuthenticationManagerBuilder authenticationBuilder = | ||
new AuthenticationManagerBuilder(objectPostProcessor); | ||
|
||
HttpSecurity httpSecurity = | ||
new HttpSecurity( | ||
objectPostProcessor, | ||
authenticationBuilder, | ||
Collections.singletonMap(ApplicationContext.class, applicationContext)); | ||
|
||
DefaultSecurityFilterChain filterChain = httpSecurity.build(); | ||
|
||
assertThat(filterChain.getFilters()) | ||
.filteredOn( | ||
item -> | ||
item.getClass() | ||
.getName() | ||
.endsWith(EnduserAttributesCapturingServletFilter.class.getSimpleName())) | ||
.hasSize(1); | ||
} | ||
} |
Oops, something went wrong.