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