PermissionsUtil.java
package de.dlr.shepard.security;
import de.dlr.shepard.labJournal.services.LabJournalEntryService;
import de.dlr.shepard.neo4Core.entities.Permissions;
import de.dlr.shepard.neo4Core.entities.User;
import de.dlr.shepard.neo4Core.entities.UserGroup;
import de.dlr.shepard.neo4Core.io.RolesIO;
import de.dlr.shepard.neo4Core.services.DataObjectService;
import de.dlr.shepard.neo4Core.services.PermissionsService;
import de.dlr.shepard.neo4Core.services.UserGroupService;
import de.dlr.shepard.util.AccessType;
import de.dlr.shepard.util.Constants;
import de.dlr.shepard.util.PermissionType;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.core.PathSegment;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
@RequestScoped
public class PermissionsUtil {
private PermissionsService permissionsService;
private UserGroupService userGroupService;
private LabJournalEntryService labJournalEntryService;
private DataObjectService dataObjectService;
PermissionsUtil() {}
@Inject
public PermissionsUtil(
PermissionsService permissionsService,
UserGroupService userGroupService,
LabJournalEntryService labJournalEntryService,
DataObjectService dataObjectService
) {
this.permissionsService = permissionsService;
this.userGroupService = userGroupService;
this.labJournalEntryService = labJournalEntryService;
this.dataObjectService = dataObjectService;
}
/**
* Checks if the request is allowed based on access type and user name. The check is performed by checking the path segments, and request body.
* @param requestContext
* @param accessType
* @param userName
*/
public boolean isAllowed(ContainerRequestContext requestContext, AccessType accessType, String userName) {
List<PathSegment> pathSegments = requestContext.getUriInfo().getPathSegments();
var idSegment = pathSegments.size() > 1 ? pathSegments.get(1).getPath() : null;
// Check initially for lab journal entries requests, then pass it to the generic check
if (pathSegments.get(0).getPath().equals(Constants.LAB_JOURNAL_ENTRIES)) {
return isAllowedLabJournalEntryRequest(requestContext, accessType, userName, idSegment);
}
// Perform the generic check
if (idSegment == null || idSegment.isBlank()) {
// No id in path
return true;
} else if (!StringUtils.isNumeric(idSegment)) {
// usersearch and containersearch
if (
pathSegments.get(0).getPath().equals(Constants.SEARCH) &&
List.of(Constants.USERS, Constants.CONTAINERS).contains(pathSegments.get(1).getPath()) &&
pathSegments.size() == 2
) return true;
// non-numeric id
else if (pathSegments.get(0).getPath().equals(Constants.USERS)) {
if (pathSegments.size() <= 2 && AccessType.Read.equals(accessType)) return true; // it is allowed to read all users
else if (userName.equals(idSegment)) return true; // it is allowed to access yourself
}
return false;
}
var entityId = Long.parseLong(idSegment);
return isAccessTypeAllowedForUser(entityId, accessType, userName);
}
private boolean isAllowedLabJournalEntryRequest(
ContainerRequestContext requestContext,
AccessType accessType,
String userName,
String idSegment
) {
String dataObjectId = requestContext.getUriInfo().getQueryParameters().getFirst(Constants.DATA_OBJECT_ID);
// If the labjournalEntry request has objectId parameter [in GET/labJournals and POST /labJournals]
if (dataObjectId != null && !dataObjectId.isEmpty() && StringUtils.isNumeric(dataObjectId)) {
Long collectionId = dataObjectService.getCollectionId(Long.parseLong(dataObjectId));
if (collectionId == null) return true;
return isAccessTypeAllowedForUser(collectionId, accessType, userName);
}
if (idSegment == null || idSegment.isBlank()) {
return true;
}
Long labJournalId = Long.parseLong(idSegment);
Long collectionId = labJournalEntryService.getCollectionId(labJournalId);
if (collectionId == null) return true;
// If the labjournalEntry request has labjournalId as path segment [in GET/labJournals/{labjournalId}, PUT/labJournals/{labjournalId}, DELETE/labJournals/{labjournalId} ]
return isAccessTypeAllowedForUser(collectionId, accessType, userName);
}
/**
* Check whether a request is allowed or not
*
* @param entityId the entity that is to be accessed
* @param accessType the access type (read, write, manage)
* @param username the user that wants access
* @return whether the access is allowed or not
*/
public boolean isAccessTypeAllowedForUser(long entityId, AccessType accessType, String username) {
var perms = permissionsService.getPermissionsByNeo4jId(entityId);
if (perms == null) return true; // No permissions
if (isOwner(perms, username)) return true; // Is owner
if (AccessType.Manage.equals(accessType)) {
return isManager(perms, username);
} else if (AccessType.Read.equals(accessType)) {
return isReader(perms, username);
} else if (AccessType.Write.equals(accessType)) {
return isWriter(perms, username);
}
return false;
}
private Set<String> fetchUserNames(List<UserGroup> userGroups) {
Set<String> ret = new HashSet<>();
for (UserGroup userGroup : userGroups) {
UserGroup fullUserGroup = userGroupService.getUserGroup(userGroup.getId());
for (User user : fullUserGroup.getUsers()) {
ret.add(user.getUsername());
}
}
return ret;
}
public RolesIO getRolesByNeo4jId(long id, String username) {
var perms = permissionsService.getPermissionsByNeo4jId(id);
return getRoles(perms, username);
}
public RolesIO getRolesByShepardId(long shepardId, String username) {
var perms = permissionsService.getPermissionsByShepardId(shepardId);
return getRoles(perms, username);
}
private RolesIO getRoles(Permissions perms, String username) {
if (perms == null) {
// Legacy entity without permissions
return new RolesIO(false, true, true, true);
}
var roles = new RolesIO(
isOwner(perms, username),
isManager(perms, username),
isWriter(perms, username),
isReader(perms, username)
);
return roles;
}
private boolean isOwner(Permissions perms, String username) {
return perms.getOwner() != null && username.equals(perms.getOwner().getUsername());
}
private boolean isManager(Permissions perms, String username) {
return perms.getManager().stream().anyMatch(u -> username.equals(u.getUsername()));
}
private boolean isReader(Permissions perms, String username) {
var pub = PermissionType.Public.equals(perms.getPermissionType());
var pubRead = PermissionType.PublicReadable.equals(perms.getPermissionType());
var reader = perms.getReader().stream().anyMatch(u -> username.equals(u.getUsername()));
var readerGroup = fetchUserNames(perms.getReaderGroups()).contains(username);
return pub || pubRead || reader || readerGroup;
}
private boolean isWriter(Permissions perms, String username) {
var pub = PermissionType.Public.equals(perms.getPermissionType());
var writer = perms.getWriter().stream().anyMatch(u -> username.equals(u.getUsername()));
var writerGroup = fetchUserNames(perms.getWriterGroups()).contains(username);
return pub || writer || writerGroup;
}
}