Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audit module (Change Tracking) #390

Merged
merged 24 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9794e55
merged changeset_user_info
litvinovg May 12, 2023
edf94db
changed default graph uri
litvinovg May 12, 2023
6d7fe36
audit code
litvinovg May 12, 2023
d8de8ae
Modified audit controller
litvinovg May 12, 2023
d7c0dc5
Audit controller added user information
litvinovg May 12, 2023
edeef81
Add listener to RDFService configuration models
litvinovg May 12, 2023
d44b1f3
added some filters for audit history
litvinovg May 15, 2023
b93db9e
More filtering options for audit controller
litvinovg May 15, 2023
591db33
added user id in audit history table
litvinovg May 16, 2023
5aa66e5
Allow only admins to access audit page
litvinovg May 16, 2023
b92aa85
fix for prev commit
litvinovg May 16, 2023
6bc75f7
Set default end date to be the next day to avoid empty results
litvinovg May 16, 2023
b8b280b
Removed not implemented AuditDaoFS implementation, added tests for Au…
litvinovg May 19, 2023
388ae28
added example configuration
litvinovg May 19, 2023
29d319a
Fixed some typos
litvinovg May 19, 2023
3446c27
Don't show empty changes on audit history page
litvinovg May 22, 2023
b238eae
fix: release dataset before close
litvinovg Aug 24, 2023
4292ffb
remove empty test file
litvinovg Aug 24, 2023
290373d
chore: fixed formatting
litvinovg Aug 24, 2023
6db6aa7
Don't delete TDB dataset from temporary directory as it results in te…
litvinovg Aug 24, 2023
6060ba8
refactored and improved sparql queries
litvinovg Dec 14, 2023
e102dc1
style fixes
litvinovg Dec 14, 2023
1eb38b5
fixes
litvinovg Dec 21, 2023
2d7621e
one more fix
litvinovg Dec 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import edu.cornell.mannlib.vitro.webapp.audit.AuditModule;
import org.apache.jena.ontology.OntDocumentManager;

import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess;
Expand Down Expand Up @@ -41,6 +42,7 @@ public class ApplicationImpl implements Application {
private SearchIndexer searchIndexer;
private ImageProcessor imageProcessor;
private FileStorage fileStorage;
private AuditModule auditModule;
private ContentTripleSource contentTripleSource;
private ConfigurationTripleSource configurationTripleSource;
private TBoxReasonerModule tboxReasonerModule;
Expand Down Expand Up @@ -103,6 +105,15 @@ public void setFileStorage(FileStorage fs) {
fileStorage = fs;
}

@Override
public AuditModule getAuditModule() {
return auditModule;
}

@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasAuditModule", minOccurs = 0, maxOccurs = 1)
public void setAuditModule(AuditModule am) {
auditModule = am;
}
@Override
public ContentTripleSource getContentTripleSource() {
return contentTripleSource;
Expand Down Expand Up @@ -165,6 +176,11 @@ public void contextInitialized(ServletContextEvent sce) {
fileStorage.startup(app, css);
ss.info(this, "Started the FileStorage system: " + fileStorage);

AuditModule auditModule = app.getAuditModule();
if (auditModule != null) {
auditModule.startup(app, css);
}

ContentTripleSource contentTripleSource = app
.getContentTripleSource();
contentTripleSource.startup(app, css);
Expand Down Expand Up @@ -213,6 +229,11 @@ public void contextDestroyed(ServletContextEvent sce) {
app.getFileStorage().shutdown(app);
app.getImageProcessor().shutdown(app);
app.getSearchEngine().shutdown(app);

AuditModule auditModule = app.getAuditModule();
if (auditModule != null) {
auditModule.shutdown(app);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* $This file is distributed under the terms of the license in LICENSE$ */

package edu.cornell.mannlib.vitro.webapp.audit;

import java.util.List;

import freemarker.ext.beans.StringModel;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModelException;

/**
* Base helper method for Freemarker
*/
public abstract class AbstractListStatementsMethod implements TemplateMethodModelEx {
@Override
public Object exec(List arguments) throws TemplateModelException {
// We expect two arguments
// 1 - an AuditChangeSet
// 2 - a graph URI
if (arguments.size() == 2) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to require a null check.

A Freemarker SimpleMethodModel
https://github.com/litvinovg/Vitro/blob/c0b48511e2b7b4446b90a4096ed19de3bae6d7bd/api/src/main/java/freemarker/ext/dump/BaseDumpDirective.java#L433C25-L433C43 calling with null. May be a red herring.

However, the API does not indicate the argument to be non-null.

https://freemarker.apache.org/docs/api/freemarker/ext/beans/SimpleMethodModel.html#exec-java.util.List-

Object arg1 = arguments.get(0);
Object arg2 = arguments.get(1);

// This looks odd, but the AuditChangeSet is wrapped in a StringModel
if (arg1 instanceof StringModel) {
arg1 = ((StringModel) arg1).getWrappedObject();
}

if (arg1 instanceof AuditChangeSet && arg2 instanceof SimpleScalar) {
AuditChangeSet dataset = (AuditChangeSet) arg1;
String graphUri = ((SimpleScalar) arg2).getAsString();

// Get the statements from the changeset for the named graph
return getStatements(dataset, graphUri);
}
}

throw new TemplateModelException("Wrong arguments");
}

/**
* Abstract method to be implemented for Added / Removed statements
*
* @param dataset
* @param graphUri
* @return
*/
protected abstract Object getStatements(AuditChangeSet dataset, String graphUri);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* $This file is distributed under the terms of the license in LICENSE$ */

package edu.cornell.mannlib.vitro.webapp.audit;

import edu.cornell.mannlib.vitro.webapp.audit.storage.AuditDAOFactory;
import edu.cornell.mannlib.vitro.webapp.audit.storage.AuditVocabulary;
import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeListener;
import edu.cornell.mannlib.vitro.webapp.rdfservice.ModelChange;
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jena.rdf.listeners.StatementListener;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelChangedListener;

/**
* Listener for changes in the RDFService
*/
public class AuditChangeListener extends StatementListener implements ModelChangedListener, ChangeListener {
private static final Log log = LogFactory.getLog(AuditChangeListener.class);

@Override
public void notifyModelChange(ModelChange modelChange) {

// Convert the serialized statements into a Jena Model
Model changes =
RDFServiceUtils.parseModel(modelChange.getSerializedModel(), modelChange.getSerializationFormat());

// Get the changeset for the current request
AuditChangeSet auditChangeset = new AuditChangeSet();
Model additions = auditChangeset.getAddedModel(modelChange.getGraphURI());

String userId = modelChange.getUserId();
if (StringUtils.isBlank(userId)) {
Exception e = new Exception();
log.debug("User id is not provided.", e);
userId = AuditVocabulary.RESOURCE_UNKNOWN;
}
auditChangeset.setUserId(userId);

// Is the change adding or removing statements?
if (modelChange.getOperation() == ModelChange.Operation.REMOVE) {
// If we are removing statements, make sure we don't retain them in the additions
additions.remove(changes);

// Record all of the changes in the Model of removed statements
Model removed = auditChangeset.getRemovedModel(modelChange.getGraphURI());
removed.add(changes);
} else {
// Record all of the changes in the Model of added statements
additions.add(changes);
}
if (!auditChangeset.isEmpty()) {
// Write the changes to the audit store
AuditDAOFactory.getAuditDAO().write(auditChangeset);
}
}

@Override
public void notifyEvent(String graphURI, Object event) {
}
}
Loading