View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.mcr.acl.accesskey;
20  
21  import java.util.ArrayList;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Optional;
25  import java.util.Set;
26  import java.util.concurrent.TimeUnit;
27  import java.util.stream.Collectors;
28  
29  import org.mycore.access.MCRAccessCacheHelper;
30  import org.mycore.access.MCRAccessManager;
31  import org.mycore.common.MCRException;
32  import org.mycore.common.MCRSession;
33  import org.mycore.common.MCRSessionMgr;
34  import org.mycore.common.MCRUserInformation;
35  import org.mycore.common.config.MCRConfiguration2;
36  import org.mycore.datamodel.metadata.MCRMetadataManager;
37  import org.mycore.datamodel.metadata.MCRObjectID;
38  import org.mycore.mcr.acl.accesskey.model.MCRAccessKey;
39  import org.mycore.mcr.acl.accesskey.exception.MCRAccessKeyException;
40  import org.mycore.mcr.acl.accesskey.exception.MCRAccessKeyNotFoundException;
41  import org.mycore.user2.MCRUser;
42  import org.mycore.user2.MCRUserAttribute;
43  import org.mycore.user2.MCRUserManager;
44  
45  /**
46   * Methods for setting and removing {@link MCRAccessKey} for users.
47   */
48  public class MCRAccessKeyUtils {
49      /**
50       * Prefix for user attribute name for value
51       */
52      public static final String ACCESS_KEY_PREFIX = "acckey_";
53  
54      private static final String ACCESS_KEY_STRATEGY_PROP_PREFX = "MCR.ACL.AccessKey.Strategy";
55  
56      private static final String ALLOWED_OBJECT_TYPES_PROP = ACCESS_KEY_STRATEGY_PROP_PREFX + ".AllowedObjectTypes";
57  
58      private static final String ALLOWED_SESSION_PERMISSION_TYPES_PROP = ACCESS_KEY_STRATEGY_PROP_PREFX
59          + ".AllowedSessionPermissionTypes";
60  
61      private static Set<String> allowedObjectTypes = loadAllowedObjectTypes();
62  
63      private static Set<String> allowedSessionPermissionTypes = loadAllowedSessionPermissionTypes();
64  
65      private static Set<String> parseListStringToSet(final Optional<String> listString) {
66          return listString.stream()
67              .flatMap(MCRConfiguration2::splitValue)
68              .collect(Collectors.toSet());
69      }
70  
71      private static Set<String> loadAllowedObjectTypes() {
72          MCRConfiguration2.addPropertyChangeEventLister(ALLOWED_OBJECT_TYPES_PROP::equals, (p1, p2, p3) -> {
73              allowedObjectTypes = parseListStringToSet(p3);
74          });
75          return parseListStringToSet(MCRConfiguration2.getString(ALLOWED_OBJECT_TYPES_PROP));
76      }
77  
78      private static Set<String> loadAllowedSessionPermissionTypes() {
79          MCRConfiguration2.addPropertyChangeEventLister(ALLOWED_SESSION_PERMISSION_TYPES_PROP::equals, (p1, p2, p3) -> {
80              allowedSessionPermissionTypes = parseListStringToSet(p3);
81          });
82          return parseListStringToSet(MCRConfiguration2.getString(ALLOWED_SESSION_PERMISSION_TYPES_PROP));
83      }
84  
85      /**
86       * Adds the value of {@link MCRAccessKey} as an attribute to a {@link MCRSession} for {@link MCRObjectID}.
87       *
88       * @param session the {@link MCRSession}
89       * @param objectId the {@link MCRObjectID}
90       * @param value the value of a {@link MCRAccessKey}
91       * @throws MCRException if there is no matching {@link MCRAccessKey} with the same value or not allowed.
92       */
93      public static synchronized void addAccessKeySecretForObject(final MCRSession session, final MCRObjectID objectId,
94          final String value) throws MCRException {
95          if (!isAccessKeyForSessionAllowed()) {
96              throw new MCRAccessKeyException("Access keys is not allowed.");
97          }
98          if (isAccessKeyForObjectTypeAllowed(objectId.getTypeId())) {
99              final String secret = MCRAccessKeyManager.hashSecret(value, objectId);
100             final MCRAccessKey accessKey = MCRAccessKeyManager.getAccessKeyWithSecret(objectId, secret);
101             if (accessKey == null) {
102                 throw new MCRAccessKeyNotFoundException("Access key is invalid.");
103             } else if (isAccessKeyForSessionAllowed(accessKey.getType())) {
104                 session.put(getAttributeName(objectId), secret);
105                 MCRAccessManager.invalidPermissionCacheByID(objectId.toString());
106             } else {
107                 throw new MCRAccessKeyException("Access key is not allowed.");
108             }
109         } else {
110             throw new MCRAccessKeyException("Access key is not allowed.");
111         }
112     }
113 
114     /**
115      * Adds the value of {@link MCRAccessKey} as an attribute to a {@link MCRSession} for {@link MCRObjectID}
116      * including derivates.
117      *
118      * @param session the {@link MCRSession}
119      * @param objectId the {@link MCRObjectID}
120      * @param value the value of a {@link MCRAccessKey}
121      * @throws MCRException if there is no matching {@link MCRAccessKey} with the same value or not allowed.
122      */
123     public static synchronized void addAccessKeySecret(final MCRSession session, final MCRObjectID objectId,
124         final String value) throws MCRException {
125         if (!isAccessKeyForSessionAllowed()) {
126             throw new MCRAccessKeyException("Access keys is not allowed.");
127         }
128         if ("derivate".equals(objectId.getTypeId())) {
129             addAccessKeySecretForObject(session, objectId, value);
130         } else {
131             boolean success = false;
132             try {
133                 addAccessKeySecretForObject(session, objectId, value);
134                 MCRAccessCacheHelper.clearPermissionCache(objectId.toString());
135                 success = true;
136             } catch (MCRAccessKeyException e) {
137                 //
138             }
139             final List<MCRObjectID> derivateIds = MCRMetadataManager.getDerivateIds(objectId, 0, TimeUnit.SECONDS);
140             for (final MCRObjectID derivateId : derivateIds) {
141                 try {
142                     addAccessKeySecretForObject(session, derivateId, value);
143                     success = true;
144                 } catch (MCRAccessKeyException e) {
145                     //
146                 }
147             }
148             if (!success) {
149                 throw new MCRAccessKeyException("Access key is invalid or is not allowed.");
150             }
151         }
152     }
153 
154     /**
155      * Adds the value of a {@link MCRAccessKey} as user attribute to a {@link MCRUser} for a {@link MCRObjectID}.
156      *
157      * @param user the {@link MCRUser} the value should assigned
158      * @param objectId the {@link MCRObjectID}
159      * @param value the value of a {@link MCRAccessKey}
160      * @throws MCRException if there is no matching {@link MCRAccessKey} with the same value.
161      */
162     public static synchronized void addAccessKeySecretForObject(final MCRUser user, final MCRObjectID objectId,
163         final String value) throws MCRException {
164         if (isAccessKeyForObjectTypeAllowed(objectId.getTypeId())) {
165             final String secret = MCRAccessKeyManager.hashSecret(value, objectId);
166             final MCRAccessKey accessKey = MCRAccessKeyManager.getAccessKeyWithSecret(objectId, secret);
167             if (accessKey == null) {
168                 throw new MCRAccessKeyNotFoundException("Access key is invalid.");
169             } else {
170                 user.setUserAttribute(getAttributeName(objectId), secret);
171                 MCRUserManager.updateUser(user);
172                 MCRAccessManager.invalidPermissionCacheByID(objectId.toString());
173             }
174         } else {
175             throw new MCRAccessKeyException("Access key is not allowed.");
176         }
177     }
178 
179     /**
180      * Adds the value of a {@link MCRAccessKey} as user attribute to a {@link MCRUser} for a {@link MCRObjectID}
181      * including derivates.
182      *
183      * @param user the {@link MCRUser} the value should assigned
184      * @param objectId the {@link MCRObjectID}
185      * @param value the value of a {@link MCRAccessKey}
186      * @throws MCRException if there is no matching {@link MCRAccessKey} with the same value.
187      */
188     public static synchronized void addAccessKeySecret(final MCRUser user, final MCRObjectID objectId,
189         final String value) throws MCRException {
190         if ("derivate".equals(objectId.getTypeId())) {
191             addAccessKeySecretForObject(user, objectId, value);
192         } else {
193             boolean success = false;
194             try {
195                 addAccessKeySecretForObject(user, objectId, value);
196                 MCRAccessCacheHelper.clearPermissionCache(objectId.toString());
197                 success = true;
198             } catch (MCRAccessKeyException e) {
199                 //
200             }
201             final List<MCRObjectID> derivateIds = MCRMetadataManager.getDerivateIds(objectId, 0, TimeUnit.SECONDS);
202             for (final MCRObjectID derivateId : derivateIds) {
203                 try {
204                     addAccessKeySecretForObject(user, derivateId, value);
205                     success = true;
206                 } catch (MCRAccessKeyException e) {
207                     //
208                 }
209             }
210             if (!success) {
211                 throw new MCRAccessKeyException("Access key is invalid or is not allowed.");
212             }
213         }
214     }
215 
216     /**
217      * Adds the value of {@link MCRAccessKey} as an attribute to the current {@link MCRSession} for {@link MCRObjectID}.
218      *
219      * @param objectId the {@link MCRObjectID}
220      * @param value the value of a {@link MCRAccessKey}
221      * @throws MCRException if there is no matching {@link MCRAccessKey} with the same value.
222      */
223     public static synchronized void addAccessKeySecretToCurrentSession(final MCRObjectID objectId, final String value)
224         throws MCRException {
225         addAccessKeySecret(MCRSessionMgr.getCurrentSession(), objectId, value);
226     }
227 
228     /**
229      * Adds the value of {@link MCRAccessKey} as user attribute to the current {@link MCRUser} for {@link MCRObjectID}.
230      *
231      * @param objectId the {@link MCRObjectID}
232      * @param value the value of a {@link MCRAccessKey}
233      * @throws MCRException if there is no matching {@link MCRAccessKey} with the same value.
234      */
235     public static synchronized void addAccessKeySecretToCurrentUser(final MCRObjectID objectId, final String value)
236         throws MCRException {
237         addAccessKeySecret(MCRUserManager.getCurrentUser(), objectId, value);
238     }
239 
240     /**
241      * Lists all users which own at least an access key user attribute in given range
242      *
243      * @param offset the offset
244      * @param limit the limit
245      * @return a list with all users which own at least an access key in given range
246      */
247     private static List<MCRUser> listUsersWithAccessKey(final int offset, final int limit) {
248         return MCRUserManager.listUsers(null, null, null, null, ACCESS_KEY_PREFIX + "*", offset, limit);
249     }
250 
251     /**
252      * Cleans all access key secret attributes of users if the corresponding key does not exist.
253      */
254     public static void cleanUpUserAttributes() {
255         final Set<MCRUserAttribute> validAttributes = new HashSet<>();
256         final Set<MCRUserAttribute> deadAttributes = new HashSet<>();
257         int offset = 0;
258         final int limit = 1024;
259         List<MCRUser> users = new ArrayList<>();
260         do {
261             users = listUsersWithAccessKey(offset, limit);
262             for (final MCRUser user : users) {
263                 final List<MCRUserAttribute> attributes = user.getAttributes()
264                     .stream()
265                     .filter(attribute -> attribute.getName().startsWith(MCRAccessKeyUtils.ACCESS_KEY_PREFIX))
266                     .filter(attribute -> !validAttributes.contains(attribute))
267                     .collect(Collectors.toList());
268                 for (MCRUserAttribute attribute : attributes) {
269                     final String attributeName = attribute.getName();
270                     final MCRObjectID objectId = MCRObjectID.getInstance(attributeName.substring(
271                         attributeName.indexOf("_") + 1));
272                     if (deadAttributes.contains(attribute)) {
273                         MCRAccessKeyUtils.removeAccessKeySecret(user, objectId);
274                     } else {
275                         if (MCRAccessKeyManager.getAccessKeyWithSecret(objectId, attribute.getValue()) != null) {
276                             validAttributes.add(attribute);
277                         } else {
278                             MCRAccessKeyUtils.removeAccessKeySecret(user, objectId);
279                             deadAttributes.add(attribute);
280                         }
281                     }
282                 }
283             }
284             offset += limit;
285         } while (users.size() == limit);
286     }
287 
288     /**
289      * Fetches access key value from session attribute for a {@link MCRObjectID}.
290      *
291      * @param session the {@link MCRSession}
292      * @param objectId the {@link MCRObjectID}
293      * @return secret or null
294      */
295     public static synchronized String getAccessKeySecret(final MCRSession session, final MCRObjectID objectId) {
296         final Object secret = session.get(getAttributeName(objectId));
297         if (secret != null) {
298             return (String) secret;
299         }
300         return null;
301     }
302 
303     /**
304      * Fetches access key value from user attribute for a {@link MCRObjectID}.
305      *
306      * @param userInformation the {@link MCRUserInformation}
307      * @param objectId the {@link MCRObjectID}
308      * @return secret or null
309      */
310     public static synchronized String getAccessKeySecret(final MCRUserInformation userInformation,
311         final MCRObjectID objectId) {
312         return userInformation.getUserAttribute(getAttributeName(objectId));
313     }
314 
315     /**
316      * Fetches access key value from session attribute for a {@link MCRObjectID}.
317      *
318      * @param objectId the {@link MCRObjectID}
319      * @return secret or null
320      */
321     public static synchronized String getAccessKeySecretFromCurrentSession(final MCRObjectID objectId) {
322         return getAccessKeySecret(MCRSessionMgr.getCurrentSession(), objectId);
323     }
324 
325     /**
326      * Fetches access key value from user attribute for a {@link MCRObjectID}.
327      *
328      * @param objectId the {@link MCRObjectID}
329      * @return secret or null
330      */
331     public static synchronized String getAccessKeySecretFromCurrentUser(final MCRObjectID objectId) {
332         return getAccessKeySecret(MCRSessionMgr.getCurrentSession().getUserInformation(), objectId);
333     }
334 
335     /**
336      * Returns the attribute name for user and session of an access key value
337      *
338      * @param objectId the {@link MCRObjectID}
339      * @return the attribute name
340      */
341     private static String getAttributeName(final MCRObjectID objectId) {
342         return ACCESS_KEY_PREFIX + objectId.toString();
343     }
344 
345     /**
346      * Retrieves linked access key if exists from session
347      *
348      * @param session the {@link MCRSession}
349      * @param objectId of a {@link MCRObjectID}
350      * @return access key
351      */
352     public static MCRAccessKey getLinkedAccessKey(final MCRSession session,
353         final MCRObjectID objectId) {
354         final String secret = getAccessKeySecret(session, objectId);
355         if (secret != null) {
356             return MCRAccessKeyManager.getAccessKeyWithSecret(objectId, secret);
357         }
358         return null;
359     }
360 
361     /**
362      * Retrieves linked access key if exists from user
363      *
364      * @param userInformation the {@link MCRUserInformation}
365      * @param objectId of a {@link MCRObjectID}
366      * @return access key
367      */
368     public static MCRAccessKey getLinkedAccessKey(final MCRUserInformation userInformation,
369         final MCRObjectID objectId) {
370         final String secret = getAccessKeySecret(userInformation, objectId);
371         if (secret != null) {
372             return MCRAccessKeyManager.getAccessKeyWithSecret(objectId, secret);
373         }
374         return null;
375     }
376 
377     /**
378      * Retrieves linked access key if exists from current session
379      *
380      * @param objectId of a {@link MCRObjectID}
381      * @return access key
382      */
383     public static MCRAccessKey getLinkedAccessKeyFromCurrentSession(final MCRObjectID objectId) {
384         return getLinkedAccessKey(MCRSessionMgr.getCurrentSession(), objectId);
385     }
386 
387     /**
388      * Retrieves linked access key if exists from current user
389      *
390      * @param objectId of a {@link MCRObjectID}
391      * @return access key
392      */
393     public static MCRAccessKey getLinkedAccessKeyFromCurrentUser(final MCRObjectID objectId) {
394         return getLinkedAccessKey(MCRSessionMgr.getCurrentSession().getUserInformation(), objectId);
395     }
396 
397     /**
398      * Deletes the access key value attribute from given {@link MCRSession} for {@link MCRObjectID}.
399      *
400      * @param session the {@link MCRSession}
401      * @param objectId the {@link MCRObjectID}
402      */
403     public static synchronized void removeAccessKeySecret(final MCRSession session, final MCRObjectID objectId) {
404         session.deleteObject(getAttributeName(objectId));
405         MCRAccessCacheHelper.clearPermissionCache(objectId.toString());
406     }
407 
408     /**
409      * Deletes the access key value user attribute from given {@link MCRUser} for {@link MCRObjectID}.
410      *
411      * @param user the {@link MCRUser}
412      * @param objectId the {@link MCRObjectID}
413      */
414     public static synchronized void removeAccessKeySecret(final MCRUser user, final MCRObjectID objectId) {
415         user.getAttributes().removeIf(ua -> ua.getName().equals(getAttributeName(objectId)));
416         MCRUserManager.updateUser(user);
417         MCRAccessCacheHelper.clearPermissionCache(objectId.toString());
418     }
419 
420     /**
421      * Deletes access key value attribute from current {@link MCRSession} for a {@link MCRObjectID}.
422      *
423      * @param objectId the {@link MCRObjectID}
424      */
425     public static synchronized void removeAccessKeySecretFromCurrentSession(final MCRObjectID objectId) {
426         removeAccessKeySecret(MCRSessionMgr.getCurrentSession(), objectId);
427     }
428 
429     /**
430      * Deletes access key value user attribute from current {@link MCRUser} for a {@link MCRObjectID}.
431      *
432      * @param objectId the {@link MCRObjectID}
433      */
434     public static synchronized void removeAccessKeySecretFromCurrentUser(final MCRObjectID objectId) {
435         removeAccessKeySecret(MCRUserManager.getCurrentUser(), objectId);
436     }
437 
438     public static boolean isAccessKeyForSessionAllowed() {
439         return !allowedSessionPermissionTypes.isEmpty();
440     }
441 
442     public static boolean isAccessKeyForSessionAllowed(final String permission) {
443         return allowedSessionPermissionTypes.contains(permission);
444     }
445 
446     public static boolean isAccessKeyForObjectTypeAllowed(final String type) {
447         return allowedObjectTypes.contains(type);
448     }
449 
450     public static Set<String> getAllowedSessionPermissionTypes() {
451         return allowedSessionPermissionTypes;
452     }
453 }