Skip to content

Commit

Permalink
[MODINVOICE-516] Added a permission to bypass acqunit checks (#871)
Browse files Browse the repository at this point in the history
  • Loading branch information
damien-git authored Mar 25, 2024
1 parent 9971c5c commit 14d7075
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 18 deletions.
14 changes: 14 additions & 0 deletions descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@
"permissionsRequired": [
"orders.po-lines.collection.get"
],
"permissionsDesired": [
"orders.bypass-acquisition-units"
],
"modulePermissions": [
"acquisitions-units-storage.units.collection.get",
"acquisitions-units-storage.memberships.collection.get",
Expand Down Expand Up @@ -195,6 +198,9 @@
"permissionsRequired": [
"orders.po-lines.item.get"
],
"permissionsDesired": [
"orders.bypass-acquisition-units"
],
"modulePermissions": [
"orders-storage.po-lines.item.get",
"orders-storage.alerts.item.get",
Expand All @@ -214,6 +220,9 @@
"permissionsRequired": [
"orders.po-lines.item.put"
],
"permissionsDesired": [
"orders.bypass-acquisition-units"
],
"modulePermissions": [
"orders-storage.alerts.item.post",
"orders-storage.alerts.item.put",
Expand Down Expand Up @@ -1450,6 +1459,11 @@
"acquisitions-units.units.item.delete"
]
},
{
"permissionName": "orders.bypass-acquisition-units",
"displayName": "Bypass acquisition units checks",
"description": "Backend internal permission to bypass acquisition units checks"
},
{
"permissionName": "orders.acquisitions-units-assignments.assign",
"displayName": "Acquisitions unit assignment - create unit assignment",
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/folio/helper/PurchaseOrderHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,7 @@ private Future<Void> checkOrderApprovalPermissions(CompositePurchaseOrder compPO
boolean isApprovalRequired = isApprovalRequiredConfiguration(tenantConfig);
if (isApprovalRequired && compPO.getApproved()
.equals(Boolean.TRUE)) {
if (isUserNotHaveApprovePermission(requestContext)) {
if (userDoesNotHaveApprovePermission(requestContext)) {
throw new HttpException(HttpStatus.HTTP_FORBIDDEN.toInt(), USER_HAS_NO_APPROVAL_PERMISSIONS);
}
compPO.setApprovalDate(new Date());
Expand All @@ -626,13 +626,13 @@ private Future<Void> checkOrderApprovalPermissions(CompositePurchaseOrder compPO
}

private void checkOrderUnopenPermissions(RequestContext requestContext) {
if (isUserNotHaveUnopenPermission(requestContext)) {
if (userDoesNotHaveUnopenPermission(requestContext)) {
throw new HttpException(HttpStatus.HTTP_FORBIDDEN.toInt(), USER_HAS_NO_UNOPEN_PERMISSIONS);
}
}

private void checkOrderReopenPermissions(RequestContext requestContext) {
if (isUserNotHaveReopenPermission(requestContext)) {
if (userDoesNotHaveReopenPermission(requestContext)) {
throw new HttpException(HttpStatus.HTTP_FORBIDDEN.toInt(), USER_HAS_NO_REOPEN_PERMISSIONS);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ public enum AcqDesiredPermissions {
ASSIGN("orders.acquisitions-units-assignments.assign"),
MANAGE("orders.acquisitions-units-assignments.manage"),
TITLES_ASSIGN("titles.acquisitions-units-assignments.assign"),
TITLES_MANAGE("titles.acquisitions-units-assignments.manage");
TITLES_MANAGE("titles.acquisitions-units-assignments.manage"),
BYPASS_ACQ_UNITS("orders.bypass-acquisition-units");

private final String permission;
private static final List<String> values;
Expand All @@ -25,7 +26,7 @@ public String getPermission() {
return permission;
}

public static List<String> getValues() {
return values;
public static List<String> getValuesExceptBypass() {
return values.stream().filter(v -> !BYPASS_ACQ_UNITS.getPermission().equals(v)).toList();
}
}
12 changes: 8 additions & 4 deletions src/main/java/org/folio/orders/utils/PermissionsUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ public class PermissionsUtil {

private PermissionsUtil() {}

public static boolean isUserDoesNotHaveDesiredPermission(AcqDesiredPermissions acqPerm, RequestContext requestContext) {
public static boolean userHasDesiredPermission(AcqDesiredPermissions acqPerm, RequestContext requestContext) {
return getProvidedPermissions(requestContext).contains(acqPerm.getPermission());
}

public static boolean userDoesNotHaveDesiredPermission(AcqDesiredPermissions acqPerm, RequestContext requestContext) {
return !getProvidedPermissions(requestContext).contains(acqPerm.getPermission());
}

Expand All @@ -32,15 +36,15 @@ private static List<String> getProvidedPermissions(RequestContext requestContext
.toList();
}

public static boolean isUserNotHaveApprovePermission(RequestContext requestContext) {
public static boolean userDoesNotHaveApprovePermission(RequestContext requestContext) {
return !getProvidedPermissions(requestContext).contains(PERMISSION_ORDER_APPROVE);
}

public static boolean isUserNotHaveUnopenPermission(RequestContext requestContext) {
public static boolean userDoesNotHaveUnopenPermission(RequestContext requestContext) {
return !getProvidedPermissions(requestContext).contains(PERMISSION_ORDER_UNOPEN);
}

public static boolean isUserNotHaveReopenPermission(RequestContext requestContext) {
public static boolean userDoesNotHaveReopenPermission(RequestContext requestContext) {
return !getProvidedPermissions(requestContext).contains(PERMISSION_ORDER_REOPEN);
}
}
14 changes: 11 additions & 3 deletions src/main/java/org/folio/service/ProtectionService.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package org.folio.service;

import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
import static org.folio.orders.utils.AcqDesiredPermissions.BYPASS_ACQ_UNITS;
import static org.folio.orders.utils.HelperUtils.combineCqlExpressions;
import static org.folio.orders.utils.HelperUtils.convertIdsToCqlQuery;
import static org.folio.orders.utils.PermissionsUtil.isManagePermissionRequired;
import static org.folio.orders.utils.PermissionsUtil.isUserDoesNotHaveDesiredPermission;
import static org.folio.orders.utils.PermissionsUtil.userDoesNotHaveDesiredPermission;
import static org.folio.orders.utils.PermissionsUtil.userHasDesiredPermission;
import static org.folio.rest.core.exceptions.ErrorCodes.ORDER_UNITS_NOT_FOUND;
import static org.folio.rest.core.exceptions.ErrorCodes.USER_HAS_NO_ACQ_PERMISSIONS;
import static org.folio.rest.core.exceptions.ErrorCodes.USER_NOT_A_MEMBER_OF_THE_ACQ;
Expand Down Expand Up @@ -66,6 +68,9 @@ public Future<Void> isOperationRestricted(List<String> unitIds, ProtectedOperati
* exist; successfully otherwise
*/
public Future<Void> isOperationRestricted(List<String> unitIds, Set<ProtectedOperationType> operations, RequestContext requestContext) {
if (userHasDesiredPermission(BYPASS_ACQ_UNITS, requestContext)) {
return Future.succeededFuture();
}
if (CollectionUtils.isNotEmpty(unitIds)) {
return getUnitsByIds(unitIds, requestContext)
.compose(units -> {
Expand Down Expand Up @@ -120,7 +125,7 @@ public Future<Void> validateAcqUnitsOnCreate(List<String> acqUnitIds,
private Void verifyUserHasAssignPermission(List<String> acqUnitIds,
AcqDesiredPermissions permission,
RequestContext requestContext) {
if (isNotEmpty(acqUnitIds) && isUserDoesNotHaveDesiredPermission(permission, requestContext)){
if (isNotEmpty(acqUnitIds) && userDoesNotHaveDesiredPermission(permission, requestContext)){
throw new HttpException(HttpStatus.HTTP_FORBIDDEN.toInt(), USER_HAS_NO_ACQ_PERMISSIONS);
}
return null;
Expand Down Expand Up @@ -160,6 +165,9 @@ public Future<Void> validateAcqUnitsOnUpdate(List<String> newAcqUnitIds,
* @return new string with acq check
*/
public Future<String> getQueryWithAcqUnitsCheck(String tablePrefix, String query, RequestContext requestContext) {
if (userHasDesiredPermission(BYPASS_ACQ_UNITS, requestContext)) {
return Future.succeededFuture(query);
}
return acquisitionsUnitsService.buildAcqUnitsCqlExprToSearchRecords(tablePrefix, requestContext)
.map(acqUnitsCqlExpr -> {
if (StringUtils.isNotEmpty(query)) {
Expand All @@ -185,7 +193,7 @@ private void verifyUserHasManagePermission(List<String> newAcqUnitIds,
Set<String> newAcqUnits = new HashSet<>(CollectionUtils.emptyIfNull(newAcqUnitIds));
Set<String> acqUnitsFromStorage = new HashSet<>(CollectionUtils.emptyIfNull(acqUnitIdsFromStorage));

if (isManagePermissionRequired(newAcqUnits, acqUnitsFromStorage) && isUserDoesNotHaveDesiredPermission(permission, requestContext)){
if (isManagePermissionRequired(newAcqUnits, acqUnitsFromStorage) && userDoesNotHaveDesiredPermission(permission, requestContext)){
throw new HttpException(HttpStatus.HTTP_FORBIDDEN.toInt(), USER_HAS_NO_ACQ_PERMISSIONS);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import static org.folio.orders.utils.HelperUtils.DEFAULT_POLINE_LIMIT;
import static org.folio.orders.utils.HelperUtils.ORDER_CONFIG_MODULE_NAME;
import static org.folio.orders.utils.HelperUtils.PO_LINES_LIMIT_PROPERTY;
import static org.folio.orders.utils.PermissionsUtil.isUserNotHaveApprovePermission;
import static org.folio.orders.utils.PermissionsUtil.userDoesNotHaveApprovePermission;
import static org.folio.rest.jaxrs.model.CompositePoLine.OrderFormat.ELECTRONIC_RESOURCE;
import static org.folio.rest.jaxrs.model.CompositePoLine.OrderFormat.OTHER;
import static org.folio.rest.jaxrs.model.CompositePoLine.OrderFormat.PHYSICAL_RESOURCE;
Expand Down Expand Up @@ -279,7 +279,7 @@ private Future<Object> setApprovedFalseIfUserNotHaveApprovalPermission(DataImpor
CompositePurchaseOrder order = Json.decodeValue(dataImportEventPayload.getContext().get(ORDER.value()), CompositePurchaseOrder.class);
Boolean isApproved = order.getApproved();
boolean isApprovalRequired = isApprovalRequiredConfiguration(tenantConfig);
boolean isUserNotHaveApprovalPermission = isUserNotHaveApprovePermission(requestContext);
boolean isUserNotHaveApprovalPermission = userDoesNotHaveApprovePermission(requestContext);

if (isApprovalRequired && Boolean.TRUE.equals(isApproved) && isUserNotHaveApprovalPermission) {
order.setApproved(false);
Expand All @@ -302,7 +302,7 @@ private Future<Void> adjustEventType(DataImportEventPayload dataImportEventPaylo
}

boolean isApprovalRequired = isApprovalRequiredConfiguration(tenantConfig);
boolean isUserNotHaveApprovalPermission = isUserNotHaveApprovePermission(requestContext);
boolean isUserNotHaveApprovalPermission = userDoesNotHaveApprovePermission(requestContext);

if (workflowStatus.equals(WorkflowStatus.OPEN)) {
if (isApprovalRequired && isUserNotHaveApprovalPermission) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ public class PurchaseOrdersApiTest {
public static final String ORDER_DELETE_ERROR_TENANT = "order_delete_error";
static final Header ERROR_ORDER_DELETE_TENANT_HEADER = new Header(OKAPI_HEADER_TENANT, ORDER_DELETE_ERROR_TENANT);

public static final Header ALL_DESIRED_PERMISSIONS_HEADER = new Header(OKAPI_HEADER_PERMISSIONS, new JsonArray(AcqDesiredPermissions.getValues()).encode());
public static final Header ALL_DESIRED_PERMISSIONS_HEADER = new Header(OKAPI_HEADER_PERMISSIONS, new JsonArray(AcqDesiredPermissions.getValuesExceptBypass()).encode());
public static final Header APPROVAL_PERMISSIONS_HEADER = new Header(OKAPI_HEADER_PERMISSIONS, new JsonArray(Collections.singletonList("orders.item.approve")).encode());
public static final Header UNOPEN_PERMISSIONS_HEADER = new Header(OKAPI_HEADER_PERMISSIONS, new JsonArray(Collections.singletonList("orders.item.unopen")).encode());
public static final Header REOPEN_PERMISSIONS_HEADER = new Header(OKAPI_HEADER_PERMISSIONS, new JsonArray(Collections.singletonList("orders.item.reopen")).encode());
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/org/folio/rest/impl/TitlesApiTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public class TitlesApiTest {
public static final String SAMPLE_TITLE_ID = "9a665b22-9fe5-4c95-b4ee-837a5433c95d";
private final JsonObject titleJsonReqData = getMockAsJson(TITLES_MOCK_DATA_PATH + "title.json");
private final JsonObject packageTitleJsonReqData = getMockAsJson(TITLES_MOCK_DATA_PATH + "package_title.json");
public static final Header ALL_DESIRED_PERMISSIONS_HEADER = new Header(OKAPI_HEADER_PERMISSIONS, new JsonArray(AcqDesiredPermissions.getValues()).encode());
public static final Header ALL_DESIRED_PERMISSIONS_HEADER = new Header(OKAPI_HEADER_PERMISSIONS, new JsonArray(AcqDesiredPermissions.getValuesExceptBypass()).encode());

private static boolean runningOnOwn;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import static org.folio.TestConstants.EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10;
import static org.folio.TestConstants.X_OKAPI_USER_ID;
import static org.folio.TestUtils.encodePrettily;
import static org.folio.orders.utils.AcqDesiredPermissions.BYPASS_ACQ_UNITS;
import static org.folio.orders.utils.PermissionsUtil.OKAPI_HEADER_PERMISSIONS;
import static org.folio.rest.core.exceptions.ErrorCodes.ORDER_UNITS_NOT_FOUND;
import static org.folio.rest.core.exceptions.ErrorCodes.USER_NOT_A_MEMBER_OF_THE_ACQ;
import static org.folio.rest.impl.PurchaseOrderLinesApiTest.LINES_PATH;
Expand All @@ -16,9 +18,12 @@
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.hamcrest.core.IsEqual.equalTo;

import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

import io.restassured.http.Header;
import io.vertx.core.json.JsonArray;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.ApiTestSuite;
Expand Down Expand Up @@ -126,4 +131,20 @@ void testWithProtectedUnitsAndForbiddenUser(ProtectedOperations operation) {

validateNumberOfRequests(1, 1);
}

@ParameterizedTest
@ValueSource(strings = {
"UPDATE",
"READ"
})
void testBypassAcqUnitChecks(ProtectedOperations operation) {
Header permissionHeader = new Header(OKAPI_HEADER_PERMISSIONS,
new JsonArray(List.of(BYPASS_ACQ_UNITS.getPermission())).encode());
Headers headers = prepareHeaders(EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10, permissionHeader, X_OKAPI_USER_ID);
operation.process(LINES_PATH, encodePrettily(preparePoLine(PROTECTED_UNITS, PENDING)),
headers, operation.getContentType(), operation.getCode());

validateNumberOfRequests(0, 0);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import static org.folio.TestUtils.encodePrettily;
import static org.folio.TestUtils.getMinimalContentCompositePoLine;
import static org.folio.TestUtils.getMinimalContentCompositePurchaseOrder;
import static org.folio.orders.utils.AcqDesiredPermissions.BYPASS_ACQ_UNITS;
import static org.folio.orders.utils.PermissionsUtil.OKAPI_HEADER_PERMISSIONS;
import static org.folio.orders.utils.ResourcePathResolver.ACQUISITIONS_UNITS;
import static org.folio.orders.utils.ResourcePathResolver.PO_LINES_STORAGE;
import static org.folio.orders.utils.ResourcePathResolver.PURCHASE_ORDER_STORAGE;
Expand Down Expand Up @@ -39,6 +41,8 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

import io.restassured.http.Header;
import io.vertx.core.json.JsonArray;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.ApiTestSuite;
Expand Down Expand Up @@ -366,4 +370,19 @@ void testUpdateOrderAssignedToCreateProtectedUnitAddingAndDeletingPoLinesUserAss
validateNumberOfRequests(1, 1);
}

@ParameterizedTest
@ValueSource(strings = {
"UPDATE",
"READ"
})
void testBypassAcqUnitChecks(ProtectedOperations operation) {
Header permissionHeader = new Header(OKAPI_HEADER_PERMISSIONS,
new JsonArray(List.of(BYPASS_ACQ_UNITS.getPermission())).encode());
final Headers headers = prepareHeaders(EXIST_CONFIG_X_OKAPI_TENANT_LIMIT_10, permissionHeader, X_OKAPI_USER_ID);
CompositePurchaseOrder order = operation == CREATE ? getMinimalContentCompositePurchaseOrder().withAcqUnitIds(new ArrayList<>(PROTECTED_UNITS)) : prepareOrder(PROTECTED_UNITS, PENDING);
operation.process(COMPOSITE_ORDERS_PATH, encodePrettily(order), headers, operation.getContentType(), operation.getCode());

validateNumberOfRequests(0, 0);
}

}

0 comments on commit 14d7075

Please sign in to comment.