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