View Javadoc
1   package de.dlr.shepard.context.collection.services;
2   
3   import de.dlr.shepard.auth.permission.io.PermissionsIO;
4   import de.dlr.shepard.auth.permission.model.Permissions;
5   import de.dlr.shepard.auth.permission.model.Roles;
6   import de.dlr.shepard.auth.permission.services.PermissionsService;
7   import de.dlr.shepard.auth.security.AuthenticationContext;
8   import de.dlr.shepard.auth.users.services.UserService;
9   import de.dlr.shepard.common.exceptions.InvalidAuthException;
10  import de.dlr.shepard.common.exceptions.InvalidPathException;
11  import de.dlr.shepard.common.exceptions.InvalidRequestException;
12  import de.dlr.shepard.common.util.AccessType;
13  import de.dlr.shepard.common.util.Constants;
14  import de.dlr.shepard.common.util.DateHelper;
15  import de.dlr.shepard.common.util.PermissionType;
16  import de.dlr.shepard.common.util.QueryParamHelper;
17  import de.dlr.shepard.context.collection.daos.CollectionDAO;
18  import de.dlr.shepard.context.collection.entities.Collection;
19  import de.dlr.shepard.context.collection.io.CollectionIO;
20  import de.dlr.shepard.context.version.daos.VersionDAO;
21  import de.dlr.shepard.context.version.entities.Version;
22  import de.dlr.shepard.data.file.entities.FileContainer;
23  import de.dlr.shepard.data.file.services.FileContainerService;
24  import jakarta.enterprise.context.RequestScoped;
25  import jakarta.inject.Inject;
26  import java.util.Date;
27  import java.util.List;
28  import java.util.UUID;
29  
30  @RequestScoped
31  public class CollectionService {
32  
33    @Inject
34    CollectionDAO collectionDAO;
35  
36    @Inject
37    UserService userService;
38  
39    @Inject
40    PermissionsService permissionsService;
41  
42    @Inject
43    DateHelper dateHelper;
44  
45    @Inject
46    VersionDAO versionDAO;
47  
48    @Inject
49    AuthenticationContext authenticationContext;
50  
51    @Inject
52    FileContainerService fileContainerService;
53  
54    /**
55     * Creates a Collection and stores it in Neo4J
56     *
57     * @param collection to be stored
58     * @return the created collection
59     * @throws InvalidPathException if default FileContainer is specified, but the FileContainer cannot be found
60     * @throws InvalidAuthException if default FileContainer is specified, but user has no read permission on FileContainer
61     */
62    public Collection createCollection(CollectionIO collection) {
63      Date date = dateHelper.getDate();
64      var user = userService.getCurrentUser();
65  
66      var toCreate = new Collection();
67      toCreate.setAttributes(collection.getAttributes());
68      toCreate.setCreatedBy(user);
69      toCreate.setCreatedAt(date);
70      toCreate.setDescription(collection.getDescription());
71      toCreate.setName(collection.getName());
72  
73      if (collection.getDefaultFileContainerId() != null) {
74        FileContainer fileContainer = fileContainerService.getContainer(collection.getDefaultFileContainerId());
75        toCreate.setFileContainer(fileContainer);
76      } else {
77        toCreate.setFileContainer(null);
78      }
79  
80      var createdCollection = collectionDAO.createOrUpdate(toCreate);
81  
82      Version nullVersion = new Version(Constants.HEAD, Constants.HEAD_VERSION, date, user);
83      Version savedNullVersion = versionDAO.createOrUpdate(nullVersion);
84  
85      long collectionId = createdCollection.getId();
86      createdCollection.setShepardId(collectionId);
87      createdCollection.setVersion(savedNullVersion);
88      var updated = collectionDAO.createOrUpdate(createdCollection);
89      permissionsService.createPermissions(updated, user, PermissionType.Private);
90  
91      return updated;
92    }
93  
94    /**
95     * Searches the database for all Collections
96     *
97     * @param params encapsulates possible parameters
98     * @param username the name of the user
99     * @return a list of Collections
100    */
101   public List<Collection> getAllCollections(QueryParamHelper params) {
102     List<Collection> queryResult = collectionDAO.findAllCollectionsByShepardId(
103       params,
104       authenticationContext.getCurrentUserName()
105     );
106     List<Collection> collections = queryResult.stream().map(this::cutDeleted).toList();
107     return collections;
108   }
109 
110   /**
111    * Retrieves a collection by shepardId.
112    * The returned collection is in 'light' format, so dataobjects and incoming references are excluded.
113    *
114    * @param shepardId long
115    * @return Collection
116    * @throws InvalidPathException if no collection could be found by shepardId
117    * @throws InvalidAuthException if the user does not have permissions to read the collection
118    */
119   public Collection getCollection(long shepardId) {
120     return getCollection(shepardId, null, true);
121   }
122 
123   /**
124    * Retrieves a collection by shepardId and versionUID.
125    * The returned collection is in 'light' format, so dataobjects and incoming references are excluded.
126    *
127    * @param shepardId long
128    * @param versionUID UUID
129    * @return Collection
130    * @throws InvalidPathException if no collection (with specified version) could be found by shepardId
131    * @throws InvalidAuthException if the user does not have permissions to read the collection
132    */
133   public Collection getCollection(long shepardId, UUID versionUID) {
134     return getCollection(shepardId, versionUID, true);
135   }
136 
137   /**
138    * Fetches a collection including permissions, attributes, contained data objects and incoming references.
139    * @param shepardId shepardId of the desired collection
140    * @return Collection
141    * @throws InvalidPathException if no collection could be found by shepardId
142    * @throws InvalidAuthException if the user does not have permissions to read the collection
143    */
144   public Collection getCollectionWithDataObjectsAndIncomingReferences(long shepardId) {
145     return getCollection(shepardId, null, false);
146   }
147 
148   /**
149    * Fetches a collection including permissions, attributes, contained data objects and incoming references.
150    * @param shepardId shepardId of the desired collection
151    * @param versionUID ID of the version to retrieve
152    * @return Collection
153    * @throws InvalidPathException if no collection could be found by shepardId
154    * @throws InvalidAuthException if the user does not have permissions to read the collection
155    */
156   public Collection getCollectionWithDataObjectsAndIncomingReferences(long shepardId, UUID versionUID) {
157     return getCollection(shepardId, versionUID, false);
158   }
159 
160   /**
161    * Return collection by shepard Id or shepard id + versionUId.
162    *
163    * @return Collection
164    * @throws InvalidPathException if no collection could be found by shepardId
165    * @throws InvalidAuthException if the user does not have permissions to read the collection
166    */
167   private Collection getCollection(long shepardId, UUID versionUID, boolean excludeDataObjectsAndIncomingReferences) {
168     Collection ret;
169     String errorMsg;
170     if (versionUID == null) {
171       ret = collectionDAO.findByShepardId(shepardId, excludeDataObjectsAndIncomingReferences);
172       errorMsg = String.format("Collection with id %s is null or deleted", shepardId);
173     } else {
174       ret = collectionDAO.findByShepardId(shepardId, versionUID, excludeDataObjectsAndIncomingReferences);
175       errorMsg = String.format("Collection with id %s and versionUID %s is null or deleted", shepardId, versionUID);
176     }
177     if (ret == null || ret.isDeleted()) {
178       throw new InvalidPathException("ID ERROR - " + errorMsg);
179     }
180     assertIsAllowedToReadCollection(shepardId);
181     cutDeleted(ret);
182 
183     return ret;
184   }
185 
186   /**
187    * Updates a Collection with new Attributes.
188    *
189    * @param shepardId  collection's shepardID
190    * @param collection which contains the new Attributes
191    * @param username   of the related user
192    * @return updated Collection
193    * @throws InvalidPathException if no collection could be found by shepardId
194    * @throws InvalidAuthException if the user does not have permissions to read or edit the collection
195    * @throws InvalidPathException if default FileContainer is specified, but the FileContainer cannot be found
196    * @throws InvalidAuthException if default FileContainer is specified, but user has no read permission on FileContainer
197    */
198   public Collection updateCollectionByShepardId(long shepardId, CollectionIO collection) {
199     Collection old = getCollectionWithDataObjectsAndIncomingReferences(shepardId);
200     assertIsAllowedToEditCollection(shepardId);
201 
202     old.setUpdatedBy(userService.getCurrentUser());
203     old.setUpdatedAt(dateHelper.getDate());
204     old.setAttributes(collection.getAttributes());
205     old.setDescription(collection.getDescription());
206     old.setName(collection.getName());
207 
208     if (collection.getDefaultFileContainerId() != null) {
209       FileContainer fileContainer = fileContainerService.getContainer(collection.getDefaultFileContainerId());
210       old.setFileContainer(fileContainer);
211     } else {
212       old.setFileContainer(null);
213     }
214 
215     Collection updated = collectionDAO.createOrUpdate(old);
216     cutDeleted(updated);
217     return updated;
218   }
219 
220   /**
221    * Deletes a Collection in Neo4j.
222    * Before a collection is deleted, a check is run to test if collection exists.
223    *
224    * @param shepardId identifies the Collection
225    * @param username  of the related user
226    * @return a boolean to determine if Collection was successfully deleted
227    * @throws InvalidPathException if no collection could be found by shepardId
228    * @throws InvalidAuthException if the user does not have permissions to read or edit the collection
229    */
230   public void deleteCollection(long shepardId) {
231     getCollection(shepardId);
232     assertIsAllowedToEditCollection(shepardId);
233 
234     var date = dateHelper.getDate();
235     var user = userService.getCurrentUser();
236     if (!collectionDAO.deleteCollectionByShepardId(shepardId, user, date)) {
237       throw new InvalidRequestException(String.format("Could not delete Collection with ShepardId %s", shepardId));
238     }
239   }
240 
241   /**
242    * Gets roles for collection specified by id
243    *
244    * @param collectionId
245    * @return Roles
246    * @throws InvalidPathException if collection with collectionId does not exist
247    * @throws InvalidAuthException if user has no read permissions on specified collection
248    */
249   public Roles getCollectionRoles(long collectionId) {
250     getCollection(collectionId);
251 
252     // We can use the collectionId as neo4jId here since permissions are global for all versions and shepardId and neo4jId are equal for the head version.
253     return permissionsService.getUserRolesOnEntity(collectionId, authenticationContext.getCurrentUserName());
254   }
255 
256   /**
257    * Gets Permissions for collection specified by id
258    *
259    * @param collectionId
260    * @return Permissions
261    * @throws InvalidPathException if collection with collectionId does not exist
262    * @throws InvalidAuthException if user has no read permissions on specified collection, or is not allowed to manage permissions on collection
263    */
264   public Permissions getCollectionPermissions(long collectionId) {
265     getCollection(collectionId);
266     assertIsAllowedToManageCollection(collectionId);
267 
268     // We can use the collectionId as neo4jId here since permissions are global for all versions and shepardId and neo4jId are equal for the head version.
269     return permissionsService.getPermissionsOfEntity(collectionId);
270   }
271 
272   /**
273    * Updates Permissions for collection specified by id
274    *
275    * @param collectionId
276    * @return Permissions
277    * @throws InvalidPathException if collection with collectionId does not exist
278    * @throws InvalidAuthException if user has no read permissions on specified collection, or is not allowed to manage permissions on collection
279    */
280   public Permissions updateCollectionPermissions(PermissionsIO newPermissions, long collectionId) {
281     getCollection(collectionId);
282     assertIsAllowedToManageCollection(collectionId);
283 
284     // We can use the collectionId as neo4jId here since permissions are global for all versions and shepardId and neo4jId are equal for the head version.
285     return permissionsService.updatePermissionsByNeo4jId(newPermissions, collectionId);
286   }
287 
288   /**
289    * Checks if the user requested the Collection is allowed to read it
290    *
291    * @throws InvalidAuthException when user is not allowed to read the Collection
292    */
293   public void assertIsAllowedToReadCollection(long collectionId) {
294     if (
295       !permissionsService.isAccessTypeAllowedForUser(
296         collectionId,
297         AccessType.Read,
298         authenticationContext.getCurrentUserName()
299       )
300     ) {
301       throw new InvalidAuthException("The requested action is forbidden by the permission policies");
302     }
303   }
304 
305   /**
306    * Checks if the user requested the Collection is allowed to edit it
307    *
308    * @throws InvalidAuthException when user is not allowed to edit the Collection
309    */
310   public void assertIsAllowedToEditCollection(long collectionId) {
311     if (
312       !permissionsService.isAccessTypeAllowedForUser(
313         collectionId,
314         AccessType.Write,
315         authenticationContext.getCurrentUserName()
316       )
317     ) {
318       throw new InvalidAuthException("The requested action is forbidden by the permission policies");
319     }
320   }
321 
322   /**
323    * Checks if the user requested the Collection is allowed to manage it
324    *
325    * @throws InvalidAuthException when user is not allowed to manage the Collection
326    */
327   public void assertIsAllowedToManageCollection(long collectionId) {
328     if (
329       !permissionsService.isAccessTypeAllowedForUser(
330         collectionId,
331         AccessType.Manage,
332         authenticationContext.getCurrentUserName()
333       )
334     ) {
335       throw new InvalidAuthException("The requested action is forbidden by the permission policies");
336     }
337   }
338 
339   private Collection cutDeleted(Collection collection) {
340     var dataObjects = collection.getDataObjects().stream().filter(d -> !d.isDeleted()).toList();
341     collection.setDataObjects(dataObjects);
342     return collection;
343   }
344 }