[bs-15] Add audit abstraction and sensible opinionated defaults
* Added AuditEvent and AuditEventRepository * Also AuditApplicationEvent and AuditListener for handling AUditEvents as Spring ApplicationEvents [Fixes #48155753]pull/1/merge
parent
cee78386ee
commit
a310a79909
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* 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.audit;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A value object representing an audit event: at a particular time, a particular user or
|
||||||
|
* agent carried out an action of a particular type. This object records the details of
|
||||||
|
* such an event.
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AuditEvent {
|
||||||
|
|
||||||
|
final private Date timestamp;
|
||||||
|
final private String principal;
|
||||||
|
final private String type;
|
||||||
|
final private Map<String, Object> data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new audit event for the current time from data provided as name-value
|
||||||
|
* pairs
|
||||||
|
*/
|
||||||
|
public AuditEvent(String principal, String type, String... data) {
|
||||||
|
this(new Date(), principal, type, convert(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new audit event for the current time
|
||||||
|
*/
|
||||||
|
public AuditEvent(String principal, String type, Map<String, Object> data) {
|
||||||
|
this(new Date(), principal, type, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new audit event.
|
||||||
|
*/
|
||||||
|
public AuditEvent(Date timestamp, String principal, String type,
|
||||||
|
Map<String, Object> data) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.principal = principal;
|
||||||
|
this.type = type;
|
||||||
|
this.data = Collections.unmodifiableMap(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getTimestamp() {
|
||||||
|
return this.timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrincipal() {
|
||||||
|
return this.principal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getData() {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, Object> convert(String[] data) {
|
||||||
|
Map<String, Object> result = new HashMap<String, Object>();
|
||||||
|
for (String entry : data) {
|
||||||
|
if (entry.contains("=")) {
|
||||||
|
int index = entry.indexOf("=");
|
||||||
|
result.put(entry.substring(0, index), entry.substring(index + 1));
|
||||||
|
} else {
|
||||||
|
result.put(entry, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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.audit;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface AuditEventRepository {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find audit events relating to the specified principal since the time provided.
|
||||||
|
*
|
||||||
|
* @param principal the principal name to search for
|
||||||
|
* @param after timestamp of earliest result required
|
||||||
|
* @return audit events relating to the principal
|
||||||
|
*/
|
||||||
|
List<AuditEvent> find(String principal, Date after);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log an event.
|
||||||
|
*
|
||||||
|
* @param event the audit event to log
|
||||||
|
*/
|
||||||
|
void add(AuditEvent event);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* 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.audit;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class InMemoryAuditEventRepository implements AuditEventRepository {
|
||||||
|
|
||||||
|
private int capacity = 100;
|
||||||
|
|
||||||
|
private Map<String, List<AuditEvent>> events = new HashMap<String, List<AuditEvent>>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param capacity the capacity to set
|
||||||
|
*/
|
||||||
|
public void setCapacity(int capacity) {
|
||||||
|
this.capacity = capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AuditEvent> find(String principal, Date after) {
|
||||||
|
synchronized (this.events) {
|
||||||
|
return Collections.unmodifiableList(getEvents(principal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AuditEvent> getEvents(String principal) {
|
||||||
|
if (!this.events.containsKey(principal)) {
|
||||||
|
this.events.put(principal, new ArrayList<AuditEvent>());
|
||||||
|
}
|
||||||
|
return this.events.get(principal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(AuditEvent event) {
|
||||||
|
synchronized (this.events) {
|
||||||
|
List<AuditEvent> list = getEvents(event.getPrincipal());
|
||||||
|
while (list.size() >= this.capacity) {
|
||||||
|
list.remove(0);
|
||||||
|
}
|
||||||
|
list.add(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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.audit.listener;
|
||||||
|
|
||||||
|
import org.springframework.bootstrap.service.audit.AuditEvent;
|
||||||
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AuditApplicationEvent extends ApplicationEvent {
|
||||||
|
|
||||||
|
private AuditEvent auditEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param auditEvent the source of this event
|
||||||
|
*/
|
||||||
|
public AuditApplicationEvent(AuditEvent auditEvent) {
|
||||||
|
super(auditEvent);
|
||||||
|
this.auditEvent = auditEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the audit event
|
||||||
|
*/
|
||||||
|
public AuditEvent getAuditEvent() {
|
||||||
|
return this.auditEvent;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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.audit.listener;
|
||||||
|
|
||||||
|
import org.springframework.bootstrap.service.audit.AuditEventRepository;
|
||||||
|
import org.springframework.context.ApplicationListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AuditListener implements ApplicationListener<AuditApplicationEvent> {
|
||||||
|
|
||||||
|
private final AuditEventRepository auditEventRepository;
|
||||||
|
|
||||||
|
public AuditListener(AuditEventRepository auditEventRepository) {
|
||||||
|
this.auditEventRepository = auditEventRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onApplicationEvent(AuditApplicationEvent event) {
|
||||||
|
this.auditEventRepository.add(event.getAuditEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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.audit;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AuditEventTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNowEvent() throws Exception {
|
||||||
|
AuditEvent event = new AuditEvent("phil", "UNKNOWN", Collections.singletonMap(
|
||||||
|
"a", (Object) "b"));
|
||||||
|
assertEquals("b", event.getData().get("a"));
|
||||||
|
assertEquals("UNKNOWN", event.getType());
|
||||||
|
assertEquals("phil", event.getPrincipal());
|
||||||
|
assertNotNull(event.getTimestamp());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConvertStringsToData() throws Exception {
|
||||||
|
AuditEvent event = new AuditEvent("phil", "UNKNOWN", "a=b", "c=d");
|
||||||
|
assertEquals("b", event.getData().get("a"));
|
||||||
|
assertEquals("d", event.getData().get("c"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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.audit;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class InMemoryAuditEventRepositoryTests {
|
||||||
|
|
||||||
|
private InMemoryAuditEventRepository repository = new InMemoryAuditEventRepository();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddToCapacity() throws Exception {
|
||||||
|
this.repository.setCapacity(2);
|
||||||
|
this.repository.add(new AuditEvent("phil", "UNKNOWN"));
|
||||||
|
this.repository.add(new AuditEvent("phil", "UNKNOWN"));
|
||||||
|
this.repository.add(new AuditEvent("dave", "UNKNOWN"));
|
||||||
|
this.repository.add(new AuditEvent("dave", "UNKNOWN"));
|
||||||
|
this.repository.add(new AuditEvent("phil", "UNKNOWN"));
|
||||||
|
assertEquals(2, this.repository.find("phil", new Date(0L)).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue