Commit 52264cd0 authored by Phillip Webb's avatar Phillip Webb

Merge pull request #4406 from vpavic/audit-config

* pr/4406:
  Allow security AuditListener overrides
parents 564cbfe9 a0c696b1
...@@ -21,6 +21,8 @@ import org.springframework.boot.actuate.audit.AuditEvent; ...@@ -21,6 +21,8 @@ import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.boot.actuate.audit.AuditEventRepository; import org.springframework.boot.actuate.audit.AuditEventRepository;
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository; import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
import org.springframework.boot.actuate.audit.listener.AuditListener; 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.AuthenticationAuditListener;
import org.springframework.boot.actuate.security.AuthorizationAuditListener; import org.springframework.boot.actuate.security.AuthorizationAuditListener;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
...@@ -33,6 +35,7 @@ import org.springframework.context.annotation.Configuration; ...@@ -33,6 +35,7 @@ import org.springframework.context.annotation.Configuration;
* {@link EnableAutoConfiguration Auto-configuration} for {@link AuditEvent}s. * {@link EnableAutoConfiguration Auto-configuration} for {@link AuditEvent}s.
* *
* @author Dave Syer * @author Dave Syer
* @author Vedran Pavic
*/ */
@Configuration @Configuration
public class AuditAutoConfiguration { public class AuditAutoConfiguration {
...@@ -47,12 +50,14 @@ public class AuditAutoConfiguration { ...@@ -47,12 +50,14 @@ public class AuditAutoConfiguration {
@Bean @Bean
@ConditionalOnClass(name = "org.springframework.security.authentication.event.AbstractAuthenticationEvent") @ConditionalOnClass(name = "org.springframework.security.authentication.event.AbstractAuthenticationEvent")
@ConditionalOnMissingBean(AbstractAuthenticationAuditListener.class)
public AuthenticationAuditListener authenticationAuditListener() throws Exception { public AuthenticationAuditListener authenticationAuditListener() throws Exception {
return new AuthenticationAuditListener(); return new AuthenticationAuditListener();
} }
@Bean @Bean
@ConditionalOnClass(name = "org.springframework.security.access.event.AbstractAuthorizationEvent") @ConditionalOnClass(name = "org.springframework.security.access.event.AbstractAuthorizationEvent")
@ConditionalOnMissingBean(AbstractAuthorizationAuditListener.class)
public AuthorizationAuditListener authorizationAuditListener() throws Exception { public AuthorizationAuditListener authorizationAuditListener() throws Exception {
return new AuthorizationAuditListener(); return new AuthorizationAuditListener();
} }
......
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.security;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.AbstractAuthenticationEvent;
/**
* Abstract {@link ApplicationListener} to expose Spring Security
* {@link AbstractAuthenticationEvent authentication events} as {@link AuditEvent}s.
*
* @author Dave Syer
* @author Vedran Pavic
* @since 1.3.0
*/
public abstract class AbstractAuthenticationAuditListener implements
ApplicationListener<AbstractAuthenticationEvent>, ApplicationEventPublisherAware {
}
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.actuate.security;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.security.access.event.AbstractAuthorizationEvent;
/**
* Abstract {@link ApplicationListener} to expose Spring Security
* {@link AbstractAuthorizationEvent authorization events} as {@link AuditEvent}s.
*
* @author Dave Syer
* @author Vedran Pavic
* @since 1.3.0
*/
public abstract class AbstractAuthorizationAuditListener implements
ApplicationListener<AbstractAuthorizationEvent>, ApplicationEventPublisherAware {
}
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -22,8 +22,6 @@ import java.util.Map; ...@@ -22,8 +22,6 @@ import java.util.Map;
import org.springframework.boot.actuate.audit.AuditEvent; import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.boot.actuate.audit.listener.AuditApplicationEvent; import org.springframework.boot.actuate.audit.listener.AuditApplicationEvent;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.AbstractAuthenticationEvent; import org.springframework.security.authentication.event.AbstractAuthenticationEvent;
import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent; import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent; import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
...@@ -31,13 +29,11 @@ import org.springframework.security.web.authentication.switchuser.Authentication ...@@ -31,13 +29,11 @@ import org.springframework.security.web.authentication.switchuser.Authentication
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
/** /**
* {@link ApplicationListener} expose Spring Security {@link AbstractAuthenticationEvent * Default implementation of {@link AuthenticationAuditListener}.
* authentication events} as {@link AuditEvent}s.
* *
* @author Dave Syer * @author Dave Syer
*/ */
public class AuthenticationAuditListener implements public class AuthenticationAuditListener extends AbstractAuthenticationAuditListener {
ApplicationListener<AbstractAuthenticationEvent>, ApplicationEventPublisherAware {
private static final String WEB_LISTENER_CHECK_CLASS = "org.springframework.security.web.authentication.switchuser.AuthenticationSwitchUserEvent"; private static final String WEB_LISTENER_CHECK_CLASS = "org.springframework.security.web.authentication.switchuser.AuthenticationSwitchUserEvent";
......
/* /*
* Copyright 2012-2013 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -22,20 +22,16 @@ import java.util.Map; ...@@ -22,20 +22,16 @@ import java.util.Map;
import org.springframework.boot.actuate.audit.AuditEvent; import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.boot.actuate.audit.listener.AuditApplicationEvent; import org.springframework.boot.actuate.audit.listener.AuditApplicationEvent;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.security.access.event.AbstractAuthorizationEvent; import org.springframework.security.access.event.AbstractAuthorizationEvent;
import org.springframework.security.access.event.AuthenticationCredentialsNotFoundEvent; import org.springframework.security.access.event.AuthenticationCredentialsNotFoundEvent;
import org.springframework.security.access.event.AuthorizationFailureEvent; import org.springframework.security.access.event.AuthorizationFailureEvent;
/** /**
* {@link ApplicationListener} expose Spring Security {@link AbstractAuthorizationEvent * Default implementation of {@link AuthorizationAuditListener}.
* authorization events} as {@link AuditEvent}s.
* *
* @author Dave Syer * @author Dave Syer
*/ */
public class AuthorizationAuditListener implements public class AuthorizationAuditListener extends AbstractAuthorizationAuditListener {
ApplicationListener<AbstractAuthorizationEvent>, ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher; private ApplicationEventPublisher publisher;
......
/* /*
* Copyright 2012-2013 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -20,11 +20,16 @@ import org.junit.Test; ...@@ -20,11 +20,16 @@ import org.junit.Test;
import org.springframework.boot.actuate.audit.AuditEventRepository; import org.springframework.boot.actuate.audit.AuditEventRepository;
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository; import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
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.AuthenticationAuditListener;
import org.springframework.boot.actuate.security.AuthorizationAuditListener; import org.springframework.boot.actuate.security.AuthorizationAuditListener;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.event.AbstractAuthorizationEvent;
import org.springframework.security.authentication.event.AbstractAuthenticationEvent;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
...@@ -34,16 +39,15 @@ import static org.junit.Assert.assertThat; ...@@ -34,16 +39,15 @@ import static org.junit.Assert.assertThat;
* Tests for {@link AuditAutoConfiguration}. * Tests for {@link AuditAutoConfiguration}.
* *
* @author Dave Syer * @author Dave Syer
* @author Vedran Pavic
*/ */
public class AuditAutoConfigurationTests { public class AuditAutoConfigurationTests {
private AnnotationConfigApplicationContext context; private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
@Test @Test
public void testTraceConfiguration() throws Exception { public void testTraceConfiguration() throws Exception {
this.context = new AnnotationConfigApplicationContext(); registerAndRefresh(AuditAutoConfiguration.class);
this.context.register(AuditAutoConfiguration.class);
this.context.refresh();
assertNotNull(this.context.getBean(AuditEventRepository.class)); assertNotNull(this.context.getBean(AuditEventRepository.class));
assertNotNull(this.context.getBean(AuthenticationAuditListener.class)); assertNotNull(this.context.getBean(AuthenticationAuditListener.class));
assertNotNull(this.context.getBean(AuthorizationAuditListener.class)); assertNotNull(this.context.getBean(AuthorizationAuditListener.class));
...@@ -51,15 +55,35 @@ public class AuditAutoConfigurationTests { ...@@ -51,15 +55,35 @@ public class AuditAutoConfigurationTests {
@Test @Test
public void ownAutoRepository() throws Exception { public void ownAutoRepository() throws Exception {
this.context = new AnnotationConfigApplicationContext(); registerAndRefresh(CustomAuditEventRepositoryConfiguration.class,
this.context.register(Config.class, AuditAutoConfiguration.class); AuditAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBean(AuditEventRepository.class), assertThat(this.context.getBean(AuditEventRepository.class),
instanceOf(TestAuditEventRepository.class)); instanceOf(TestAuditEventRepository.class));
} }
@Test
public void ownAuthenticationAuditListener() throws Exception {
registerAndRefresh(CustomAuthenticationAuditListenerConfiguration.class,
AuditAutoConfiguration.class);
assertThat(this.context.getBean(AbstractAuthenticationAuditListener.class),
instanceOf(TestAuthenticationAuditListener.class));
}
@Test
public void ownAuthorizationAuditListener() throws Exception {
registerAndRefresh(CustomAuthorizationAuditListenerConfiguration.class,
AuditAutoConfiguration.class);
assertThat(this.context.getBean(AbstractAuthorizationAuditListener.class),
instanceOf(TestAuthorizationAuditListener.class));
}
private void registerAndRefresh(Class<?>... annotatedClasses) {
this.context.register(annotatedClasses);
this.context.refresh();
}
@Configuration @Configuration
public static class Config { public static class CustomAuditEventRepositoryConfiguration {
@Bean @Bean
public TestAuditEventRepository testAuditEventRepository() { public TestAuditEventRepository testAuditEventRepository() {
...@@ -71,4 +95,50 @@ public class AuditAutoConfigurationTests { ...@@ -71,4 +95,50 @@ public class AuditAutoConfigurationTests {
public static class TestAuditEventRepository extends InMemoryAuditEventRepository { public static class TestAuditEventRepository extends InMemoryAuditEventRepository {
} }
@Configuration
protected static class CustomAuthenticationAuditListenerConfiguration {
@Bean
public TestAuthenticationAuditListener authenticationAuditListener() {
return new TestAuthenticationAuditListener();
}
}
protected static class TestAuthenticationAuditListener
extends AbstractAuthenticationAuditListener {
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
}
@Override
public void onApplicationEvent(AbstractAuthenticationEvent event) {
}
}
@Configuration
protected static class CustomAuthorizationAuditListenerConfiguration {
@Bean
public TestAuthorizationAuditListener authorizationAuditListener() {
return new TestAuthorizationAuditListener();
}
}
protected static class TestAuthorizationAuditListener
extends AbstractAuthorizationAuditListener {
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
}
@Override
public void onApplicationEvent(AbstractAuthorizationEvent event) {
}
}
} }
/* /*
* Copyright 2012-2013 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
......
/* /*
* Copyright 2012-2013 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
......
...@@ -1324,7 +1324,9 @@ channel and any subscribers you need). ...@@ -1324,7 +1324,9 @@ channel and any subscribers you need).
Spring Boot Actuator has a flexible audit framework that will publish events once Spring Spring Boot Actuator has a flexible audit framework that will publish events once Spring
Security is in play ('`authentication success`', '`failure`' and '`access denied`' Security is in play ('`authentication success`', '`failure`' and '`access denied`'
exceptions by default). This can be very useful for reporting, and also to implement a exceptions by default). This can be very useful for reporting, and also to implement a
lock-out policy based on authentication failures. lock-out policy based on authentication failures. To customize published security events
you can provide your own implementations of `AbstractAuthenticationAuditListener` and
`AbstractAuthorizationAuditListener`.
You can also choose to use the audit services for your own business events. To do that You can also choose to use the audit services for your own business events. To do that
you can either inject the existing `AuditEventRepository` into your own components and you can either inject the existing `AuditEventRepository` into your own components and
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment