Commit 51762642 authored by Phillip Webb's avatar Phillip Webb

Polish audit event endpoint support

Closes gh-6579
parent 5b40eb48
...@@ -34,6 +34,7 @@ import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; ...@@ -34,6 +34,7 @@ import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.lang.UsesJava8;
// Flyway must go first // Flyway must go first
@SpringBootApplication @SpringBootApplication
...@@ -59,12 +60,16 @@ public class SpringBootHypermediaApplication implements CommandLineRunner { ...@@ -59,12 +60,16 @@ public class SpringBootHypermediaApplication implements CommandLineRunner {
@Override @Override
public void run(String... args) throws Exception { public void run(String... args) throws Exception {
this.auditEventRepository.add(new AuditEvent(Date.from(Instant.parse( this.auditEventRepository.add(
"2016-11-01T11:00:00Z")), "user", "AUTHENTICATION_FAILURE", createEvent("2016-11-01T11:00:00Z", "user", "AUTHENTICATION_FAILURE"));
Collections.emptyMap())); this.auditEventRepository.add(
this.auditEventRepository.add(new AuditEvent(Date.from(Instant.parse( createEvent("2016-11-01T12:00:00Z", "admin", "AUTHENTICATION_SUCCESS"));
"2016-11-01T12:00:00Z")), "admin", "AUTHENTICATION_SUCCESS", }
Collections.emptyMap()));
@UsesJava8
private AuditEvent createEvent(String instant, String principal, String type) {
return new AuditEvent(Date.from(Instant.parse(instant)), principal, type,
Collections.<String, Object>emptyMap());
} }
} }
...@@ -17,7 +17,8 @@ ...@@ -17,7 +17,8 @@
package org.springframework.boot.actuate.endpoint.mvc; package org.springframework.boot.actuate.endpoint.mvc;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.actuate.audit.AuditEvent; import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.boot.actuate.audit.AuditEventRepository; import org.springframework.boot.actuate.audit.AuditEventRepository;
...@@ -34,6 +35,7 @@ import org.springframework.web.bind.annotation.ResponseBody; ...@@ -34,6 +35,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
* {@link MvcEndpoint} to expose {@link AuditEvent}s. * {@link MvcEndpoint} to expose {@link AuditEvent}s.
* *
* @author Vedran Pavic * @author Vedran Pavic
* @author Phillip Webb
* @since 1.5.0 * @since 1.5.0
*/ */
@ConfigurationProperties(prefix = "endpoints.auditevents") @ConfigurationProperties(prefix = "endpoints.auditevents")
...@@ -47,41 +49,18 @@ public class AuditEventsMvcEndpoint extends AbstractNamedMvcEndpoint { ...@@ -47,41 +49,18 @@ public class AuditEventsMvcEndpoint extends AbstractNamedMvcEndpoint {
this.auditEventRepository = auditEventRepository; this.auditEventRepository = auditEventRepository;
} }
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE, params = { "after" }) @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody @ResponseBody
public ResponseEntity<?> findByAfter( public ResponseEntity<?> findByPrincipalAndAfterAndType(
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssZ") Date after) { @RequestParam(required = false) String principal,
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssZ") Date after,
@RequestParam(required = false) String type) {
if (!isEnabled()) { if (!isEnabled()) {
return DISABLED_RESPONSE; return DISABLED_RESPONSE;
} }
List<AuditEvent> auditEvents = this.auditEventRepository.find(after); Map<Object, Object> result = new LinkedHashMap<Object, Object>();
return ResponseEntity.ok(auditEvents); result.put("events", this.auditEventRepository.find(principal, after, type));
} return ResponseEntity.ok(result);
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE,
params = { "principal", "after" })
@ResponseBody
public ResponseEntity<?> findByPrincipalAndAfter(@RequestParam String principal,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssZ") Date after) {
if (!isEnabled()) {
return DISABLED_RESPONSE;
}
List<AuditEvent> auditEvents = this.auditEventRepository.find(principal, after);
return ResponseEntity.ok(auditEvents);
}
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE,
params = { "principal", "after", "type" })
@ResponseBody
public ResponseEntity<?> findByPrincipalAndAfterAndType(@RequestParam String principal,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssZ") Date after,
@RequestParam String type) {
if (!isEnabled()) {
return DISABLED_RESPONSE;
}
List<AuditEvent> auditEvents = this.auditEventRepository.find(principal, after,
type);
return ResponseEntity.ok(auditEvents);
} }
} }
...@@ -109,8 +109,7 @@ public class EndpointMvcIntegrationTests { ...@@ -109,8 +109,7 @@ public class EndpointMvcIntegrationTests {
private final List<HttpMessageConverter<?>> converters; private final List<HttpMessageConverter<?>> converters;
public Application( public Application(ObjectProvider<List<HttpMessageConverter<?>>> converters) {
ObjectProvider<List<HttpMessageConverter<?>>> converters) {
this.converters = converters.getIfAvailable(); this.converters = converters.getIfAvailable();
} }
......
...@@ -37,6 +37,7 @@ import org.springframework.boot.test.context.SpringBootTest; ...@@ -37,6 +37,7 @@ import org.springframework.boot.test.context.SpringBootTest;
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.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
...@@ -55,6 +56,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. ...@@ -55,6 +56,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*/ */
@SpringBootTest @SpringBootTest
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@TestPropertySource(properties = "management.security.enabled=false")
public class AuditEventsMvcEndpointTests { public class AuditEventsMvcEndpointTests {
@Autowired @Autowired
...@@ -78,26 +80,27 @@ public class AuditEventsMvcEndpointTests { ...@@ -78,26 +80,27 @@ public class AuditEventsMvcEndpointTests {
@Test @Test
public void invokeFilterByDateAfter() throws Exception { public void invokeFilterByDateAfter() throws Exception {
this.mvc.perform(get("/auditevents").param("after", "2016-11-01T13:00:00+0000")) this.mvc.perform(get("/auditevents").param("after", "2016-11-01T13:00:00+0000"))
.andExpect(status().isOk()).andExpect(content().string("[]")); .andExpect(status().isOk())
.andExpect(content().string("{\"events\":[]}"));
} }
@Test @Test
public void invokeFilterByPrincipalAndDateAfter() throws Exception { public void invokeFilterByPrincipalAndDateAfter() throws Exception {
this.mvc.perform(get("/auditevents").param("principal", "user") this.mvc.perform(get("/auditevents").param("principal", "user").param("after",
.param("after", "2016-11-01T10:00:00+0000")) "2016-11-01T10:00:00+0000"))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().string(containsString( .andExpect(content().string(
"\"principal\":\"user\",\"type\":\"login\""))) containsString("\"principal\":\"user\",\"type\":\"login\"")))
.andExpect(content().string(not(containsString("admin")))); .andExpect(content().string(not(containsString("admin"))));
} }
@Test @Test
public void invokeFilterByPrincipalAndDateAfterAndType() throws Exception { public void invokeFilterByPrincipalAndDateAfterAndType() throws Exception {
this.mvc.perform(get("/auditevents").param("principal", "admin") this.mvc.perform(get("/auditevents").param("principal", "admin")
.param("after", "2016-11-01T10:00:00+0000") .param("after", "2016-11-01T10:00:00+0000").param("type", "logout"))
.param("type", "logout")).andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().string(containsString( .andExpect(content().string(
"\"principal\":\"admin\",\"type\":\"logout\""))) containsString("\"principal\":\"admin\",\"type\":\"logout\"")))
.andExpect(content().string(not(containsString("login")))); .andExpect(content().string(not(containsString("login"))));
} }
...@@ -111,15 +114,17 @@ public class AuditEventsMvcEndpointTests { ...@@ -111,15 +114,17 @@ public class AuditEventsMvcEndpointTests {
@Bean @Bean
public AuditEventRepository auditEventsRepository() { public AuditEventRepository auditEventsRepository() {
AuditEventRepository repository = new InMemoryAuditEventRepository(3); AuditEventRepository repository = new InMemoryAuditEventRepository(3);
repository.add(new AuditEvent(Date.from(Instant.parse( repository.add(createEvent("2016-11-01T11:00:00Z", "admin", "login"));
"2016-11-01T11:00:00Z")), "admin", "login", Collections.emptyMap())); repository.add(createEvent("2016-11-01T12:00:00Z", "admin", "logout"));
repository.add(new AuditEvent(Date.from(Instant.parse( repository.add(createEvent("2016-11-01T12:00:00Z", "user", "login"));
"2016-11-01T12:00:00Z")), "admin", "logout", Collections.emptyMap()));
repository.add(new AuditEvent(Date.from(Instant.parse(
"2016-11-01T12:00:00Z")), "user", "login", Collections.emptyMap()));
return repository; return repository;
} }
private AuditEvent createEvent(String instant, String principal, String type) {
return new AuditEvent(Date.from(Instant.parse(instant)), principal, type,
Collections.<String, Object>emptyMap());
}
} }
} }
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