1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.mcr.acl.accesskey;
20
21 import static java.nio.charset.StandardCharsets.UTF_8;
22
23 import java.security.NoSuchAlgorithmException;
24 import java.util.Date;
25 import java.util.List;
26
27 import org.apache.logging.log4j.LogManager;
28 import org.apache.logging.log4j.Logger;
29 import org.mycore.access.MCRAccessCacheHelper;
30 import org.mycore.access.MCRAccessManager;
31 import org.mycore.backend.jpa.MCREntityManagerProvider;
32 import org.mycore.common.MCRException;
33 import org.mycore.common.MCRSessionMgr;
34 import org.mycore.common.MCRUtils;
35 import org.mycore.common.config.MCRConfiguration2;
36 import org.mycore.crypt.MCRCipher;
37 import org.mycore.crypt.MCRCipherManager;
38 import org.mycore.crypt.MCRCryptKeyFileNotFoundException;
39 import org.mycore.crypt.MCRCryptKeyNoPermissionException;
40 import org.mycore.datamodel.metadata.MCRObjectID;
41 import org.mycore.mcr.acl.accesskey.exception.MCRAccessKeyCollisionException;
42 import org.mycore.mcr.acl.accesskey.exception.MCRAccessKeyException;
43 import org.mycore.mcr.acl.accesskey.exception.MCRAccessKeyInvalidSecretException;
44 import org.mycore.mcr.acl.accesskey.exception.MCRAccessKeyInvalidTypeException;
45 import org.mycore.mcr.acl.accesskey.exception.MCRAccessKeyNotFoundException;
46 import org.mycore.mcr.acl.accesskey.model.MCRAccessKey;
47
48 import jakarta.persistence.EntityManager;
49
50
51
52
53 public final class MCRAccessKeyManager {
54
55 private static final Logger LOGGER = LogManager.getLogger();
56
57 private static final String SECRET_STORAGE_MODE_PROP_PREFX = "MCR.ACL.AccessKey.Secret.Storage.Mode";
58
59 private static final String SECRET_STORAGE_MODE = MCRConfiguration2
60 .getStringOrThrow(SECRET_STORAGE_MODE_PROP_PREFX);
61
62 private static final int HASHING_ITERATIONS = MCRConfiguration2
63 .getInt(SECRET_STORAGE_MODE_PROP_PREFX + ".Hash.Iterations").orElse(1000);
64
65
66
67
68
69
70
71 public static synchronized List<MCRAccessKey> listAccessKeys(final MCRObjectID objectId) {
72 final EntityManager em = MCREntityManagerProvider.getCurrentEntityManager();
73 final List<MCRAccessKey> accessKeys = em.createNamedQuery("MCRAccessKey.getWithObjectId", MCRAccessKey.class)
74 .setParameter("objectId", objectId)
75 .getResultList();
76 for (MCRAccessKey accessKey : accessKeys) {
77 em.detach(accessKey);
78 }
79 return accessKeys;
80 }
81
82
83
84
85
86
87
88 public static boolean isValidType(final String type) {
89 return (MCRAccessManager.PERMISSION_READ.equals(type) || MCRAccessManager.PERMISSION_WRITE.equals(type));
90 }
91
92
93
94
95
96
97
98 public static boolean isValidSecret(final String secret) {
99 return secret.length() > 0;
100 }
101
102
103
104
105
106
107
108
109
110 public static String hashSecret(final String secret, final MCRObjectID objectId) throws MCRException {
111 switch (SECRET_STORAGE_MODE) {
112 case "plain":
113 return secret;
114 case "crypt":
115 try {
116 final MCRCipher cipher = MCRCipherManager.getCipher("accesskey");
117 return cipher.encrypt(objectId.toString() + secret);
118 } catch (MCRCryptKeyFileNotFoundException | MCRCryptKeyNoPermissionException e) {
119 throw new MCRException(e);
120 }
121 case "hash":
122 try {
123 return MCRUtils.asSHA256String(HASHING_ITERATIONS, objectId.toString().getBytes(UTF_8), secret);
124 } catch (NoSuchAlgorithmException e) {
125 throw new MCRException("Cannot hash secret.", e);
126 }
127 default:
128 throw new MCRException("Please configure a valid storage mode for secret.");
129 }
130 }
131
132
133
134
135
136
137
138
139
140 public static synchronized void createAccessKey(final MCRObjectID objectId, final MCRAccessKey accessKey)
141 throws MCRException {
142 final String secret = accessKey.getSecret();
143 if (secret == null || !isValidSecret(secret)) {
144 throw new MCRAccessKeyInvalidSecretException("Incorrect secret.");
145 }
146 accessKey.setSecret(hashSecret(secret, objectId));
147 accessKey.setCreatedBy(MCRSessionMgr.getCurrentSession().getUserInformation().getUserID());
148 accessKey.setCreated(new Date());
149 accessKey.setLastModifiedBy(MCRSessionMgr.getCurrentSession().getUserInformation().getUserID());
150 accessKey.setLastModified(new Date());
151 if (accessKey.getIsActive() == null) {
152 accessKey.setIsActive(true);
153 }
154 addAccessKey(objectId, accessKey);
155 }
156
157
158
159
160
161
162
163
164
165 private static synchronized void addAccessKey(final MCRObjectID objectId, final MCRAccessKey accessKey)
166 throws MCRException {
167 final String secret = accessKey.getSecret();
168 if (secret == null) {
169 LOGGER.debug("Incorrect secret.");
170 throw new MCRAccessKeyInvalidSecretException("Incorrect secret.");
171 }
172 final String type = accessKey.getType();
173 if (type == null || !isValidType(type)) {
174 LOGGER.debug("Invalid permission type.");
175 throw new MCRAccessKeyInvalidTypeException("Invalid permission type.");
176 }
177 if (getAccessKeyWithSecret(objectId, secret) == null) {
178 accessKey.setId(0);
179 accessKey.setObjectId(objectId);
180 final EntityManager em = MCREntityManagerProvider.getCurrentEntityManager();
181 em.persist(accessKey);
182 em.detach(accessKey);
183 } else {
184 LOGGER.debug("Key collision.");
185 throw new MCRAccessKeyCollisionException("Key collision.");
186 }
187 }
188
189
190
191
192
193
194
195
196 public static synchronized void addAccessKeys(final MCRObjectID objectId, final List<MCRAccessKey> accessKeys)
197 throws MCRAccessKeyException {
198 for (MCRAccessKey accessKey : accessKeys) {
199 addAccessKey(objectId, accessKey);
200 }
201 }
202
203
204
205
206 public static void clearAccessKeys() {
207 MCREntityManagerProvider.getCurrentEntityManager()
208 .createNamedQuery("MCRAccessKey.clear")
209 .executeUpdate();
210 }
211
212
213
214
215
216
217 public static void clearAccessKeys(final MCRObjectID objectId) {
218 MCREntityManagerProvider.getCurrentEntityManager()
219 .createNamedQuery("MCRAccessKey.clearWithObjectId")
220 .setParameter("objectId", objectId)
221 .executeUpdate();
222 }
223
224
225
226
227
228
229
230 public static synchronized void removeAccessKey(final MCRObjectID objectId, final String secret)
231 throws MCRAccessKeyNotFoundException {
232 final MCRAccessKey accessKey = getAccessKeyWithSecret(objectId, secret);
233 if (accessKey == null) {
234 LOGGER.debug("Key does not exist.");
235 throw new MCRAccessKeyNotFoundException("Key does not exist.");
236 } else {
237 MCRAccessCacheHelper.clearAllPermissionCaches(objectId.toString());
238 final EntityManager em = MCREntityManagerProvider.getCurrentEntityManager();
239 em.remove(em.contains(accessKey) ? accessKey : em.merge(accessKey));
240 }
241 }
242
243
244
245
246
247
248
249
250
251 public static synchronized void updateAccessKey(final MCRObjectID objectId, final String secret,
252 final MCRAccessKey updatedAccessKey) throws MCRException {
253 final MCRAccessKey accessKey = getAccessKeyWithSecret(objectId, secret);
254 if (accessKey != null) {
255 final String type = updatedAccessKey.getType();
256 if (type != null && !accessKey.getType().equals(type)) {
257 if (isValidType(type)) {
258 MCRAccessCacheHelper.clearAllPermissionCaches(objectId.toString());
259 accessKey.setType(type);
260 } else {
261 LOGGER.debug("Unkown Type.");
262 throw new MCRAccessKeyInvalidTypeException("Unknown permission type.");
263 }
264 }
265 final Boolean isActive = updatedAccessKey.getIsActive();
266 if (isActive != null) {
267 MCRAccessCacheHelper.clearAllPermissionCaches(objectId.toString());
268 accessKey.setIsActive(isActive);
269 }
270 final Date expiration = updatedAccessKey.getExpiration();
271 if (expiration != null) {
272 MCRAccessCacheHelper.clearAllPermissionCaches(objectId.toString());
273 accessKey.setExpiration(expiration);
274 }
275 final String comment = updatedAccessKey.getComment();
276 if (comment != null) {
277 accessKey.setComment(comment);
278 }
279 accessKey.setLastModifiedBy(MCRSessionMgr.getCurrentSession().getUserInformation().getUserID());
280 accessKey.setLastModified(new Date());
281 final EntityManager em = MCREntityManagerProvider.getCurrentEntityManager();
282 em.merge(accessKey);
283 } else {
284 LOGGER.debug("Key does not exist.");
285 throw new MCRAccessKeyNotFoundException("Key does not exist.");
286 }
287 }
288
289
290
291
292
293
294
295
296 public static synchronized MCRAccessKey getAccessKeyWithSecret(final MCRObjectID objectId, final String secret) {
297 final EntityManager em = MCREntityManagerProvider.getCurrentEntityManager();
298 final MCRAccessKey accessKey = em.createNamedQuery("MCRAccessKey.getWithSecret", MCRAccessKey.class)
299 .setParameter("objectId", objectId)
300 .setParameter("secret", secret)
301 .getResultList()
302 .stream()
303 .findFirst()
304 .orElse(null);
305 if (accessKey != null) {
306 em.detach(accessKey);
307 }
308 return accessKey;
309 }
310
311
312
313
314
315
316
317
318 public static synchronized List<MCRAccessKey> listAccessKeysWithType(final MCRObjectID objectId,
319 final String type) {
320 final EntityManager em = MCREntityManagerProvider.getCurrentEntityManager();
321 final List<MCRAccessKey> accessKeys = em.createNamedQuery("MCRAccessKey.getWithType", MCRAccessKey.class)
322 .setParameter("objectId", objectId)
323 .setParameter("type", type)
324 .getResultList();
325 for (MCRAccessKey accessKey : accessKeys) {
326 em.detach(accessKey);
327 }
328 return accessKeys;
329 }
330 }