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 gh-16110
This commit is contained in:
parent
e2b15c3f2a
commit
07d6eb6397
|
@ -16,10 +16,8 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.audit;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.audit.AuditEvent;
|
||||
import org.springframework.boot.actuate.audit.AuditEventRepository;
|
||||
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
|
||||
import org.springframework.boot.actuate.audit.listener.AbstractAuditListener;
|
||||
import org.springframework.boot.actuate.audit.listener.AuditListener;
|
||||
import org.springframework.boot.actuate.security.AbstractAuthenticationAuditListener;
|
||||
|
@ -27,8 +25,10 @@ import org.springframework.boot.actuate.security.AbstractAuthorizationAuditListe
|
|||
import org.springframework.boot.actuate.security.AuthenticationAuditListener;
|
||||
import org.springframework.boot.actuate.security.AuthorizationAuditListener;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
@ -40,13 +40,15 @@ import org.springframework.context.annotation.Configuration;
|
|||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnBean(AuditEventRepository.class)
|
||||
@ConditionalOnProperty(prefix = "management.auditevents", name = "enabled",
|
||||
matchIfMissing = true)
|
||||
public class AuditAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AbstractAuditListener.class)
|
||||
public AuditListener auditListener(
|
||||
ObjectProvider<AuditEventRepository> auditEventRepository) throws Exception {
|
||||
return new AuditListener(auditEventRepository.getIfAvailable());
|
||||
public AuditListener auditListener(AuditEventRepository auditEventRepository) {
|
||||
return new AuditListener(auditEventRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
@ -65,15 +67,4 @@ public class AuditAutoConfiguration {
|
|||
return new AuthorizationAuditListener();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingBean(AuditEventRepository.class)
|
||||
protected static class AuditEventRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public InMemoryAuditEventRepository auditEventRepository() throws Exception {
|
||||
return new InMemoryAuditEventRepository();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -65,6 +65,12 @@
|
|||
"description": "Whether to skip SSL verification for Cloud Foundry actuator endpoint security calls.",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "management.auditevents.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Whether to enable storage of audit events.",
|
||||
"defaultValue": true
|
||||
},
|
||||
{
|
||||
"name": "management.health.cassandra.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
|
|
|
@ -22,12 +22,14 @@ import org.springframework.boot.actuate.audit.AuditEvent;
|
|||
import org.springframework.boot.actuate.audit.AuditEventRepository;
|
||||
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
|
||||
import org.springframework.boot.actuate.audit.listener.AbstractAuditListener;
|
||||
import org.springframework.boot.actuate.audit.listener.AuditListener;
|
||||
import org.springframework.boot.actuate.security.AbstractAuthenticationAuditListener;
|
||||
import org.springframework.boot.actuate.security.AbstractAuthorizationAuditListener;
|
||||
import org.springframework.boot.actuate.security.AuthenticationAuditListener;
|
||||
import org.springframework.boot.actuate.security.AuthorizationAuditListener;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.access.event.AbstractAuthorizationEvent;
|
||||
|
@ -40,54 +42,71 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
*
|
||||
* @author Dave Syer
|
||||
* @author Vedran Pavic
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
public class AuditAutoConfigurationTests {
|
||||
|
||||
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(AuditAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void defaultConfiguration() {
|
||||
registerAndRefresh(AuditAutoConfiguration.class);
|
||||
assertThat(this.context.getBean(AuditEventRepository.class)).isNotNull();
|
||||
assertThat(this.context.getBean(AuthenticationAuditListener.class)).isNotNull();
|
||||
assertThat(this.context.getBean(AuthorizationAuditListener.class)).isNotNull();
|
||||
public void autoConfigurationIsDisabledByDefault() {
|
||||
this.contextRunner.run((context) -> assertThat(context)
|
||||
.doesNotHaveBean(AuditAutoConfiguration.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ownAuditEventRepository() {
|
||||
registerAndRefresh(CustomAuditEventRepositoryConfiguration.class,
|
||||
AuditAutoConfiguration.class);
|
||||
assertThat(this.context.getBean(AuditEventRepository.class))
|
||||
.isInstanceOf(TestAuditEventRepository.class);
|
||||
public void autoConfigurationIsEnabledWhenAuditEventRepositoryBeanPresent() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(CustomAuditEventRepositoryConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context.getBean(AuditEventRepository.class)).isNotNull();
|
||||
assertThat(context.getBean(AuthenticationAuditListener.class))
|
||||
.isNotNull();
|
||||
assertThat(context.getBean(AuthorizationAuditListener.class))
|
||||
.isNotNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ownAuthenticationAuditListener() {
|
||||
registerAndRefresh(CustomAuthenticationAuditListenerConfiguration.class,
|
||||
AuditAutoConfiguration.class);
|
||||
assertThat(this.context.getBean(AbstractAuthenticationAuditListener.class))
|
||||
.isInstanceOf(TestAuthenticationAuditListener.class);
|
||||
this.contextRunner
|
||||
.withUserConfiguration(CustomAuditEventRepositoryConfiguration.class)
|
||||
.withUserConfiguration(
|
||||
CustomAuthenticationAuditListenerConfiguration.class)
|
||||
.run((context) -> assertThat(
|
||||
context.getBean(AbstractAuthenticationAuditListener.class))
|
||||
.isInstanceOf(TestAuthenticationAuditListener.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ownAuthorizationAuditListener() {
|
||||
registerAndRefresh(CustomAuthorizationAuditListenerConfiguration.class,
|
||||
AuditAutoConfiguration.class);
|
||||
assertThat(this.context.getBean(AbstractAuthorizationAuditListener.class))
|
||||
.isInstanceOf(TestAuthorizationAuditListener.class);
|
||||
this.contextRunner
|
||||
.withUserConfiguration(CustomAuditEventRepositoryConfiguration.class)
|
||||
.withUserConfiguration(
|
||||
CustomAuthorizationAuditListenerConfiguration.class)
|
||||
.run((context) -> assertThat(
|
||||
context.getBean(AbstractAuthorizationAuditListener.class))
|
||||
.isInstanceOf(TestAuthorizationAuditListener.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ownAuditListener() {
|
||||
registerAndRefresh(CustomAuditListenerConfiguration.class,
|
||||
AuditAutoConfiguration.class);
|
||||
assertThat(this.context.getBean(AbstractAuditListener.class))
|
||||
.isInstanceOf(TestAuditListener.class);
|
||||
this.contextRunner
|
||||
.withUserConfiguration(CustomAuditEventRepositoryConfiguration.class)
|
||||
.withUserConfiguration(CustomAuditListenerConfiguration.class)
|
||||
.run((context) -> assertThat(context.getBean(AbstractAuditListener.class))
|
||||
.isInstanceOf(TestAuditListener.class));
|
||||
}
|
||||
|
||||
private void registerAndRefresh(Class<?>... annotatedClasses) {
|
||||
this.context.register(annotatedClasses);
|
||||
this.context.refresh();
|
||||
@Test
|
||||
public void backsOffWhenDisabled() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(CustomAuditEventRepositoryConfiguration.class)
|
||||
.withPropertyValues("management.auditevents.enabled=false")
|
||||
.run((context) -> assertThat(context).doesNotHaveBean(AuditListener.class)
|
||||
.doesNotHaveBean(AuthenticationAuditListener.class)
|
||||
.doesNotHaveBean(AuthorizationAuditListener.class));
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
|
|
@ -19,8 +19,11 @@ package org.springframework.boot.actuate.autoconfigure.audit;
|
|||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.audit.AuditEventsEndpoint;
|
||||
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
@ -38,14 +41,24 @@ public class AuditEventsEndpointAutoConfigurationTests {
|
|||
AuditEventsEndpointAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void runShouldHaveEndpointBean() {
|
||||
public void runWhenRepositoryBeanAvailableShouldHaveEndpointBean() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(CustomAuditEventRepositoryConfiguration.class)
|
||||
.withPropertyValues(
|
||||
"management.endpoints.web.exposure.include=auditevents")
|
||||
.run((context) -> assertThat(context)
|
||||
.hasSingleBean(AuditEventsEndpoint.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void endpointBacksOffWhenRepositoryNotAvailable() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"management.endpoints.web.exposure.include=auditevents")
|
||||
.run((context) -> assertThat(context)
|
||||
.doesNotHaveBean(AuditEventsEndpoint.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runWhenNotExposedShouldNotHaveEndpointBean() {
|
||||
this.contextRunner.run((context) -> assertThat(context)
|
||||
|
@ -55,10 +68,21 @@ public class AuditEventsEndpointAutoConfigurationTests {
|
|||
@Test
|
||||
public void runWhenEnabledPropertyIsFalseShouldNotHaveEndpoint() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(CustomAuditEventRepositoryConfiguration.class)
|
||||
.withPropertyValues("management.endpoint.auditevents.enabled:false")
|
||||
.withPropertyValues("management.endpoints.web.exposure.include=*")
|
||||
.run((context) -> assertThat(context)
|
||||
.doesNotHaveBean(AuditEventsEndpoint.class));
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public static class CustomAuditEventRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public InMemoryAuditEventRepository testAuditEventRepository() {
|
||||
return new InMemoryAuditEventRepository();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import javax.management.ReflectionException;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
|
@ -53,7 +54,8 @@ public class JmxEndpointIntegrationTests {
|
|||
EndpointAutoConfiguration.class, JmxEndpointAutoConfiguration.class,
|
||||
HealthIndicatorAutoConfiguration.class,
|
||||
HttpTraceAutoConfiguration.class))
|
||||
.withUserConfiguration(HttpTraceRepositoryConfiguration.class)
|
||||
.withUserConfiguration(HttpTraceRepositoryConfiguration.class,
|
||||
AuditEventRepositoryConfiguration.class)
|
||||
.withPropertyValues("spring.jmx.enabled=true").withConfiguration(
|
||||
AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL));
|
||||
|
||||
|
@ -152,4 +154,14 @@ public class JmxEndpointIntegrationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public static class AuditEventRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public InMemoryAuditEventRepository auditEventRepository() {
|
||||
return new InMemoryAuditEventRepository();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration;
|
||||
|
@ -83,7 +84,8 @@ public class WebMvcEndpointExposureIntegrationTests {
|
|||
AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL))
|
||||
.withUserConfiguration(CustomMvcEndpoint.class,
|
||||
CustomServletEndpoint.class,
|
||||
HttpTraceRepositoryConfiguration.class)
|
||||
HttpTraceRepositoryConfiguration.class,
|
||||
AuditEventRepositoryConfiguration.class)
|
||||
.withPropertyValues("server.port:0");
|
||||
|
||||
@Test
|
||||
|
@ -229,4 +231,14 @@ public class WebMvcEndpointExposureIntegrationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public static class AuditEventRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public InMemoryAuditEventRepository auditEventRepository() {
|
||||
return new InMemoryAuditEventRepository();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2200,12 +2200,23 @@ maximum size for the "Metaspace", you could add an additional `tag=id:Metaspace`
|
|||
Once Spring Security is in play, Spring Boot Actuator has a flexible audit framework that
|
||||
publishes events (by default, "`authentication success`", "`failure`" and
|
||||
"`access denied`" exceptions). This feature can be very useful for reporting and for
|
||||
implementing a lock-out policy based on authentication failures. To customize published
|
||||
security events, you can provide your own implementations of
|
||||
implementing a lock-out policy based on authentication failures.
|
||||
|
||||
Auditing can be enabled by providing a bean of type `AuditEventRepository` in your application's
|
||||
configuration. For convenience, Spring Boot offers an `InMemoryAuditEventRepository`.
|
||||
`InMemoryAuditEventRepository` has limited capabilities and we recommend using it only for development
|
||||
environments. For production environments, consider creating your own alternative `AuditEventRepository`
|
||||
implementation.
|
||||
|
||||
|
||||
|
||||
[[production-ready-auditing-custom]]
|
||||
=== Custom Auditing
|
||||
To customize published security events, you can provide your own implementations of
|
||||
`AbstractAuthenticationAuditListener` and `AbstractAuthorizationAuditListener`.
|
||||
|
||||
You can also use the audit services for your own business events. To do so, either inject
|
||||
the existing `AuditEventRepository` into your own components and use that directly or
|
||||
the `AuditEventRepository` bean into your own components and use that directly or
|
||||
publish an `AuditApplicationEvent` with the Spring `ApplicationEventPublisher` (by
|
||||
implementing `ApplicationEventPublisherAware`).
|
||||
|
||||
|
|
Loading…
Reference in New Issue