diff --git a/spring-bootstrap-service/README.md b/spring-bootstrap-service/README.md
index 69f43244d6..880d48f259 100644
--- a/spring-bootstrap-service/README.md
+++ b/spring-bootstrap-service/README.md
@@ -22,8 +22,9 @@ production, and in other environments.
|Logging |Logback, Log4j or JDK | Whatever is on the classpath. Sensible defaults. |
|Database |HSQLDB or H2 | Per classpath, or define a DataSource to override |
|Externalized configuration | Properties or YAML | Support for Spring profiles. Bind automatically to @Bean. |
+|Audit | Spring Security and Spring ApplicationEvent |Flexible abstraction with sensible defaults for security events |
|Validation | JSR-303 | |
-|Management endpoints | Spring MVC | Health, basic metrics, request tracing, shutdown |
+|Management endpoints | Spring MVC | Health, basic metrics, request tracing, shutdown, thread dumps |
|Error pages | Spring MVC | Sensible defaults based on exception and status code |
|JSON |Jackson 2 | |
|ORM |Spring Data JPA | If on the classpath |
@@ -33,7 +34,7 @@ production, and in other environments.
# Getting Started
You will need Java (6 at least) and a build tool (Maven is what we use
-below, but you are more than wecome to use gradle). These can be
+below, but you are more than welcome to use gradle). These can be
downloaded or installed easily in most operating systems. FIXME:
short instructions for Mac and Linux.
@@ -101,12 +102,17 @@ Then in another terminal
you if the application is running and healthy. `/varz` is the default
location for the metrics endpoint - it gives you basic counts and
response timing data by default but there are plenty of ways to
-customize it.
+customize it. You can also try `/trace` and `/dump` to get some
+interesting information about how and what your app is doing.
+
+What about the home page?
$ curl localhost:8080/
{"status": 404, "error": "Not Found", "message": "Not Found"}
-That's OK, we haven't added any business content yet.
+That's OK, we haven't added any business content yet. But it shows
+that there are sensible defaults built in for rendering HTTP and
+server-side errors.
## Adding a business endpoint
@@ -146,7 +152,48 @@ and re-package:
$ curl localhost:8080/
{"message": "Hello World"}
-# Add a database
+# Adding security
+
+If you add Spring Security java config to your runtime classpath you
+will enable HTTP basic authentication by default on all the endpoints.
+In the `pom.xml` it would look like this:
+
+
+ org.springframework.security
+ spring-security-javaconfig
+ 1.0.0.BUILD-SNAPSHOT
+
+
+(Spring Security java config is still work in progress so we have used
+a snapshot. Beware of sudden changes. FIXME: update to full
+release.)
+
+Try it out:
+
+ $ curl localhost:8080/
+ {"status": 403, "error": "Forbidden", "message": "Access Denied"}
+ $ curl user:password@localhost:8080/
+ {"message": "Hello World"}
+
+The default auto configuration has an in-memory user database with one
+entry. If you want to extend or expand that, or point to a database
+or directory server, you only need to provide a `@Bean` definition for
+an `AuthenticationManager`, e.g. in your `SampleController`:
+
+ @Bean
+ public AuthenticationManager authenticationManager() throws Exception {
+ return new AuthenticationBuilder().inMemoryAuthentication().withUser("client")
+ .password("secret").roles("USER").and().and().build();
+ }
+
+Try it out:
+
+ $ curl user:password@localhost:8080/
+ {"status": 403, "error": "Forbidden", "message": "Access Denied"}
+ $ curl client:secret@localhost:8080/
+ {"message": "Hello World"}
+
+# Adding a database
Just add `spring-jdbc` and an embedded database to your dependencies:
diff --git a/spring-bootstrap-service/pom.xml b/spring-bootstrap-service/pom.xml
index b291904291..1efca15fc0 100644
--- a/spring-bootstrap-service/pom.xml
+++ b/spring-bootstrap-service/pom.xml
@@ -40,11 +40,6 @@
javax.servlet-api
provided
-
- joda-time
- joda-time
- 1.6
-
org.hibernate
hibernate-validator
@@ -65,10 +60,6 @@
com.fasterxml.jackson.core
jackson-databind
-
- com.fasterxml.jackson.datatype
- jackson-datatype-joda
-
org.springframework.security
spring-security-javaconfig
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/AuditConfiguration.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/AuditConfiguration.java
new file mode 100644
index 0000000000..51ab15009e
--- /dev/null
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/AuditConfiguration.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2012-2013 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.bootstrap.autoconfigure.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
+import org.springframework.bootstrap.service.audit.AuditEventRepository;
+import org.springframework.bootstrap.service.audit.InMemoryAuditEventRepository;
+import org.springframework.bootstrap.service.audit.listener.AuditListener;
+import org.springframework.bootstrap.service.security.AuthenticationAuditListener;
+import org.springframework.bootstrap.service.security.AuthorizationAuditListener;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Dave Syer
+ *
+ */
+@Configuration
+public class AuditConfiguration {
+
+ @Autowired(required = false)
+ private AuditEventRepository auditEventRepository = new InMemoryAuditEventRepository();
+
+ @Bean
+ @ConditionalOnMissingBean(AuditEventRepository.class)
+ public AuditEventRepository auditEventRepository() throws Exception {
+ return this.auditEventRepository;
+ }
+
+ @Bean
+ public AuditListener auditListener() throws Exception {
+ return new AuditListener(this.auditEventRepository);
+ }
+
+ @Bean
+ public AuthenticationAuditListener authenticationAuditListener() throws Exception {
+ return new AuthenticationAuditListener();
+ }
+
+ @Bean
+ public AuthorizationAuditListener authorizationAuditListener() throws Exception {
+ return new AuthorizationAuditListener();
+ }
+
+}
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/MetricFilterConfiguration.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/MetricFilterConfiguration.java
index 194fc2f441..9eb451e41a 100644
--- a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/MetricFilterConfiguration.java
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/MetricFilterConfiguration.java
@@ -27,8 +27,6 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.joda.time.DateTime;
-import org.joda.time.Duration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.bootstrap.context.annotation.ConditionalOnBean;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
@@ -81,7 +79,7 @@ public class MetricFilterConfiguration {
UrlPathHelper helper = new UrlPathHelper();
String suffix = helper.getPathWithinApplication(servletRequest);
int status = 999;
- DateTime t0 = new DateTime();
+ long t0 = System.currentTimeMillis();
try {
chain.doFilter(request, response);
} finally {
@@ -90,7 +88,7 @@ public class MetricFilterConfiguration {
} catch (Exception e) {
// ignore
}
- set("response", suffix, new Duration(t0, new DateTime()).getMillis());
+ set("response", suffix, System.currentTimeMillis() - t0);
increment("status." + status, suffix);
}
}
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/SecurityConfiguration.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/SecurityConfiguration.java
index 47f6ebedf6..d968630d4c 100644
--- a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/SecurityConfiguration.java
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/SecurityConfiguration.java
@@ -24,28 +24,39 @@ import org.springframework.bootstrap.context.annotation.EnableConfigurationPrope
import org.springframework.bootstrap.service.properties.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
+import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.authentication.AuthenticationBuilder;
import org.springframework.security.config.annotation.web.EnableWebSecurity;
import org.springframework.security.config.annotation.web.ExpressionUrlAuthorizations;
import org.springframework.security.config.annotation.web.HttpConfigurator;
import org.springframework.security.config.annotation.web.SpringSecurityFilterChainBuilder.IgnoredRequestRegistry;
-import org.springframework.security.config.annotation.web.WebSecurityConfiguration;
import org.springframework.security.config.annotation.web.WebSecurityConfigurerAdapter;
-import org.springframework.stereotype.Component;
/**
* @author Dave Syer
*/
@Configuration
@ConditionalOnClass({ EnableWebSecurity.class })
-@ConditionalOnMissingBean({ WebSecurityConfiguration.class })
@EnableWebSecurity
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityConfiguration {
- @Component
- public static class WebSecurityAdapter extends WebSecurityConfigurerAdapter {
+ @Bean
+ @ConditionalOnMissingBean({ AuthenticationEventPublisher.class })
+ public AuthenticationEventPublisher authenticationEventPublisher() {
+ return new DefaultAuthenticationEventPublisher();
+ }
+
+ @Bean
+ public WebSecurityConfigurerAdapter webSecurityConfigurerAdapter() {
+ return new BoostrapWebSecurityConfigurerAdapter();
+ }
+
+ private static class BoostrapWebSecurityConfigurerAdapter extends
+ WebSecurityConfigurerAdapter {
@Value("${endpoints.healthz.path:/healthz}")
private String healthzPath = "/healthz";
@@ -53,6 +64,9 @@ public class SecurityConfiguration {
@Autowired
private SecurityProperties security;
+ @Autowired
+ private AuthenticationEventPublisher authenticationEventPublisher;
+
@Override
protected void ignoredRequests(IgnoredRequestRegistry ignoredRequests) {
ignoredRequests.antMatchers(this.healthzPath);
@@ -69,6 +83,17 @@ public class SecurityConfiguration {
if (this.security.isRequireSsl()) {
http.requiresChannel().antMatchers("/**").requiresSecure();
}
+
+ }
+
+ @Override
+ protected AuthenticationManager authenticationManager() throws Exception {
+ AuthenticationManager manager = super.authenticationManager();
+ if (manager instanceof ProviderManager) {
+ ((ProviderManager) manager)
+ .setAuthenticationEventPublisher(this.authenticationEventPublisher);
+ }
+ return manager;
}
}
@@ -84,4 +109,5 @@ public class SecurityConfiguration {
}
}
+
}
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/ServiceAutoConfiguration.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/ServiceAutoConfiguration.java
index 7b3b922303..48f21de1ca 100644
--- a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/ServiceAutoConfiguration.java
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/ServiceAutoConfiguration.java
@@ -32,7 +32,6 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import com.fasterxml.jackson.databind.SerializationFeature;
-import com.fasterxml.jackson.datatype.joda.JodaModule;
/**
* {@link EnableAutoConfiguration Auto-configuration} for service apps.
@@ -42,7 +41,7 @@ import com.fasterxml.jackson.datatype.joda.JodaModule;
@Configuration
@Import({ ManagementConfiguration.class, MetricConfiguration.class,
ServerConfiguration.class, SecurityConfiguration.class,
- MetricFilterConfiguration.class })
+ MetricFilterConfiguration.class, AuditConfiguration.class })
public class ServiceAutoConfiguration extends WebMvcConfigurationSupport {
@Override
@@ -51,7 +50,6 @@ public class ServiceAutoConfiguration extends WebMvcConfigurationSupport {
for (HttpMessageConverter> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter) converter;
- jacksonConverter.getObjectMapper().registerModule(new JodaModule());
jacksonConverter.getObjectMapper().disable(
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/TraceConfiguration.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/TraceConfiguration.java
index 82a3390ac1..15e225859e 100644
--- a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/TraceConfiguration.java
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/autoconfigure/service/TraceConfiguration.java
@@ -24,8 +24,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
+import org.springframework.bootstrap.service.security.SecurityFilterPostProcessor;
import org.springframework.bootstrap.service.trace.InMemoryTraceRepository;
-import org.springframework.bootstrap.service.trace.SecurityFilterPostProcessor;
import org.springframework.bootstrap.service.trace.TraceEndpoint;
import org.springframework.bootstrap.service.trace.TraceRepository;
import org.springframework.context.annotation.Bean;
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/audit/AuditEvent.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/audit/AuditEvent.java
index c76b689b8a..d3bdad5c37 100644
--- a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/audit/AuditEvent.java
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/audit/AuditEvent.java
@@ -90,4 +90,10 @@ public class AuditEvent {
return result;
}
+ @Override
+ public String toString() {
+ return "AuditEvent [timestamp=" + this.timestamp + ", principal="
+ + this.principal + ", type=" + this.type + ", data=" + this.data + "]";
+ }
+
}
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/audit/listener/AuditListener.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/audit/listener/AuditListener.java
index 6c1bd1d01f..52e9785dd0 100644
--- a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/audit/listener/AuditListener.java
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/audit/listener/AuditListener.java
@@ -15,6 +15,8 @@
*/
package org.springframework.bootstrap.service.audit.listener;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.springframework.bootstrap.service.audit.AuditEventRepository;
import org.springframework.context.ApplicationListener;
@@ -24,6 +26,8 @@ import org.springframework.context.ApplicationListener;
*/
public class AuditListener implements ApplicationListener {
+ private static Log logger = LogFactory.getLog(AuditListener.class);
+
private final AuditEventRepository auditEventRepository;
public AuditListener(AuditEventRepository auditEventRepository) {
@@ -32,6 +36,7 @@ public class AuditListener implements ApplicationListener
@Override
public void onApplicationEvent(AuditApplicationEvent event) {
+ logger.info(event.getAuditEvent());
this.auditEventRepository.add(event.getAuditEvent());
}
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/DefaultCounterService.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/DefaultCounterService.java
index 8f73f646de..18062d95a4 100644
--- a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/DefaultCounterService.java
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/DefaultCounterService.java
@@ -16,7 +16,7 @@
package org.springframework.bootstrap.service.metrics;
-import org.joda.time.DateTime;
+import java.util.Date;
/**
* @author Dave Syer
@@ -35,17 +35,17 @@ public class DefaultCounterService implements CounterService {
@Override
public void increment(String metricName) {
- this.counterRepository.increment(wrap(metricName), 1, new DateTime());
+ this.counterRepository.increment(wrap(metricName), 1, new Date());
}
@Override
public void decrement(String metricName) {
- this.counterRepository.increment(wrap(metricName), -1, new DateTime());
+ this.counterRepository.increment(wrap(metricName), -1, new Date());
}
@Override
public void reset(String metricName) {
- this.counterRepository.set(wrap(metricName), 0, new DateTime());
+ this.counterRepository.set(wrap(metricName), 0, new Date());
}
private String wrap(String metricName) {
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/DefaultGaugeService.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/DefaultGaugeService.java
index 404d6cd08b..afc57d4d4d 100644
--- a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/DefaultGaugeService.java
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/DefaultGaugeService.java
@@ -16,7 +16,7 @@
package org.springframework.bootstrap.service.metrics;
-import org.joda.time.DateTime;
+import java.util.Date;
/**
* @author Dave Syer
@@ -35,7 +35,7 @@ public class DefaultGaugeService implements GaugeService {
@Override
public void set(String metricName, double value) {
- this.metricRepository.set(wrap(metricName), value, new DateTime());
+ this.metricRepository.set(wrap(metricName), value, new Date());
}
private String wrap(String metricName) {
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/InMemoryMetricRepository.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/InMemoryMetricRepository.java
index a8098973fb..e8dd216cd4 100644
--- a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/InMemoryMetricRepository.java
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/InMemoryMetricRepository.java
@@ -18,11 +18,10 @@ package org.springframework.bootstrap.service.metrics;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import org.joda.time.DateTime;
-
/**
* @author Dave Syer
*/
@@ -31,27 +30,27 @@ public class InMemoryMetricRepository implements MetricRepository {
private ConcurrentMap metrics = new ConcurrentHashMap();
@Override
- public void increment(String metricName, int amount, DateTime dateTime) {
+ public void increment(String metricName, int amount, Date timestamp) {
Measurement current = this.metrics.get(metricName);
if (current != null) {
Metric metric = current.getMetric();
this.metrics.replace(metricName, current,
- new Measurement(dateTime, metric.increment(amount)));
+ new Measurement(timestamp, metric.increment(amount)));
} else {
- this.metrics.putIfAbsent(metricName, new Measurement(dateTime, new Metric(
+ this.metrics.putIfAbsent(metricName, new Measurement(timestamp, new Metric(
metricName, amount)));
}
}
@Override
- public void set(String metricName, double value, DateTime dateTime) {
+ public void set(String metricName, double value, Date timestamp) {
Measurement current = this.metrics.get(metricName);
if (current != null) {
Metric metric = current.getMetric();
this.metrics.replace(metricName, current,
- new Measurement(dateTime, metric.set(value)));
+ new Measurement(timestamp, metric.set(value)));
} else {
- this.metrics.putIfAbsent(metricName, new Measurement(dateTime, new Metric(
+ this.metrics.putIfAbsent(metricName, new Measurement(timestamp, new Metric(
metricName, value)));
}
}
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/Measurement.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/Measurement.java
index bb9d602cf0..3a857c75be 100644
--- a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/Measurement.java
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/Measurement.java
@@ -16,24 +16,24 @@
package org.springframework.bootstrap.service.metrics;
-import org.joda.time.DateTime;
+import java.util.Date;
/**
* @author Dave Syer
*/
public class Measurement {
- private DateTime dateTime;
+ private Date timestamp;
private Metric metric;
- public Measurement(DateTime dateTime, Metric metric) {
- this.dateTime = dateTime;
+ public Measurement(Date timestamp, Metric metric) {
+ this.timestamp = timestamp;
this.metric = metric;
}
- public DateTime getDateTime() {
- return this.dateTime;
+ public Date getTimestamp() {
+ return this.timestamp;
}
public Metric getMetric() {
@@ -42,7 +42,7 @@ public class Measurement {
@Override
public String toString() {
- return "Measurement [dateTime=" + this.dateTime + ", metric=" + this.metric + "]";
+ return "Measurement [dateTime=" + this.timestamp + ", metric=" + this.metric + "]";
}
@Override
@@ -50,7 +50,7 @@ public class Measurement {
final int prime = 31;
int result = 1;
result = prime * result
- + ((this.dateTime == null) ? 0 : this.dateTime.hashCode());
+ + ((this.timestamp == null) ? 0 : this.timestamp.hashCode());
result = prime * result + ((this.metric == null) ? 0 : this.metric.hashCode());
return result;
}
@@ -64,10 +64,10 @@ public class Measurement {
if (getClass() != obj.getClass())
return false;
Measurement other = (Measurement) obj;
- if (this.dateTime == null) {
- if (other.dateTime != null)
+ if (this.timestamp == null) {
+ if (other.timestamp != null)
return false;
- } else if (!this.dateTime.equals(other.dateTime))
+ } else if (!this.timestamp.equals(other.timestamp))
return false;
if (this.metric == null) {
if (other.metric != null)
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/MetricRepository.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/MetricRepository.java
index 34a03df716..d903582f8a 100644
--- a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/MetricRepository.java
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/metrics/MetricRepository.java
@@ -17,17 +17,16 @@
package org.springframework.bootstrap.service.metrics;
import java.util.Collection;
-
-import org.joda.time.DateTime;
+import java.util.Date;
/**
* @author Dave Syer
*/
public interface MetricRepository {
- void increment(String metricName, int amount, DateTime dateTime);
+ void increment(String metricName, int amount, Date timestamp);
- void set(String metricName, double value, DateTime dateTime);
+ void set(String metricName, double value, Date timestamp);
void delete(String metricName);
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/security/AuthenticationAuditListener.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/security/AuthenticationAuditListener.java
new file mode 100644
index 0000000000..e4b2ced3ee
--- /dev/null
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/security/AuthenticationAuditListener.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2012-2013 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.bootstrap.service.security;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.bootstrap.service.audit.AuditEvent;
+import org.springframework.bootstrap.service.audit.listener.AuditApplicationEvent;
+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.AbstractAuthenticationFailureEvent;
+import org.springframework.security.web.authentication.switchuser.AuthenticationSwitchUserEvent;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class AuthenticationAuditListener implements
+ ApplicationListener, ApplicationEventPublisherAware {
+
+ private ApplicationEventPublisher publisher;
+
+ @Override
+ public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
+ this.publisher = publisher;
+ }
+
+ @Override
+ public void onApplicationEvent(AbstractAuthenticationEvent event) {
+ Map data = new HashMap();
+ if (event instanceof AbstractAuthenticationFailureEvent) {
+ data.put("type", ((AbstractAuthenticationFailureEvent) event).getException()
+ .getClass().getName());
+ data.put("message", ((AbstractAuthenticationFailureEvent) event)
+ .getException().getMessage());
+ publish(new AuditEvent(event.getAuthentication().getName(),
+ "AUTHENTICATION_FAILURE", data));
+ } else if (event instanceof AuthenticationSwitchUserEvent) {
+ if (event.getAuthentication().getDetails() != null) {
+ data.put("details", event.getAuthentication().getDetails());
+ }
+ data.put("target", ((AuthenticationSwitchUserEvent) event).getTargetUser()
+ .getUsername());
+ publish(new AuditEvent(event.getAuthentication().getName(),
+ "AUTHENTICATION_SWITCH", data));
+
+ } else {
+ if (event.getAuthentication().getDetails() != null) {
+ data.put("details", event.getAuthentication().getDetails());
+ }
+ publish(new AuditEvent(event.getAuthentication().getName(),
+ "AUTHENTICATION_SUCCESS", data));
+ }
+ }
+
+ private void publish(AuditEvent event) {
+ if (this.publisher != null) {
+ this.publisher.publishEvent(new AuditApplicationEvent(event));
+ }
+ }
+
+}
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/security/AuthorizationAuditListener.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/security/AuthorizationAuditListener.java
new file mode 100644
index 0000000000..98a627b54b
--- /dev/null
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/security/AuthorizationAuditListener.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012-2013 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.bootstrap.service.security;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.bootstrap.service.audit.AuditEvent;
+import org.springframework.bootstrap.service.audit.listener.AuditApplicationEvent;
+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.AuthenticationCredentialsNotFoundEvent;
+import org.springframework.security.access.event.AuthorizationFailureEvent;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class AuthorizationAuditListener implements
+ ApplicationListener, ApplicationEventPublisherAware {
+
+ private ApplicationEventPublisher publisher;
+
+ @Override
+ public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
+ this.publisher = publisher;
+ }
+
+ @Override
+ public void onApplicationEvent(AbstractAuthorizationEvent event) {
+ Map data = new HashMap();
+ if (event instanceof AuthenticationCredentialsNotFoundEvent) {
+ data.put("type", ((AuthenticationCredentialsNotFoundEvent) event)
+ .getCredentialsNotFoundException().getClass().getName());
+ data.put("message", ((AuthenticationCredentialsNotFoundEvent) event)
+ .getCredentialsNotFoundException().getMessage());
+ publish(new AuditEvent("", "AUTHENTICATION_FAILURE", data));
+ } else if (event instanceof AuthorizationFailureEvent) {
+ data.put("type", ((AuthorizationFailureEvent) event)
+ .getAccessDeniedException().getClass().getName());
+ data.put("message", ((AuthorizationFailureEvent) event)
+ .getAccessDeniedException().getMessage());
+ publish(new AuditEvent(((AuthorizationFailureEvent) event)
+ .getAuthentication().getName(), "AUTHORIZATION_FAILURE", data));
+ }
+ }
+
+ private void publish(AuditEvent event) {
+ if (this.publisher != null) {
+ this.publisher.publishEvent(new AuditApplicationEvent(event));
+ }
+ }
+
+}
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/trace/SecurityFilterPostProcessor.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/security/SecurityFilterPostProcessor.java
similarity index 96%
rename from spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/trace/SecurityFilterPostProcessor.java
rename to spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/security/SecurityFilterPostProcessor.java
index 6c039e9616..d849581fd4 100644
--- a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/trace/SecurityFilterPostProcessor.java
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/security/SecurityFilterPostProcessor.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.springframework.bootstrap.service.trace;
+package org.springframework.bootstrap.service.security;
import java.io.IOException;
import java.util.Collections;
@@ -35,6 +35,8 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.bootstrap.service.trace.InMemoryTraceRepository;
+import org.springframework.bootstrap.service.trace.TraceRepository;
import org.springframework.core.Ordered;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/trace/InMemoryTraceRepository.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/trace/InMemoryTraceRepository.java
index cf6ab2fb15..ccd72e653e 100644
--- a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/trace/InMemoryTraceRepository.java
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/trace/InMemoryTraceRepository.java
@@ -17,11 +17,10 @@ package org.springframework.bootstrap.service.trace;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Date;
import java.util.List;
import java.util.Map;
-import org.joda.time.DateTime;
-
/**
* @author Dave Syer
*
@@ -48,7 +47,7 @@ public class InMemoryTraceRepository implements TraceRepository {
@Override
public void add(Map map) {
- Trace trace = new Trace(new DateTime(), map);
+ Trace trace = new Trace(new Date(), map);
synchronized (this.traces) {
while (this.traces.size() >= this.capacity) {
this.traces.remove(0);
diff --git a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/trace/Trace.java b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/trace/Trace.java
index 1961843181..98ec693fa8 100644
--- a/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/trace/Trace.java
+++ b/spring-bootstrap-service/src/main/java/org/springframework/bootstrap/service/trace/Trace.java
@@ -15,27 +15,26 @@
*/
package org.springframework.bootstrap.service.trace;
+import java.util.Date;
import java.util.Map;
-import org.joda.time.DateTime;
-
/**
* @author Dave Syer
*
*/
public class Trace {
- private DateTime timestamp;
+ private Date timestamp;
private Map info;
- public Trace(DateTime timestamp, Map info) {
+ public Trace(Date timestamp, Map info) {
super();
this.timestamp = timestamp;
this.info = info;
}
- public DateTime getTimestamp() {
+ public Date getTimestamp() {
return this.timestamp;
}
diff --git a/spring-bootstrap-service/src/test/java/org/springframework/bootstrap/service/trace/SecurityFilterPostProcessorTests.java b/spring-bootstrap-service/src/test/java/org/springframework/bootstrap/service/security/SecurityFilterPostProcessorTests.java
similarity index 81%
rename from spring-bootstrap-service/src/test/java/org/springframework/bootstrap/service/trace/SecurityFilterPostProcessorTests.java
rename to spring-bootstrap-service/src/test/java/org/springframework/bootstrap/service/security/SecurityFilterPostProcessorTests.java
index 617515bd36..71c90179c6 100644
--- a/spring-bootstrap-service/src/test/java/org/springframework/bootstrap/service/trace/SecurityFilterPostProcessorTests.java
+++ b/spring-bootstrap-service/src/test/java/org/springframework/bootstrap/service/security/SecurityFilterPostProcessorTests.java
@@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.springframework.bootstrap.service.trace;
+package org.springframework.bootstrap.service.security;
import java.util.Map;
import org.junit.Test;
-import org.springframework.bootstrap.service.trace.SecurityFilterPostProcessor.WebRequestLoggingFilter;
+import org.springframework.bootstrap.service.security.SecurityFilterPostProcessor;
+import org.springframework.bootstrap.service.security.SecurityFilterPostProcessor.WebRequestLoggingFilter;
+import org.springframework.bootstrap.service.trace.InMemoryTraceRepository;
import org.springframework.mock.web.MockHttpServletRequest;
import static org.junit.Assert.assertEquals;