blob: 9a17404a20421712547eb6716640a8a0d1e43978 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.fineract.commands.api;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriInfo;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.commands.data.AuditData;
import org.apache.fineract.commands.data.AuditSearchData;
import org.apache.fineract.commands.service.AuditReadPlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.PaginationParameters;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.infrastructure.security.service.SqlValidator;
import org.apache.fineract.infrastructure.security.utils.SQLBuilder;
import org.springframework.stereotype.Component;
@Path("/v1/audits")
@Component
@Tag(name = "Audits", description = "Every non-read Mifos API request is audited. A fully processed request can not be changed or deleted. See maker checker api for situations where an audit is not fully processed.\n"
+ "\n"
+ "Permissions: To search and look at audit entries a user needs to be attached to a role that has one of the ALL_FUNCTIONS, ALL_FUNCTIONS_READ or READ_AUDIT permissions.\n"
+ "\n"
+ "Data Scope: A user can only see audits that are within their data scope. However, 'head office' users can see all audits including those that aren't office/branch related e.g. Loan Product changes.\")")
@RequiredArgsConstructor
public class AuditsApiResource {
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList("id", "actionName", "entityName", "resourceId",
"subresourceId", "maker", "madeOnDate", "checker", "checkedOnDate", "processingResult", "commandAsJson", "officeName",
"groupLevelName", "groupName", "clientName", "loanAccountNo", "savingsAccountNo", "clientId", "loanId", "url"));
private static final String RESOURCE_NAME_FOR_PERMISSIONS = "AUDIT";
private final PlatformSecurityContext context;
private final AuditReadPlatformService auditReadPlatformService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final DefaultToApiJsonSerializer<AuditData> toApiJsonSerializer;
private final DefaultToApiJsonSerializer<AuditSearchData> toApiJsonSerializerSearchTemplate;
private final SqlValidator sqlValidator;
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "List Audits", description = "Get a 200 list of audits that match the criteria supplied and sorted by audit id in descending order, and are within the requestors' data scope. Also it supports pagination and sorting\n"
+ "\n" + "Example Requests:\n" + "\n" + "audits\n" + "\n" + "audits?fields=madeOnDate,maker,processingResult\n" + "\n"
+ "audits?makerDateTimeFrom=2013-03-25 08:00:00&makerDateTimeTo=2013-04-04 18:00:00\n" + "\n" + "audits?officeId=1\n" + "\n"
+ "audits?officeId=1&includeJson=true")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = MakercheckersApiResourceSwagger.GetMakerCheckerResponse.class)))) })
public String retrieveAuditEntries(@Context final UriInfo uriInfo,
@QueryParam("actionName") @Parameter(description = "actionName") final String actionName,
@QueryParam("entityName") @Parameter(description = "entityName") final String entityName,
@QueryParam("resourceId") @Parameter(description = "resourceId") final Long resourceId,
@QueryParam("makerId") @Parameter(description = "makerId") final Long makerId,
@QueryParam("makerDateTimeFrom") @Parameter(description = "makerDateTimeFrom") final String makerDateTimeFrom,
@QueryParam("makerDateTimeTo") @Parameter(description = "makerDateTimeTo") final String makerDateTimeTo,
@QueryParam("checkerId") @Parameter(description = "checkerId") final Long checkerId,
@QueryParam("checkerDateTimeFrom") @Parameter(description = "checkerDateTimeFrom") final String checkerDateTimeFrom,
@QueryParam("checkerDateTimeTo") @Parameter(description = "checkerDateTimeTo") final String checkerDateTimeTo,
@QueryParam("processingResult") @Parameter(description = "processingResult") final Integer processingResult,
@QueryParam("officeId") @Parameter(description = "officeId") final Integer officeId,
@QueryParam("groupId") @Parameter(description = "groupId") final Integer groupId,
@QueryParam("clientId") @Parameter(description = "clientId") final Integer clientId,
@QueryParam("loanid") @Parameter(description = "loanid") final Integer loanId,
@QueryParam("savingsAccountId") @Parameter(description = "savingsAccountId") final Integer savingsAccountId,
@QueryParam("paged") @Parameter(description = "paged") final Boolean paged,
@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
@QueryParam("limit") @Parameter(description = "limit") final Integer limit,
@QueryParam("orderBy") @Parameter(description = "orderBy") final String orderBy,
@QueryParam("sortOrder") @Parameter(description = "sortOrder") final String sortOrder) {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
sqlValidator.validate(orderBy);
sqlValidator.validate(sortOrder);
final PaginationParameters parameters = PaginationParameters.builder().paged(Boolean.TRUE.equals(paged)).limit(limit).offset(offset)
.orderBy(orderBy).sortOrder(sortOrder).build();
final SQLBuilder extraCriteria = getExtraCriteria(actionName, entityName, resourceId, makerId, makerDateTimeFrom, makerDateTimeTo,
checkerId, checkerDateTimeFrom, checkerDateTimeTo, processingResult, officeId, groupId, clientId, loanId, savingsAccountId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
if (parameters.isPaged()) {
final Page<AuditData> auditEntries = this.auditReadPlatformService.retrievePaginatedAuditEntries(extraCriteria,
settings.isIncludeJson(), parameters);
return this.toApiJsonSerializer.serialize(settings, auditEntries, RESPONSE_DATA_PARAMETERS);
}
final Collection<AuditData> auditEntries = this.auditReadPlatformService.retrieveAuditEntries(extraCriteria,
settings.isIncludeJson());
return this.toApiJsonSerializer.serialize(settings, auditEntries, RESPONSE_DATA_PARAMETERS);
}
@GET
@Path("{auditId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve an Audit Entry", description = "Example Requests:\n" + "\n" + "audits/20\n"
+ "audits/20?fields=madeOnDate,maker,processingResult")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = MakercheckersApiResourceSwagger.GetMakerCheckerResponse.class))) })
public String retrieveAuditEntry(@PathParam("auditId") @Parameter(description = "auditId") final Long auditId,
@Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
final AuditData auditEntry = this.auditReadPlatformService.retrieveAuditEntry(auditId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, auditEntry, RESPONSE_DATA_PARAMETERS);
}
@GET
@Path("/searchtemplate")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Audit Search Template", description = "This is a convenience resource. It can be useful when building an Audit Search UI. \"appUsers\" are data scoped to the office/branch the requestor is associated with.\n"
+ "\n" + "Example Requests:\n" + "\n" + "audits/searchtemplate\n" + "audits/searchtemplate?fields=actionNames")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = MakercheckersApiResourceSwagger.GetMakerCheckersSearchTemplateResponse.class))) })
public String retrieveAuditSearchTemplate(@Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSIONS);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
final AuditSearchData auditSearchData = this.auditReadPlatformService.retrieveSearchTemplate("audit");
final Set<String> RESPONSE_DATA_PARAMETERS_SEARCH_TEMPLATE = new HashSet<>(
Arrays.asList("appUsers", "actionNames", "entityNames", "status"));
return this.toApiJsonSerializerSearchTemplate.serialize(settings, auditSearchData, RESPONSE_DATA_PARAMETERS_SEARCH_TEMPLATE);
}
private SQLBuilder getExtraCriteria(final String actionName, final String entityName, final Long resourceId, final Long makerId,
final String makerDateTimeFrom, final String makerDateTimeTo, final Long checkerId, final String checkerDateTimeFrom,
final String checkerDateTimeTo, final Integer status, final Integer officeId, final Integer groupId, final Integer clientId,
final Integer loanId, final Integer savingsAccountId) {
SQLBuilder extraCriteria = new SQLBuilder();
extraCriteria.addNonNullCriteria("aud.action_name = ", actionName);
if (entityName != null) {
extraCriteria.addCriteria("aud.entity_name like", entityName + "%");
}
extraCriteria.addNonNullCriteria("aud.resource_id = ", resourceId);
extraCriteria.addNonNullCriteria("aud.maker_id = ", makerId);
extraCriteria.addNonNullCriteria("aud.checker_id = ", checkerId);
extraCriteria.addNonNullCriteria("aud.made_on_date >= ", makerDateTimeFrom);
extraCriteria.addNonNullCriteria("aud.made_on_date <= ", makerDateTimeTo);
extraCriteria.addNonNullCriteria("aud.checked_on_date >= ", checkerDateTimeFrom);
extraCriteria.addNonNullCriteria("aud.checked_on_date <= ", checkerDateTimeTo);
extraCriteria.addNonNullCriteria("aud.status = ", status);
extraCriteria.addNonNullCriteria("aud.office_id = ", officeId);
extraCriteria.addNonNullCriteria("aud.group_id = ", groupId);
extraCriteria.addNonNullCriteria("aud.client_id = ", clientId);
extraCriteria.addNonNullCriteria("aud.loan_id = ", loanId);
extraCriteria.addNonNullCriteria("aud.savings_account_id = ", savingsAccountId);
return extraCriteria;
}
}