1 package de.dlr.shepard.auth.permission.services;
2
3 import de.dlr.shepard.auth.permission.daos.PermissionsDAO;
4 import de.dlr.shepard.auth.permission.io.PermissionsIO;
5 import de.dlr.shepard.auth.permission.model.Permissions;
6 import de.dlr.shepard.auth.permission.model.Roles;
7 import de.dlr.shepard.auth.users.entities.User;
8 import de.dlr.shepard.auth.users.entities.UserGroup;
9 import de.dlr.shepard.auth.users.services.UserGroupService;
10 import de.dlr.shepard.auth.users.services.UserService;
11 import de.dlr.shepard.common.exceptions.InvalidAuthException;
12 import de.dlr.shepard.common.exceptions.InvalidRequestException;
13 import de.dlr.shepard.common.exceptions.ShepardProcessingException;
14 import de.dlr.shepard.common.neo4j.entities.BasicEntity;
15 import de.dlr.shepard.common.util.AccessType;
16 import de.dlr.shepard.common.util.Constants;
17 import de.dlr.shepard.common.util.PermissionType;
18 import io.quarkus.cache.Cache;
19 import io.quarkus.cache.CacheName;
20 import io.quarkus.cache.CacheResult;
21 import io.quarkus.cache.CompositeCacheKey;
22 import io.quarkus.logging.Log;
23 import jakarta.enterprise.context.RequestScoped;
24 import jakarta.inject.Inject;
25 import jakarta.ws.rs.NotFoundException;
26 import jakarta.ws.rs.container.ContainerRequestContext;
27 import jakarta.ws.rs.core.PathSegment;
28 import java.util.ArrayList;
29 import java.util.HashSet;
30 import java.util.List;
31 import java.util.Optional;
32 import java.util.Set;
33 import org.apache.commons.lang3.StringUtils;
34
35 @RequestScoped
36 public class PermissionsService {
37
38 @Inject
39 @CacheName("permissions-service-cache")
40 Cache cache;
41
42 @Inject
43 PermissionsDAO permissionsDAO;
44
45 @Inject
46 UserService userService;
47
48 @Inject
49 UserGroupService userGroupService;
50
51
52
53
54
55
56
57 public Permissions createPermissions(BasicEntity entity, User user, PermissionType permissionType) {
58 return permissionsDAO.createOrUpdate(new Permissions(entity, user, PermissionType.Private));
59 }
60
61
62
63
64
65
66
67
68
69 public Optional<Permissions> getPermissionsOfEntityOptional(long entityId) {
70 var permissions = permissionsDAO.findByEntityNeo4jId(entityId);
71 if (permissions == null) {
72 Log.errorf("Permissions with entity id %s is null", entityId);
73 return Optional.empty();
74 }
75 return Optional.of(permissions);
76 }
77
78
79
80
81
82
83
84
85
86
87
88 public Permissions getPermissionsOfEntity(long entityId) {
89 User user = userService.getCurrentUser();
90 isAccessTypeAllowedForUser(entityId, AccessType.Manage, user.getUsername());
91 return getPermissionsOfEntityOptional(entityId).orElseThrow(() ->
92 new NotFoundException(String.format("Permissions with entity %s is null", entityId))
93 );
94 }
95
96
97
98
99
100
101 public Roles getUserRolesOnEntity(long entityId, String username) {
102 var perms = getPermissionsOfEntityOptional(entityId);
103 return getRoles(perms, username);
104 }
105
106
107
108
109
110
111
112
113
114 @CacheResult(cacheName = "permissions-service-cache")
115 public boolean isAccessTypeAllowedForUser(long entityId, AccessType accessType, String username) {
116 Roles userRolesOnEntity = getUserRolesOnEntity(entityId, username);
117
118 if (userRolesOnEntity.isOwner()) {
119 return true;
120 } else {
121 return switch (accessType) {
122 case Read -> userRolesOnEntity.isReader() || userRolesOnEntity.isWriter() || userRolesOnEntity.isManager();
123 case Write -> userRolesOnEntity.isWriter() || userRolesOnEntity.isManager();
124 case Manage -> userRolesOnEntity.isManager();
125 case None -> false;
126 };
127 }
128 }
129
130
131
132
133
134
135
136
137 public boolean isCurrentUserOwner(long entityId) {
138 Roles roles = getUserRolesOnEntity(entityId, userService.getCurrentUser().getUsername());
139 return roles.isOwner();
140 }
141
142 private void removeEntityFromCache(long entityId) {
143 if (cache != null) cache
144 .invalidateIf(el -> ((CompositeCacheKey) el).getKeyElements()[0].equals(entityId))
145 .await()
146 .indefinitely();
147 }
148
149
150
151
152
153
154
155
156 public Permissions updatePermissionsByNeo4jId(PermissionsIO permissionsIo, long id) {
157 Permissions oldPermissions = getPermissionsOfEntityOptional(id).orElseThrow(() ->
158 new ShepardProcessingException("Entity has no permissions attached and they cannot be updated!")
159 );
160 var newPermissions = convertPermissionsIO(permissionsIo);
161
162 if (
163 newPermissions.getOwner() == null ||
164 newPermissions.getOwner().getUniqueId().equals(oldPermissions.getOwner().getUniqueId())
165 ) {
166 oldPermissions.setOwner(oldPermissions.getOwner());
167 } else {
168 if (!isOwner(oldPermissions, userService.getCurrentUser().getUsername())) {
169 throw new InvalidAuthException("Action not allowed. Only Owners are allowed to change ownership.");
170 }
171
172 userService.getUser(newPermissions.getOwner().getUsername());
173 oldPermissions.setOwner(newPermissions.getOwner());
174 }
175
176 oldPermissions.setReader(newPermissions.getReader());
177 oldPermissions.setWriter(newPermissions.getWriter());
178 oldPermissions.setReaderGroups(newPermissions.getReaderGroups());
179 oldPermissions.setWriterGroups(newPermissions.getWriterGroups());
180 oldPermissions.setManager(newPermissions.getManager());
181 oldPermissions.setPermissionType(newPermissions.getPermissionType());
182 var res = permissionsDAO.createOrUpdate(oldPermissions);
183 removeEntityFromCache(id);
184 return res;
185 }
186
187 public boolean deletePermissions(Permissions permissions) {
188 return permissionsDAO.deleteByNeo4jId(permissions.getId());
189 }
190
191
192
193
194
195
196
197
198 public boolean isAllowed(ContainerRequestContext requestContext, AccessType accessType, String userName) {
199 List<PathSegment> pathSegments = requestContext.getUriInfo().getPathSegments();
200 var idSegment = pathSegments.size() > 1 ? pathSegments.get(1).getPath() : null;
201
202
203 if (pathSegments.get(0).getPath().equals("temp") && pathSegments.get(1).getPath().equals("migrations")) {
204 return true;
205 }
206
207
208 if (idSegment == null || idSegment.isBlank()) {
209
210 return true;
211 }
212
213
214 if (pathSegments.get(0).getPath().equals(Constants.LAB_JOURNAL_ENTRIES)) {
215
216 return true;
217 }
218
219
220 if (pathSegments.get(0).getPath().equals(Constants.USERS)) {
221
222 return true;
223 }
224
225
226 if (StringUtils.isNumeric(idSegment)) {
227 var entityId = Long.parseLong(idSegment);
228 return isAccessTypeAllowedForUser(entityId, accessType, userName);
229 }
230
231
232 if (
233 pathSegments.get(0).getPath().equals(Constants.SEARCH) &&
234 List.of(Constants.USERS, Constants.CONTAINERS, Constants.COLLECTIONS, Constants.USERGROUPS).contains(
235 pathSegments.get(1).getPath()
236 ) &&
237 pathSegments.size() == 2
238 ) {
239 return true;
240 }
241
242 return false;
243 }
244
245 private Set<String> fetchUserNames(List<UserGroup> userGroups) {
246 Set<String> ret = new HashSet<>();
247 for (UserGroup userGroup : userGroups) {
248 Optional<UserGroup> fullUserGroup = userGroupService.getUserGroupOptional(userGroup.getId());
249 if (fullUserGroup.isPresent()) {
250 for (User user : fullUserGroup.get().getUsers()) {
251 ret.add(user.getUsername());
252 }
253 }
254 }
255 return ret;
256 }
257
258 private Roles getRoles(Optional<Permissions> perms, String username) {
259 if (perms.isEmpty()) {
260
261 return new Roles(false, true, true, true);
262 }
263 var roles = new Roles(
264 isOwner(perms.get(), username),
265 isManager(perms.get(), username),
266 isWriter(perms.get(), username),
267 isReader(perms.get(), username)
268 );
269 return roles;
270 }
271
272 private boolean isOwner(Permissions perms, String username) {
273 return perms.getOwner() != null && username.equals(perms.getOwner().getUsername());
274 }
275
276 private boolean isManager(Permissions perms, String username) {
277 return perms.getManager().stream().anyMatch(u -> username.equals(u.getUsername()));
278 }
279
280 private boolean isReader(Permissions perms, String username) {
281 var pub = PermissionType.Public.equals(perms.getPermissionType());
282 var pubRead = PermissionType.PublicReadable.equals(perms.getPermissionType());
283 var reader = perms.getReader().stream().anyMatch(u -> username.equals(u.getUsername()));
284 var readerGroup = fetchUserNames(perms.getReaderGroups()).contains(username);
285 return pub || pubRead || reader || readerGroup;
286 }
287
288 private boolean isWriter(Permissions perms, String username) {
289 var pub = PermissionType.Public.equals(perms.getPermissionType());
290 var writer = perms.getWriter().stream().anyMatch(u -> username.equals(u.getUsername()));
291 var writerGroup = fetchUserNames(perms.getWriterGroups()).contains(username);
292 return pub || writer || writerGroup;
293 }
294
295
296
297
298 private Permissions convertPermissionsIO(PermissionsIO permissions) {
299 var owner = permissions.getOwner() != null
300 ? userService.getUserOptional(permissions.getOwner()).orElseGet(null)
301 : null;
302 var permissionType = permissions.getPermissionType();
303 var reader = fetchUsers(permissions.getReader());
304 var writer = fetchUsers(permissions.getWriter());
305 var readerGroups = fetchUserGroups(permissions.getReaderGroupIds());
306 var writerGroups = fetchUserGroups(permissions.getWriterGroupIds());
307 var manager = fetchUsers(permissions.getManager());
308 return new Permissions(owner, reader, writer, readerGroups, writerGroups, manager, permissionType);
309 }
310
311 private List<User> fetchUsers(String[] usernames) {
312 var result = new ArrayList<User>(usernames.length);
313 for (var username : usernames) {
314 if (username == null) {
315 continue;
316 }
317
318 Optional<User> user = userService.getUserOptional(username);
319 user.ifPresent((User u) -> result.add(u));
320 }
321 return result;
322 }
323
324 private List<UserGroup> fetchUserGroups(long[] userGroupIds) {
325 var result = new ArrayList<UserGroup>(userGroupIds.length);
326 for (var userGroupId : userGroupIds) {
327 Optional<UserGroup> userGroup = userGroupService.getUserGroupOptional(userGroupId);
328 if (userGroup.isPresent()) {
329 result.add(userGroup.get());
330 }
331 }
332 return result;
333 }
334 }