Skip to content

Commit 301c6f3

Browse files
authored
Merge pull request DSpace#11072 from 4Science/task/main/DURACOM-317
Rest: Audit Trail feature
2 parents 7bc43d1 + d35ad39 commit 301c6f3

64 files changed

Lines changed: 4070 additions & 56 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ services:
130130
cp -r /opt/solr/server/solr/configsets/qaevent/* qaevent
131131
precreate-core suggestion /opt/solr/server/solr/configsets/suggestion
132132
cp -r /opt/solr/server/solr/configsets/suggestion/* suggestion
133+
precreate-core audit /opt/solr/server/solr/configsets/audit
134+
cp -r /opt/solr/server/solr/configsets/audit/* audit
133135
chown -R solr:solr /var/solr
134136
runuser -u solr -- solr-foreground
135137
volumes:

dspace-api/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,10 @@
582582
<groupId>org.antlr</groupId>
583583
<artifactId>antlr4-runtime</artifactId>
584584
</exclusion>
585+
<exclusion>
586+
<groupId>org.apache.logging.log4j</groupId>
587+
<artifactId>log4j-slf4j-impl</artifactId>
588+
</exclusion>
585589
</exclusions>
586590
</dependency>
587591
<dependency>
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/**
2+
* The contents of this file are subject to the license and copyright
3+
* detailed in the LICENSE and NOTICE files at the root of the source
4+
* tree and available online at
5+
*
6+
* http://www.dspace.org/license/
7+
*/
8+
package org.dspace.app.audit;
9+
10+
import java.util.List;
11+
import java.util.UUID;
12+
13+
import org.dspace.app.audit.factory.AuditServiceFactory;
14+
import org.dspace.core.Context;
15+
import org.dspace.event.Consumer;
16+
import org.dspace.event.Event;
17+
import org.dspace.services.ConfigurationService;
18+
import org.dspace.services.factory.DSpaceServicesFactory;
19+
20+
/**
21+
* Class to store all received events in the audit system, if auditing is enabled.
22+
*
23+
* @author Andrea Bollini (andrea.bollini at 4science.it)
24+
* @author Stefano Maffei (stefano.maffei at 4science.com)
25+
*/
26+
27+
public class AuditConsumer implements Consumer {
28+
private AuditService auditService;
29+
private ConfigurationService configurationService;
30+
private List<Integer> meaningfulEvents;
31+
32+
33+
public void initialize() throws Exception {
34+
auditService = AuditServiceFactory.getInstance().getAuditService();
35+
configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
36+
meaningfulEvents = List.of(Event.MODIFY_METADATA, Event.CREATE, Event.DELETE,
37+
Event.REMOVE);
38+
}
39+
40+
/**
41+
* Consume a content event
42+
*
43+
* @param ctx DSpace context
44+
* @param event Content event
45+
*/
46+
@Override
47+
public void consume(Context ctx, Event event) throws Exception {
48+
if (configurationService.getBooleanProperty("audit.enabled", false)
49+
&& isEventMeaningful(event)) {
50+
auditService.store(ctx, event); // AuditService also handles detailed event logging
51+
}
52+
}
53+
54+
/**
55+
* Checks if the given event is meaningful for audit purposes.
56+
* An event is considered meaningful if its type is present in the meaningfulEvents list,
57+
* or if it has a non-null related object ID.
58+
* Some events, may not be in the meaningfulEvents list, eighter because they contain
59+
* duplicated information or because they are not relevant for auditing.
60+
*
61+
* @param event the event to check
62+
* @return true if the event is meaningful, false otherwise
63+
*/
64+
private boolean isEventMeaningful(Event event) {
65+
if (meaningfulEvents.contains(event.getEventType())) {
66+
return true;
67+
}
68+
UUID relatedObjectId = event.getObjectID();
69+
return relatedObjectId != null;
70+
}
71+
72+
@Override
73+
public void end(Context ctx) throws Exception {
74+
// no-op
75+
}
76+
77+
@Override
78+
public void finish(Context ctx) throws Exception {
79+
// No-op
80+
}
81+
82+
}
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
/**
2+
* The contents of this file are subject to the license and copyright
3+
* detailed in the LICENSE and NOTICE files at the root of the source
4+
* tree and available online at
5+
*
6+
* http://www.dspace.org/license/
7+
*/
8+
package org.dspace.app.audit;
9+
10+
import java.util.Date;
11+
import java.util.UUID;
12+
13+
14+
/**
15+
* @author Andrea Bollini (andrea.bollini at 4science.it)
16+
* @author Stefano Maffei (stefano.maffei at 4science.com)
17+
*/
18+
public class AuditEvent {
19+
private UUID uuid;
20+
private UUID epersonUUID;
21+
private UUID objectUUID;
22+
private String objectType;
23+
private UUID subjectUUID;
24+
private String subjectType;
25+
private String eventType;
26+
private Date timeStamp;
27+
private String detail;
28+
private String metadataField;
29+
private String value;
30+
private String authority;
31+
private Integer confidence;
32+
private Integer place;
33+
private String action;
34+
private String checksum;
35+
36+
private final static String LOG_FIELD_SEPARATOR = " || ";
37+
38+
/**
39+
* @return the uuid of the audit event
40+
**/
41+
public UUID getUuid() {
42+
return uuid;
43+
}
44+
45+
public void setUuid(UUID uuid) {
46+
this.uuid = uuid;
47+
}
48+
49+
/**
50+
* @return the uuid of the eperson who generates event
51+
*/
52+
public UUID getEpersonUUID() {
53+
return epersonUUID;
54+
}
55+
56+
public void setEpersonUUID(UUID epersonUUID) {
57+
this.epersonUUID = epersonUUID;
58+
}
59+
60+
/**
61+
* @return the uuid of the object involved in the event
62+
*/
63+
public UUID getObjectUUID() {
64+
return objectUUID;
65+
}
66+
67+
public void setObjectUUID(UUID objectUUID) {
68+
this.objectUUID = objectUUID;
69+
}
70+
71+
/**
72+
* @return the type of the object involved in the event
73+
*/
74+
public String getObjectType() {
75+
return objectType;
76+
}
77+
78+
public void setObjectType(String objectType) {
79+
this.objectType = objectType;
80+
}
81+
82+
/**
83+
* @return the uuid of the subject involved in the event
84+
*/
85+
public UUID getSubjectUUID() {
86+
return subjectUUID;
87+
}
88+
89+
public void setSubjectUUID(UUID subjectUUID) {
90+
this.subjectUUID = subjectUUID;
91+
}
92+
93+
/**
94+
* @return the type of the subject involved in the event e.g. ITEM, COLLECTION, COMMUNITY
95+
*/
96+
public String getSubjectType() {
97+
return subjectType;
98+
}
99+
100+
public void setSubjectType(String subjectType) {
101+
this.subjectType = subjectType;
102+
}
103+
104+
/**
105+
* @return the type of event e.g. CREATE, MODIFY_METADATA, DELETE
106+
*/
107+
public String getEventType() {
108+
return eventType;
109+
}
110+
111+
public void setEventType(String changetype) {
112+
this.eventType = changetype;
113+
}
114+
115+
/**
116+
* @return the date and time of the event
117+
*/
118+
public Date getDatetime() {
119+
return timeStamp;
120+
}
121+
122+
public void setDatetime(Date datetime) {
123+
this.timeStamp = datetime;
124+
}
125+
126+
/**
127+
* @return additional detail about the event
128+
*/
129+
public String getDetail() {
130+
return detail;
131+
}
132+
133+
public void setDetail(String detail) {
134+
this.detail = detail;
135+
}
136+
137+
/**
138+
* @return the metadata field involved in the event e.g. dc.contributor.author
139+
*/
140+
public String getMetadataField() {
141+
return metadataField;
142+
}
143+
144+
public void setMetadataField(String metadataField) {
145+
this.metadataField = metadataField;
146+
}
147+
148+
/**
149+
* @return the metadata value involved in the event e.g. "Smith, John"
150+
*/
151+
public String getValue() {
152+
return value;
153+
}
154+
155+
public void setValue(String value) {
156+
this.value = value;
157+
}
158+
159+
/**
160+
* @return the authority key of the metadata value involved in the event
161+
*/
162+
public String getAuthority() {
163+
return authority;
164+
}
165+
166+
public void setAuthority(String authority) {
167+
this.authority = authority;
168+
}
169+
170+
/**
171+
* @return the confidence level of the metadata value involved in the event
172+
*/
173+
public Integer getConfidence() {
174+
return confidence;
175+
}
176+
177+
public void setConfidence(Integer confidence) {
178+
this.confidence = confidence;
179+
}
180+
181+
/**
182+
* @return the place of the metadata value involved in the event e.g. 0
183+
*/
184+
public Integer getPlace() {
185+
return place;
186+
}
187+
188+
public void setPlace(Integer place) {
189+
this.place = place;
190+
}
191+
192+
/**
193+
* @return the action performed on the metadata value involved in the event e.g. ADD, REMOVE ...
194+
*/
195+
public String getAction() {
196+
return action;
197+
}
198+
199+
public void setAction(String action) {
200+
this.action = action;
201+
}
202+
203+
/**
204+
* @return the checksum of the bitstream involved in the event
205+
*/
206+
public String getChecksum() {
207+
return checksum;
208+
}
209+
210+
public void setChecksum(String checksum) {
211+
this.checksum = checksum;
212+
}
213+
214+
@Override
215+
public String toString() {
216+
StringBuilder sb = new StringBuilder();
217+
sb.append("AUDIT_EVENT")
218+
.append(LOG_FIELD_SEPARATOR).append("uuid=").append(nullSafe(getUuid()))
219+
.append(LOG_FIELD_SEPARATOR).append("eventType=").append(nullSafe(getEventType()))
220+
.append(LOG_FIELD_SEPARATOR).append("subjectUUID=").append(nullSafe(getSubjectUUID()))
221+
.append(LOG_FIELD_SEPARATOR).append("subjectType=").append(nullSafe(getSubjectType()))
222+
.append(LOG_FIELD_SEPARATOR).append("objectUUID=").append(nullSafe(getObjectUUID()))
223+
.append(LOG_FIELD_SEPARATOR).append("objectType=").append(nullSafe(getObjectType()))
224+
.append(LOG_FIELD_SEPARATOR).append("metadataField=").append(nullSafe(getMetadataField()))
225+
.append(LOG_FIELD_SEPARATOR).append("value=").append(nullSafe(getValue()))
226+
.append(LOG_FIELD_SEPARATOR).append("authority=").append(nullSafe(getAuthority()))
227+
.append(LOG_FIELD_SEPARATOR).append("confidence=").append(nullSafe(getConfidence()))
228+
.append(LOG_FIELD_SEPARATOR).append("place=").append(nullSafe(getPlace()))
229+
.append(LOG_FIELD_SEPARATOR).append("action=").append(nullSafe(getAction()))
230+
.append(LOG_FIELD_SEPARATOR).append("checksum=").append(nullSafe(getChecksum()))
231+
.append(LOG_FIELD_SEPARATOR).append("detail=").append(nullSafe(getDetail()))
232+
.append(LOG_FIELD_SEPARATOR).append("datetime=").append(getDatetime() == null ?
233+
"null" : String.valueOf(getDatetime().getTime()))
234+
.append(LOG_FIELD_SEPARATOR).append("epersonUUID=").append(nullSafe(getEpersonUUID()));
235+
return sb.toString();
236+
}
237+
238+
/**
239+
* Utility to avoid NPEs in log lines.
240+
*/
241+
private String nullSafe(Object o) {
242+
return o == null ? "" : o.toString();
243+
}
244+
}

0 commit comments

Comments
 (0)