Search by username in audit logs (#34)

This commit is contained in:
Francesco 2023-11-10 10:20:03 +01:00
parent bdbdd6e8a2
commit 7625462eae
11 changed files with 48 additions and 47 deletions

View File

@ -33,7 +33,6 @@ import jakarta.servlet.http.HttpServletResponse;
import tech.ailef.snapadmin.external.SnapAdmin;
import tech.ailef.snapadmin.external.SnapAdminProperties;
import tech.ailef.snapadmin.external.exceptions.SnapAdminException;
import tech.ailef.snapadmin.external.exceptions.SnapAdminForbiddenException;
import tech.ailef.snapadmin.external.exceptions.SnapAdminNotFoundException;
import tech.ailef.snapadmin.internal.UserConfiguration;

View File

@ -30,6 +30,11 @@ import tech.ailef.snapadmin.external.dbmapping.fields.DbFieldType;
import tech.ailef.snapadmin.external.exceptions.SnapAdminException;
import tech.ailef.snapadmin.external.exceptions.UnsupportedFieldTypeException;
/*
* A class that holds output fields from a user-provided SQL query
* run in the SQL console. If possible, this field is mapped to a proper
* {@link Dbfield} object, otherwise it is left as a raw object.
*/
public class DbQueryOutputField {
private String name;

View File

@ -23,6 +23,10 @@ package tech.ailef.snapadmin.external.dbmapping.query;
import java.util.ArrayList;
import java.util.List;
/**
* A wrapper for results returned by user-provided SQL queries run via
* the SQL console.
*/
public class DbQueryResult {
private List<DbQueryResultRow> rows;

View File

@ -26,6 +26,10 @@ import java.util.Map;
import tech.ailef.snapadmin.external.exceptions.SnapAdminException;
/**
* A single row of results coming from a user-provided SQL query
* run via the SQL console.
*/
public class DbQueryResultRow {
private Map<DbQueryOutputField, Object> values;

View File

@ -65,6 +65,11 @@ public class LogsSearchRequest implements FilterRequest {
*/
private String sortOrder;
/**
* The requested username filter
*/
private String username;
/**
* Returns the table specified in this search request. If the value is blank or 'Any',
* the method returns null in order ignore the field in subsequent queries.
@ -170,6 +175,14 @@ public class LogsSearchRequest implements FilterRequest {
+ page + ", pageSize=" + pageSize + ", sortKey=" + sortKey + ", sortOrder=" + sortOrder + "]";
}
public void setUsername(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
/**
* Build a Spring PageRequest object from the parameters in this request
* @return a Spring PageRequest object

View File

@ -26,6 +26,10 @@ import java.util.Map;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
/**
* Holds information about JPA validation errors occurring during
* creation or editing of items.
*/
public class ValidationErrorsContainer {
private Map<String, List<ConstraintViolation<?>>> errors = new HashMap<>();

View File

@ -1,38 +0,0 @@
/*
* SnapAdmin - An automatically generated CRUD admin UI for Spring Boot apps
* Copyright (C) 2023 Ailef (http://ailef.tech)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package tech.ailef.snapadmin.external.exceptions;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
public class SnapAdminForbiddenException extends ResponseStatusException {
private static final long serialVersionUID = 4090093290330473479L;
public SnapAdminForbiddenException(String message) {
super(HttpStatus.NOT_FOUND, message);
}
@Override
public String getMessage() {
return getReason();
}
}

View File

@ -27,6 +27,6 @@ import tech.ailef.snapadmin.internal.model.UserAction;
public interface CustomActionRepository {
public List<UserAction> findActions(LogsSearchRequest r);
public long countActions(String table, String actionType, String itemId);
public long countActions(LogsSearchRequest request);
}

View File

@ -54,6 +54,7 @@ public class CustomActionRepositoryImpl implements CustomActionRepository {
public List<UserAction> findActions(LogsSearchRequest request) {
String table = request.getTable();
String actionType = request.getActionType();
String username = request.getUsername();
String itemId = request.getItemId();
PageRequest page = request.toPageRequest();
@ -68,6 +69,8 @@ public class CustomActionRepositoryImpl implements CustomActionRepository {
predicates.add(cb.equal(userAction.get("actionType"), actionType));
if (itemId != null)
predicates.add(cb.equal(userAction.get("primaryKey"), itemId));
if (username != null)
predicates.add(cb.equal(userAction.get("username"), username));
if (!predicates.isEmpty()) {
query.select(userAction)
@ -95,12 +98,17 @@ public class CustomActionRepositoryImpl implements CustomActionRepository {
* @return the number of user actions matching the filtering parameters
*/
@Override
public long countActions(String table, String actionType, String itemId) {
public long countActions(LogsSearchRequest request) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> query = cb.createQuery(Long.class);
Root<UserAction> userAction = query.from(UserAction.class);
String table = request.getTable();
String actionType = request.getActionType();
String itemId = request.getItemId();
String username = request.getUsername();
List<Predicate> predicates = new ArrayList<>();
if (table != null)
predicates.add(cb.equal(userAction.get("onTable"), table));
@ -108,6 +116,8 @@ public class CustomActionRepositoryImpl implements CustomActionRepository {
predicates.add(cb.equal(userAction.get("actionType"), actionType));
if (itemId != null)
predicates.add(cb.equal(userAction.get("primaryKey"), itemId));
if (username != null)
predicates.add(cb.equal(userAction.get("username"), username));
if (!predicates.isEmpty()) {
query.select(cb.count(userAction))

View File

@ -60,12 +60,9 @@ public class UserActionService {
* @return a page of results matching the input request
*/
public PaginatedResult<UserAction> findActions(LogsSearchRequest request) {
String table = request.getTable();
String actionType = request.getActionType();
String itemId = request.getItemId();
PageRequest page = request.toPageRequest();
long count = customRepo.countActions(table, actionType, itemId);
long count = customRepo.countActions(request);
List<UserAction> actions = customRepo.findActions(request);
int maxPage = (int)(Math.ceil ((double)count / page.getPageSize()));

View File

@ -44,6 +44,9 @@
<span class="input-group-text ms-3">Item ID</span>
<input type="text" class="form-control" name="itemId"
th:value="${searchRequest.getItemId()}">
<span class="input-group-text ms-3">User</span>
<input type="text" class="form-control" name="username"
th:value="${searchRequest.getUsername()}">
<button class="ui-btn btn btn-primary ms-3">Filter</button>
</div>
</form>