Skip to content

Commit 07d6eb6

Browse files
committed
Disable auditing infrastructure by default
Prior to this commit, the audit auto-configuration provided an `InMemoryAuditEventRepository` bean. This commit changes the auto-config so that an `AuditEventRepository` is not provided and instead the auto-config is conditional on the presence of a `AuditEventRepository` bean. This is done to encourage the use of a custom implementation of `AuditEventRepository` since the in-memory one is quite limited and not suitable for production. A flag is available if the auto-configuration needs to be turned off even in the presence of a bean. Closes spring-projectsgh-16110
1 parent e2b15c3 commit 07d6eb6

File tree

7 files changed

+124
-49
lines changed

7 files changed

+124
-49
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/audit/AuditAutoConfiguration.java

+7-16
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,19 @@
1616

1717
package org.springframework.boot.actuate.autoconfigure.audit;
1818

19-
import org.springframework.beans.factory.ObjectProvider;
2019
import org.springframework.boot.actuate.audit.AuditEvent;
2120
import org.springframework.boot.actuate.audit.AuditEventRepository;
22-
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
2321
import org.springframework.boot.actuate.audit.listener.AbstractAuditListener;
2422
import org.springframework.boot.actuate.audit.listener.AuditListener;
2523
import org.springframework.boot.actuate.security.AbstractAuthenticationAuditListener;
2624
import org.springframework.boot.actuate.security.AbstractAuthorizationAuditListener;
2725
import org.springframework.boot.actuate.security.AuthenticationAuditListener;
2826
import org.springframework.boot.actuate.security.AuthorizationAuditListener;
2927
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
28+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
3029
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3130
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
31+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
3232
import org.springframework.context.annotation.Bean;
3333
import org.springframework.context.annotation.Configuration;
3434

@@ -40,13 +40,15 @@
4040
* @since 2.0.0
4141
*/
4242
@Configuration(proxyBeanMethods = false)
43+
@ConditionalOnBean(AuditEventRepository.class)
44+
@ConditionalOnProperty(prefix = "management.auditevents", name = "enabled",
45+
matchIfMissing = true)
4346
public class AuditAutoConfiguration {
4447

4548
@Bean
4649
@ConditionalOnMissingBean(AbstractAuditListener.class)
47-
public AuditListener auditListener(
48-
ObjectProvider<AuditEventRepository> auditEventRepository) throws Exception {
49-
return new AuditListener(auditEventRepository.getIfAvailable());
50+
public AuditListener auditListener(AuditEventRepository auditEventRepository) {
51+
return new AuditListener(auditEventRepository);
5052
}
5153

5254
@Bean
@@ -65,15 +67,4 @@ public AuthorizationAuditListener authorizationAuditListener() throws Exception
6567
return new AuthorizationAuditListener();
6668
}
6769

68-
@Configuration(proxyBeanMethods = false)
69-
@ConditionalOnMissingBean(AuditEventRepository.class)
70-
protected static class AuditEventRepositoryConfiguration {
71-
72-
@Bean
73-
public InMemoryAuditEventRepository auditEventRepository() throws Exception {
74-
return new InMemoryAuditEventRepository();
75-
}
76-
77-
}
78-
7970
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json

+6
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@
6565
"description": "Whether to skip SSL verification for Cloud Foundry actuator endpoint security calls.",
6666
"defaultValue": false
6767
},
68+
{
69+
"name": "management.auditevents.enabled",
70+
"type": "java.lang.Boolean",
71+
"description": "Whether to enable storage of audit events.",
72+
"defaultValue": true
73+
},
6874
{
6975
"name": "management.health.cassandra.enabled",
7076
"type": "java.lang.Boolean",

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/audit/AuditAutoConfigurationTests.java

+46-27
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@
2222
import org.springframework.boot.actuate.audit.AuditEventRepository;
2323
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
2424
import org.springframework.boot.actuate.audit.listener.AbstractAuditListener;
25+
import org.springframework.boot.actuate.audit.listener.AuditListener;
2526
import org.springframework.boot.actuate.security.AbstractAuthenticationAuditListener;
2627
import org.springframework.boot.actuate.security.AbstractAuthorizationAuditListener;
2728
import org.springframework.boot.actuate.security.AuthenticationAuditListener;
2829
import org.springframework.boot.actuate.security.AuthorizationAuditListener;
30+
import org.springframework.boot.autoconfigure.AutoConfigurations;
31+
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
2932
import org.springframework.context.ApplicationEventPublisher;
30-
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
3133
import org.springframework.context.annotation.Bean;
3234
import org.springframework.context.annotation.Configuration;
3335
import org.springframework.security.access.event.AbstractAuthorizationEvent;
@@ -40,54 +42,71 @@
4042
*
4143
* @author Dave Syer
4244
* @author Vedran Pavic
45+
* @author Madhura Bhave
4346
*/
4447
public class AuditAutoConfigurationTests {
4548

46-
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
49+
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
50+
.withConfiguration(AutoConfigurations.of(AuditAutoConfiguration.class));
4751

4852
@Test
49-
public void defaultConfiguration() {
50-
registerAndRefresh(AuditAutoConfiguration.class);
51-
assertThat(this.context.getBean(AuditEventRepository.class)).isNotNull();
52-
assertThat(this.context.getBean(AuthenticationAuditListener.class)).isNotNull();
53-
assertThat(this.context.getBean(AuthorizationAuditListener.class)).isNotNull();
53+
public void autoConfigurationIsDisabledByDefault() {
54+
this.contextRunner.run((context) -> assertThat(context)
55+
.doesNotHaveBean(AuditAutoConfiguration.class));
5456
}
5557

5658
@Test
57-
public void ownAuditEventRepository() {
58-
registerAndRefresh(CustomAuditEventRepositoryConfiguration.class,
59-
AuditAutoConfiguration.class);
60-
assertThat(this.context.getBean(AuditEventRepository.class))
61-
.isInstanceOf(TestAuditEventRepository.class);
59+
public void autoConfigurationIsEnabledWhenAuditEventRepositoryBeanPresent() {
60+
this.contextRunner
61+
.withUserConfiguration(CustomAuditEventRepositoryConfiguration.class)
62+
.run((context) -> {
63+
assertThat(context.getBean(AuditEventRepository.class)).isNotNull();
64+
assertThat(context.getBean(AuthenticationAuditListener.class))
65+
.isNotNull();
66+
assertThat(context.getBean(AuthorizationAuditListener.class))
67+
.isNotNull();
68+
});
6269
}
6370

6471
@Test
6572
public void ownAuthenticationAuditListener() {
66-
registerAndRefresh(CustomAuthenticationAuditListenerConfiguration.class,
67-
AuditAutoConfiguration.class);
68-
assertThat(this.context.getBean(AbstractAuthenticationAuditListener.class))
69-
.isInstanceOf(TestAuthenticationAuditListener.class);
73+
this.contextRunner
74+
.withUserConfiguration(CustomAuditEventRepositoryConfiguration.class)
75+
.withUserConfiguration(
76+
CustomAuthenticationAuditListenerConfiguration.class)
77+
.run((context) -> assertThat(
78+
context.getBean(AbstractAuthenticationAuditListener.class))
79+
.isInstanceOf(TestAuthenticationAuditListener.class));
7080
}
7181

7282
@Test
7383
public void ownAuthorizationAuditListener() {
74-
registerAndRefresh(CustomAuthorizationAuditListenerConfiguration.class,
75-
AuditAutoConfiguration.class);
76-
assertThat(this.context.getBean(AbstractAuthorizationAuditListener.class))
77-
.isInstanceOf(TestAuthorizationAuditListener.class);
84+
this.contextRunner
85+
.withUserConfiguration(CustomAuditEventRepositoryConfiguration.class)
86+
.withUserConfiguration(
87+
CustomAuthorizationAuditListenerConfiguration.class)
88+
.run((context) -> assertThat(
89+
context.getBean(AbstractAuthorizationAuditListener.class))
90+
.isInstanceOf(TestAuthorizationAuditListener.class));
7891
}
7992

8093
@Test
8194
public void ownAuditListener() {
82-
registerAndRefresh(CustomAuditListenerConfiguration.class,
83-
AuditAutoConfiguration.class);
84-
assertThat(this.context.getBean(AbstractAuditListener.class))
85-
.isInstanceOf(TestAuditListener.class);
95+
this.contextRunner
96+
.withUserConfiguration(CustomAuditEventRepositoryConfiguration.class)
97+
.withUserConfiguration(CustomAuditListenerConfiguration.class)
98+
.run((context) -> assertThat(context.getBean(AbstractAuditListener.class))
99+
.isInstanceOf(TestAuditListener.class));
86100
}
87101

88-
private void registerAndRefresh(Class<?>... annotatedClasses) {
89-
this.context.register(annotatedClasses);
90-
this.context.refresh();
102+
@Test
103+
public void backsOffWhenDisabled() {
104+
this.contextRunner
105+
.withUserConfiguration(CustomAuditEventRepositoryConfiguration.class)
106+
.withPropertyValues("management.auditevents.enabled=false")
107+
.run((context) -> assertThat(context).doesNotHaveBean(AuditListener.class)
108+
.doesNotHaveBean(AuthenticationAuditListener.class)
109+
.doesNotHaveBean(AuthorizationAuditListener.class));
91110
}
92111

93112
@Configuration(proxyBeanMethods = false)

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/audit/AuditEventsEndpointAutoConfigurationTests.java

+25-1
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@
1919
import org.junit.Test;
2020

2121
import org.springframework.boot.actuate.audit.AuditEventsEndpoint;
22+
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
2223
import org.springframework.boot.autoconfigure.AutoConfigurations;
2324
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
25+
import org.springframework.context.annotation.Bean;
26+
import org.springframework.context.annotation.Configuration;
2427

2528
import static org.assertj.core.api.Assertions.assertThat;
2629

@@ -38,14 +41,24 @@ public class AuditEventsEndpointAutoConfigurationTests {
3841
AuditEventsEndpointAutoConfiguration.class));
3942

4043
@Test
41-
public void runShouldHaveEndpointBean() {
44+
public void runWhenRepositoryBeanAvailableShouldHaveEndpointBean() {
4245
this.contextRunner
46+
.withUserConfiguration(CustomAuditEventRepositoryConfiguration.class)
4347
.withPropertyValues(
4448
"management.endpoints.web.exposure.include=auditevents")
4549
.run((context) -> assertThat(context)
4650
.hasSingleBean(AuditEventsEndpoint.class));
4751
}
4852

53+
@Test
54+
public void endpointBacksOffWhenRepositoryNotAvailable() {
55+
this.contextRunner
56+
.withPropertyValues(
57+
"management.endpoints.web.exposure.include=auditevents")
58+
.run((context) -> assertThat(context)
59+
.doesNotHaveBean(AuditEventsEndpoint.class));
60+
}
61+
4962
@Test
5063
public void runWhenNotExposedShouldNotHaveEndpointBean() {
5164
this.contextRunner.run((context) -> assertThat(context)
@@ -55,10 +68,21 @@ public void runWhenNotExposedShouldNotHaveEndpointBean() {
5568
@Test
5669
public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpoint() {
5770
this.contextRunner
71+
.withUserConfiguration(CustomAuditEventRepositoryConfiguration.class)
5872
.withPropertyValues("management.endpoint.auditevents.enabled:false")
5973
.withPropertyValues("management.endpoints.web.exposure.include=*")
6074
.run((context) -> assertThat(context)
6175
.doesNotHaveBean(AuditEventsEndpoint.class));
6276
}
6377

78+
@Configuration(proxyBeanMethods = false)
79+
public static class CustomAuditEventRepositoryConfiguration {
80+
81+
@Bean
82+
public InMemoryAuditEventRepository testAuditEventRepository() {
83+
return new InMemoryAuditEventRepository();
84+
}
85+
86+
}
87+
6488
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/JmxEndpointIntegrationTests.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import org.junit.Test;
2828

29+
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
2930
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
3031
import org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration;
3132
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
@@ -53,7 +54,8 @@ public class JmxEndpointIntegrationTests {
5354
EndpointAutoConfiguration.class, JmxEndpointAutoConfiguration.class,
5455
HealthIndicatorAutoConfiguration.class,
5556
HttpTraceAutoConfiguration.class))
56-
.withUserConfiguration(HttpTraceRepositoryConfiguration.class)
57+
.withUserConfiguration(HttpTraceRepositoryConfiguration.class,
58+
AuditEventRepositoryConfiguration.class)
5759
.withPropertyValues("spring.jmx.enabled=true").withConfiguration(
5860
AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL));
5961

@@ -152,4 +154,14 @@ public InMemoryHttpTraceRepository httpTraceRepository() {
152154

153155
}
154156

157+
@Configuration(proxyBeanMethods = false)
158+
public static class AuditEventRepositoryConfiguration {
159+
160+
@Bean
161+
public InMemoryAuditEventRepository auditEventRepository() {
162+
return new InMemoryAuditEventRepository();
163+
}
164+
165+
}
166+
155167
}

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/integrationtest/WebMvcEndpointExposureIntegrationTests.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import org.junit.Test;
2828

29+
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
2930
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
3031
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
3132
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
@@ -83,7 +84,8 @@ public class WebMvcEndpointExposureIntegrationTests {
8384
AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL))
8485
.withUserConfiguration(CustomMvcEndpoint.class,
8586
CustomServletEndpoint.class,
86-
HttpTraceRepositoryConfiguration.class)
87+
HttpTraceRepositoryConfiguration.class,
88+
AuditEventRepositoryConfiguration.class)
8789
.withPropertyValues("server.port:0");
8890

8991
@Test
@@ -229,4 +231,14 @@ public InMemoryHttpTraceRepository httpTraceRepository() {
229231

230232
}
231233

234+
@Configuration(proxyBeanMethods = false)
235+
public static class AuditEventRepositoryConfiguration {
236+
237+
@Bean
238+
public InMemoryAuditEventRepository auditEventRepository() {
239+
return new InMemoryAuditEventRepository();
240+
}
241+
242+
}
243+
232244
}

spring-boot-project/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc

+14-3
Original file line numberDiff line numberDiff line change
@@ -2200,12 +2200,23 @@ maximum size for the "Metaspace", you could add an additional `tag=id:Metaspace`
22002200
Once Spring Security is in play, Spring Boot Actuator has a flexible audit framework that
22012201
publishes events (by default, "`authentication success`", "`failure`" and
22022202
"`access denied`" exceptions). This feature can be very useful for reporting and for
2203-
implementing a lock-out policy based on authentication failures. To customize published
2204-
security events, you can provide your own implementations of
2203+
implementing a lock-out policy based on authentication failures.
2204+
2205+
Auditing can be enabled by providing a bean of type `AuditEventRepository` in your application's
2206+
configuration. For convenience, Spring Boot offers an `InMemoryAuditEventRepository`.
2207+
`InMemoryAuditEventRepository` has limited capabilities and we recommend using it only for development
2208+
environments. For production environments, consider creating your own alternative `AuditEventRepository`
2209+
implementation.
2210+
2211+
2212+
2213+
[[production-ready-auditing-custom]]
2214+
=== Custom Auditing
2215+
To customize published security events, you can provide your own implementations of
22052216
`AbstractAuthenticationAuditListener` and `AbstractAuthorizationAuditListener`.
22062217

22072218
You can also use the audit services for your own business events. To do so, either inject
2208-
the existing `AuditEventRepository` into your own components and use that directly or
2219+
the `AuditEventRepository` bean into your own components and use that directly or
22092220
publish an `AuditApplicationEvent` with the Spring `ApplicationEventPublisher` (by
22102221
implementing `ApplicationEventPublisherAware`).
22112222

0 commit comments

Comments
 (0)