-
Notifications
You must be signed in to change notification settings - Fork 85
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@litvinovg thanks for this PR. It looks nice. However, I have posted three comments. There is also an issue with running tests on Windows. It is throwing an exception with permissions for deletion temporary folders. I have posted exception in a comment.
api/src/test/java/edu/cornell/mannlib/vitro/webapp/audit/storage/AuditDAOTDBTest.java
Outdated
Show resolved
Hide resolved
api/src/main/java/edu/cornell/mannlib/vitro/webapp/rdfservice/impl/ChangeSetImpl.java
Show resolved
Hide resolved
api/src/test/java/edu/cornell/mannlib/vitro/webapp/audit/AuditDaoTDBTest.java
Outdated
Show resolved
Hide resolved
Commenting at Georgy's request: Georgy and I discussed the possibility of refactoring the audit model to use a supplied RDFService instead of having its own triple store implementations. This would allow us to avoid the Windows temp file problems by having the tests use an in-memory triple store and would also allow for users to configure remote/cloud triple stores for their audit data if desired. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@litvinovg well done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed and tested the changes on MacOS M1. Everything appears to be functioning as expected. Great job on the functionality implementation!
The only suggestion I have pertains to the UI design. Additionally, it's important to avoid using StringBuilder or string concatenation for building SPARQL queries due to their susceptibility to injection vulnerabilities.
|
||
section#audit table.history td{ | ||
padding: 2px 5px 2px 5px; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Triplets in a table column cause text overflow on the right side of the screen. This could be solved by enabling text to wrap or by adding horizontal scroll inside the table.
} | |
} | |
section#audit table.history pre{ | |
text-wrap: balance; | |
line-height: normal; | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added additional css rules to improve the view.
Didn't use text-wrap: balance as it is not widely supported yet.
queryString.append(" ?dataset a <").append(AuditVocabulary.TYPE_CHANGESET).append("> . "); | ||
queryString.append(" ?dataset <").append(AuditVocabulary.PROP_DATE).append("> ?date . "); | ||
if (!StringUtils.isBlank(userUri)) { | ||
queryString.append(" ?dataset <").append(AuditVocabulary.PROP_USER).append("> <").append(userUri) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part of the code is vulnerable to SparQL Injection.
For example I could send a filter param for userUri like this
http://vivoweb.org/audit/resource/unknown> %7d LIMIT 999999 #
It might be possible to request any data in db through this. But since this is admin only feature I guess it's not a problem at this moment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for catching that. I think I made it safer now. Please check again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Injection resolved. Great job!
I noticed that everytime admin makes a change his uri is unknown. http://vivoweb.org/audit/resource/unknown Does anyone have simmilar problem? |
…st errors on Windows and the directory will be deleted at some point anyway.
a4c3976
to
e102dc1
Compare
Yes. It can happen as some code still uses raw statement modifications. |
I tested again now, and it works for graph: http://vitro.mannlib.cornell.edu/default/vitro-kb-2 |
* Results object for retrieving entries from the audit store | ||
*/ | ||
public class AuditResults { | ||
private long total; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make these final as they are set in the constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
private long limit; | ||
|
||
private List<AuditChangeSet> datasets; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you provide a private default constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need a private default constructor here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good practice. But I have tested with https://www.jdoodle.com/online-java-compiler/ and confirmed that the default constructor is not able to be called outside of reflection or classpath overlay when a constructor is declared.
private List<AuditChangeSet> datasets; | ||
|
||
public AuditResults(long total, long offset, long limit, List<AuditChangeSet> datasets) { | ||
this.total = total; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Call default constructor before setting class members.
private static final Log log = LogFactory.getLog(AuditController.class); | ||
|
||
// Template for the UI | ||
private static final String TEMPLATE_DEFAULT = "auditHistory.ftl"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these constants or their matching literal in use anywhere else? I may be ideal for package level constant files to avoid building up the static memory across the application.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, TEMPLATE_DEFAULT used only in this class.
} | ||
// Get the offset parameter (or default if unset) | ||
int offset = getOffset(vreq); | ||
body.put("offset", offset); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use the string literal when the constant is above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
offset = Integer.parseInt(vreq.getParameter(PARAM_OFFSET)); | ||
} catch (Throwable e) { | ||
log.debug(e, e); | ||
offset = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a redundant reassignment of offset variable as if the exception was thrown it would not of reassigned in the try block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
try { | ||
if (!StringUtils.isBlank(end)) { | ||
Date endDate = sdf.parse(end); | ||
return endDate; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just return the response to the parse method call instead of temporarily allocating memory for garbage cleanup?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
// We expect two arguments | ||
// 1 - an AuditChangeSet | ||
// 2 - a graph URI | ||
if (arguments.size() == 2) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@litvinovg well done.
VIVO GitHub issue 1
VIVO GitHub issue 2
VIVO PR
What does this pull request do?
The pull request adds a module that enables tracking of changes being made in the triple store by users and non-person entitites.
Changes are recorded in a triple store, with the users ID (URI), the time, and the changes that have been made.
A user interface (/audit) is also provided that, when logged in, displays the changes.
What's new?
Adds a new AuditModule application module
TDBAuditModule can be configured / enabled via the applicationSetup.n3
RDFService change listener capture any changes made to the content and configuration stores, and record those changes in a dedicated audit store.
Tests included
How should this be tested?
Supersedes
#369
#81
Interested parties
@chenejac @brianjlowe