View Javadoc
1   package de.dlr.shepard.security;
2   
3   import de.dlr.shepard.labJournal.services.LabJournalEntryService;
4   import de.dlr.shepard.neo4Core.entities.Permissions;
5   import de.dlr.shepard.neo4Core.entities.User;
6   import de.dlr.shepard.neo4Core.entities.UserGroup;
7   import de.dlr.shepard.neo4Core.io.RolesIO;
8   import de.dlr.shepard.neo4Core.services.DataObjectService;
9   import de.dlr.shepard.neo4Core.services.PermissionsService;
10  import de.dlr.shepard.neo4Core.services.UserGroupService;
11  import de.dlr.shepard.util.AccessType;
12  import de.dlr.shepard.util.Constants;
13  import de.dlr.shepard.util.PermissionType;
14  import jakarta.enterprise.context.RequestScoped;
15  import jakarta.inject.Inject;
16  import jakarta.ws.rs.container.ContainerRequestContext;
17  import jakarta.ws.rs.core.PathSegment;
18  import java.util.HashSet;
19  import java.util.List;
20  import java.util.Set;
21  import org.apache.commons.lang3.StringUtils;
22  
23  @RequestScoped
24  public class PermissionsUtil {
25  
26    private PermissionsService permissionsService;
27    private UserGroupService userGroupService;
28    private LabJournalEntryService labJournalEntryService;
29    private DataObjectService dataObjectService;
30  
31    PermissionsUtil() {}
32  
33    @Inject
34    public PermissionsUtil(
35      PermissionsService permissionsService,
36      UserGroupService userGroupService,
37      LabJournalEntryService labJournalEntryService,
38      DataObjectService dataObjectService
39    ) {
40      this.permissionsService = permissionsService;
41      this.userGroupService = userGroupService;
42      this.labJournalEntryService = labJournalEntryService;
43      this.dataObjectService = dataObjectService;
44    }
45  
46    /**
47     * 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.
48     * @param requestContext
49     * @param accessType
50     * @param userName
51     */
52    public boolean isAllowed(ContainerRequestContext requestContext, AccessType accessType, String userName) {
53      List<PathSegment> pathSegments = requestContext.getUriInfo().getPathSegments();
54      var idSegment = pathSegments.size() > 1 ? pathSegments.get(1).getPath() : null;
55      // Check initially for lab journal entries requests, then pass it to the generic check
56      if (pathSegments.get(0).getPath().equals(Constants.LAB_JOURNAL_ENTRIES)) {
57        return isAllowedLabJournalEntryRequest(requestContext, accessType, userName, idSegment);
58      }
59      // Perform the generic check
60      if (idSegment == null || idSegment.isBlank()) {
61        // No id in path
62        return true;
63      } else if (!StringUtils.isNumeric(idSegment)) {
64        // usersearch and containersearch
65        if (
66          pathSegments.get(0).getPath().equals(Constants.SEARCH) &&
67          List.of(Constants.USERS, Constants.CONTAINERS).contains(pathSegments.get(1).getPath()) &&
68          pathSegments.size() == 2
69        ) return true;
70        // non-numeric id
71        else if (pathSegments.get(0).getPath().equals(Constants.USERS)) {
72          if (pathSegments.size() <= 2 && AccessType.Read.equals(accessType)) return true; // it is allowed to read all users
73          else if (userName.equals(idSegment)) return true; // it is allowed to access yourself
74        }
75        return false;
76      }
77  
78      var entityId = Long.parseLong(idSegment);
79      return isAccessTypeAllowedForUser(entityId, accessType, userName);
80    }
81  
82    private boolean isAllowedLabJournalEntryRequest(
83      ContainerRequestContext requestContext,
84      AccessType accessType,
85      String userName,
86      String idSegment
87    ) {
88      String dataObjectId = requestContext.getUriInfo().getQueryParameters().getFirst(Constants.DATA_OBJECT_ID);
89      // If the labjournalEntry request has objectId parameter [in GET/labJournals and POST /labJournals]
90      if (dataObjectId != null && !dataObjectId.isEmpty() && StringUtils.isNumeric(dataObjectId)) {
91        Long collectionId = dataObjectService.getCollectionId(Long.parseLong(dataObjectId));
92        if (collectionId == null) return true;
93        return isAccessTypeAllowedForUser(collectionId, accessType, userName);
94      }
95      if (idSegment == null || idSegment.isBlank()) {
96        return true;
97      }
98      Long labJournalId = Long.parseLong(idSegment);
99      Long collectionId = labJournalEntryService.getCollectionId(labJournalId);
100     if (collectionId == null) return true;
101     // If the labjournalEntry request has labjournalId as path segment [in GET/labJournals/{labjournalId}, PUT/labJournals/{labjournalId}, DELETE/labJournals/{labjournalId} ]
102     return isAccessTypeAllowedForUser(collectionId, accessType, userName);
103   }
104 
105   /**
106    * Check whether a request is allowed or not
107    *
108    * @param entityId   the entity that is to be accessed
109    * @param accessType the access type (read, write, manage)
110    * @param username   the user that wants access
111    * @return whether the access is allowed or not
112    */
113   public boolean isAccessTypeAllowedForUser(long entityId, AccessType accessType, String username) {
114     var perms = permissionsService.getPermissionsByNeo4jId(entityId);
115     if (perms == null) return true; // No permissions
116 
117     if (isOwner(perms, username)) return true; // Is owner
118 
119     if (AccessType.Manage.equals(accessType)) {
120       return isManager(perms, username);
121     } else if (AccessType.Read.equals(accessType)) {
122       return isReader(perms, username);
123     } else if (AccessType.Write.equals(accessType)) {
124       return isWriter(perms, username);
125     }
126 
127     return false;
128   }
129 
130   private Set<String> fetchUserNames(List<UserGroup> userGroups) {
131     Set<String> ret = new HashSet<>();
132     for (UserGroup userGroup : userGroups) {
133       UserGroup fullUserGroup = userGroupService.getUserGroup(userGroup.getId());
134       for (User user : fullUserGroup.getUsers()) {
135         ret.add(user.getUsername());
136       }
137     }
138     return ret;
139   }
140 
141   public RolesIO getRolesByNeo4jId(long id, String username) {
142     var perms = permissionsService.getPermissionsByNeo4jId(id);
143     return getRoles(perms, username);
144   }
145 
146   public RolesIO getRolesByShepardId(long shepardId, String username) {
147     var perms = permissionsService.getPermissionsByShepardId(shepardId);
148     return getRoles(perms, username);
149   }
150 
151   private RolesIO getRoles(Permissions perms, String username) {
152     if (perms == null) {
153       // Legacy entity without permissions
154       return new RolesIO(false, true, true, true);
155     }
156     var roles = new RolesIO(
157       isOwner(perms, username),
158       isManager(perms, username),
159       isWriter(perms, username),
160       isReader(perms, username)
161     );
162     return roles;
163   }
164 
165   private boolean isOwner(Permissions perms, String username) {
166     return perms.getOwner() != null && username.equals(perms.getOwner().getUsername());
167   }
168 
169   private boolean isManager(Permissions perms, String username) {
170     return perms.getManager().stream().anyMatch(u -> username.equals(u.getUsername()));
171   }
172 
173   private boolean isReader(Permissions perms, String username) {
174     var pub = PermissionType.Public.equals(perms.getPermissionType());
175     var pubRead = PermissionType.PublicReadable.equals(perms.getPermissionType());
176     var reader = perms.getReader().stream().anyMatch(u -> username.equals(u.getUsername()));
177     var readerGroup = fetchUserNames(perms.getReaderGroups()).contains(username);
178     return pub || pubRead || reader || readerGroup;
179   }
180 
181   private boolean isWriter(Permissions perms, String username) {
182     var pub = PermissionType.Public.equals(perms.getPermissionType());
183     var writer = perms.getWriter().stream().anyMatch(u -> username.equals(u.getUsername()));
184     var writerGroup = fetchUserNames(perms.getWriterGroups()).contains(username);
185     return pub || writer || writerGroup;
186   }
187 }