diff --git a/mycore-acl/src/main/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeyContextService.java b/mycore-acl/src/main/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeyContextService.java new file mode 100644 index 0000000000..7491b7b870 --- /dev/null +++ b/mycore-acl/src/main/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeyContextService.java @@ -0,0 +1,193 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.mcr.acl.accesskey.service; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import org.mycore.access.MCRAccessCacheHelper; +import org.mycore.access.MCRAccessManager; +import org.mycore.datamodel.metadata.MCRMetadataManager; +import org.mycore.datamodel.metadata.MCRObjectID; +import org.mycore.mcr.acl.accesskey.dto.MCRAccessKeyDto; +import org.mycore.mcr.acl.accesskey.exception.MCRAccessKeyException; +import org.mycore.mcr.acl.accesskey.exception.MCRAccessKeyNotFoundException; + +/** + * Service that provides methods to active or deactivate access key for context. + * + * @param the context + */ +public abstract class MCRAccessKeyContextService { + + private final MCRAccessKeyService accessKeyService; + + /** + * Constructs new {@link MCRAccessKeyContextService} instance. + * + * @param accessKeyService the access key service + */ + public MCRAccessKeyContextService(MCRAccessKeyService accessKeyService) { + this.accessKeyService = accessKeyService; + } + + /** + * Activates a access key for reference by secret to current context. + * + * @param reference the object or resource reference for which the access key will be activated + * @param secret the secret to active access key + * @throws UnsupportedOperationException if the reference is not supported + * @throws MCRAccessKeyException if no access key has been activated + */ + public void activateAccessKey(String reference, String secret) { + if (MCRObjectID.isValid(reference)) { + activateAccessKeyForObject(MCRObjectID.getInstance(reference), secret); + } else { + throw new UnsupportedOperationException("Reference: " + reference + " is not supported"); + } + } + + /** + * Retrieves the currently activated access key for the given reference. + * + * @param reference the reference for which the access key is retrieved + * @return the activated {@link MCRAccessKeyDto}, or {@code null} if no key is active + */ + public MCRAccessKeyDto findActiveAccessKey(String reference) { + return Optional.ofNullable(getAccessKeyFromContext(getCurrentContext(), getAttributeName(reference))) + .map(v -> accessKeyService.findAccessKeyByReferenceAndSecret(reference, v)).orElse(null); + } + + /** + * Deactivates the access key for the given reference in the current context. + * + * @param reference the reference for which the access key will be deactivated + */ + public void deactivateAccessKey(String reference) { + removeAccessKeyFromContext(getCurrentContext(), getAttributeName(reference)); + MCRAccessCacheHelper.clearPermissionCache(reference); + } + + /** + * Returns the {@link MCRAccessKeyService} used to manage access keys. + * + * @return the access key service + */ + protected MCRAccessKeyService getService() { + return accessKeyService; + } + + private String getAttributeName(String reference) { + return getAccessKeyAttributePrefix() + reference; + } + + private void addAccessKeyForObjectToContext(T context, MCRObjectID objectId, String secret) { + final String processedSecret = accessKeyService.processSecret(objectId.toString(), secret); + final MCRAccessKeyDto accessKeyDto + = accessKeyService.findAccessKeyByReferenceAndSecret(objectId.toString(), processedSecret); + if (accessKeyDto == null) { + throw new MCRAccessKeyNotFoundException("Access key is invalid."); + } else if (isAccessKeyAllowedInContext(accessKeyDto)) { + setAccessKeyAttribute(context, getAttributeName(objectId.toString()), processedSecret); + MCRAccessManager.invalidPermissionCacheByID(objectId.toString()); + } else { + throw new MCRAccessKeyException("Access key is not allowed."); + } + } + + private void activateAccessKeyForObject(MCRObjectID objectId, String secret) { + if (!MCRMetadataManager.exists(objectId)) { + throw new MCRAccessKeyException("No access key could be activated"); + } + final T context = getCurrentContext(); + if ("derivate".equals(objectId.getTypeId())) { + addAccessKeyForObjectToContext(context, objectId, secret); + } else { + boolean success = false; + try { + addAccessKeyForObjectToContext(context, objectId, secret); + MCRAccessCacheHelper.clearPermissionCache(objectId.toString()); + success = true; + } catch (MCRAccessKeyException e) { + // ignored + } + final List derivateIds = MCRMetadataManager.getDerivateIds(objectId, 0, TimeUnit.SECONDS); + for (final MCRObjectID derivateId : derivateIds) { + try { + addAccessKeyForObjectToContext(context, derivateId, secret); + success = true; + } catch (MCRAccessKeyException e) { + // ignored + } + } + if (!success) { + throw new MCRAccessKeyException("No access key could be activated"); + } + } + } + + /** + * Returns the current context in which access keys are managed. + * + * @return the current context + */ + abstract T getCurrentContext(); + + /** + * Adds an access key attribute to the given context. + * + * @param context the context in which the access key attribute is added + * @param attributeName the name of the attribute to be added + * @param secret the secret of the access key + */ + abstract void setAccessKeyAttribute(T context, String attributeName, String secret); + + /** + * Checks if adding the access key to the context is allowed based on the provided access key DTO. + * + * @param accessKeyDto the access key data transfer object + * @return {@code true} if adding the access key is allowed, {@code false} otherwise + */ + abstract boolean isAccessKeyAllowedInContext(MCRAccessKeyDto accessKeyDto); + + /** + * Deletes the access key attribute from the given context. + * + * @param context the context from which the access key attribute is removed + * @param attributeName the name of the attribute to be removed + */ + abstract void removeAccessKeyFromContext(T context, String attributeName); + + /** + * Retrieves the secret of the access key attribute from the given context. + * + * @param context the context from which the attribute secret is retrieved + * @param attributeName the name of the attribute + * @return the secret of the access key attribute, or {@code null} if no attribute is found + */ + abstract String getAccessKeyFromContext(T context, String attributeName); + + /** + * Returns the prefix to be used for access key attribute names. + * + * @return the prefix for attribute names + */ + abstract String getAccessKeyAttributePrefix(); +} diff --git a/mycore-acl/src/main/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeyServiceFactory.java b/mycore-acl/src/main/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeyServiceFactory.java index 7c035f3632..f0367bd96a 100644 --- a/mycore-acl/src/main/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeyServiceFactory.java +++ b/mycore-acl/src/main/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeyServiceFactory.java @@ -38,8 +38,16 @@ public final class MCRAccessKeyServiceFactory { private static volatile MCRAccessKeyService service; + private static volatile MCRAccessKeyUserService userService; + + private static volatile MCRAccessKeySessionService sessionService; + private static final Lock SERVICE_LOCK = new ReentrantLock(); + private static final Lock USER_SERVICE_LOCK = new ReentrantLock(); + + private static final Lock SESSION_SERVICE_LOCK = new ReentrantLock(); + private MCRAccessKeyServiceFactory() { } @@ -64,6 +72,52 @@ public static MCRAccessKeyService getAccessKeyService() { return service; } + /** + * Returns single access key user service instance. + * + * @return the instance + */ + public static MCRAccessKeyUserService getAccessKeyUserService() { + if (userService == null) { + try { + USER_SERVICE_LOCK.lock(); + if (userService == null) { + userService = createUserService(getAccessKeyService()); + } + } finally { + USER_SERVICE_LOCK.unlock(); + } + } + return userService; + } + + /** + * Returns single access key session service instance. + * + * @return the instance + */ + public static MCRAccessKeySessionService getAccessKeySessionService() { + if (sessionService == null) { + try { + SESSION_SERVICE_LOCK.lock(); + if (sessionService == null) { + sessionService = createSessionService(getAccessKeyService()); + } + } finally { + SESSION_SERVICE_LOCK.unlock(); + } + } + return sessionService; + } + + private static MCRAccessKeyUserService createUserService(MCRAccessKeyService service) { + return new MCRAccessKeyUserService(service); + } + + private static MCRAccessKeySessionService createSessionService(MCRAccessKeyService service) { + return new MCRAccessKeySessionService(service); + } + private static MCRAccessKeyService createAndConfigureService(MCRAccessKeyRepository accessKeyRepository, MCRAccessKeyValidator validator, MCRAccessKeySecretProcessor secretProcessor) { return new MCRAccessKeyServiceImpl(accessKeyRepository, validator, secretProcessor); diff --git a/mycore-acl/src/main/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeySessionService.java b/mycore-acl/src/main/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeySessionService.java new file mode 100644 index 0000000000..69ad08c643 --- /dev/null +++ b/mycore-acl/src/main/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeySessionService.java @@ -0,0 +1,75 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.mcr.acl.accesskey.service; + +import org.mycore.common.MCRSession; +import org.mycore.common.MCRSessionMgr; +import org.mycore.mcr.acl.accesskey.config.MCRAccessKeyConfig; +import org.mycore.mcr.acl.accesskey.dto.MCRAccessKeyDto; + +/** + * Implements {@link MCRAccessKeyContextService} for {@link MCRSession} context. + */ +public class MCRAccessKeySessionService extends MCRAccessKeyContextService { + + /** + * Prefix for session attribute name for secret. + */ + public static final String ACCESS_KEY_SESSION_ATTRIBUTE_PREFIX = "acckey_"; + + /** + * Constructs new {@link MCRAccessKeySessionService} with given {@link MCRAccessKeyService}. + * + * @param accessKeyService the access key service + */ + public MCRAccessKeySessionService(MCRAccessKeyService accessKeyService) { + super(accessKeyService); + } + + @Override + MCRSession getCurrentContext() { + return MCRSessionMgr.getCurrentSession(); + } + + @Override + protected void setAccessKeyAttribute(MCRSession context, String attributeName, String secret) { + context.put(attributeName, secret); + } + + @Override + protected boolean isAccessKeyAllowedInContext(MCRAccessKeyDto accessKeyDto) { + return MCRAccessKeyConfig.getAllowedSessionPermissionTypes().contains(accessKeyDto.getPermission()); + } + + @Override + protected void removeAccessKeyFromContext(MCRSession context, String attributeName) { + context.deleteObject(attributeName); + } + + @Override + protected String getAccessKeyFromContext(MCRSession context, String attributeName) { + return (String) context.get(attributeName); + } + + @Override + protected String getAccessKeyAttributePrefix() { + return ACCESS_KEY_SESSION_ATTRIBUTE_PREFIX; + } + +} diff --git a/mycore-acl/src/main/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeyUserService.java b/mycore-acl/src/main/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeyUserService.java new file mode 100644 index 0000000000..548671e4be --- /dev/null +++ b/mycore-acl/src/main/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeyUserService.java @@ -0,0 +1,130 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.mcr.acl.accesskey.service; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.mycore.mcr.acl.accesskey.dto.MCRAccessKeyDto; +import org.mycore.user2.MCRUser; +import org.mycore.user2.MCRUserAttribute; +import org.mycore.user2.MCRUserManager; + +/** + * Implements {@link MCRAccessKeyContextService} for {@link MCRUser} context. + */ +public class MCRAccessKeyUserService extends MCRAccessKeyContextService { + + /** + * Prefix for user attribute name for secret. + */ + public static final String ACCESS_KEY_USER_ATTRIBUTE_PREFIX = "acckey_"; + + /** + * Creates new {@link MCRAccessKeyUserService} with {@link MCRAccessKeyService}. + * + * @param accessKeyService the access key service + */ + public MCRAccessKeyUserService(MCRAccessKeyService accessKeyService) { + super(accessKeyService); + } + + @Override + protected MCRUser getCurrentContext() { + return MCRUserManager.getCurrentUser(); + } + + @Override + protected void setAccessKeyAttribute(MCRUser context, String attributeName, String secret) { + context.setUserAttribute(attributeName, secret); + MCRUserManager.updateUser(context); + } + + @Override + protected boolean isAccessKeyAllowedInContext(MCRAccessKeyDto accessKeyDto) { + return true; + } + + @Override + protected void removeAccessKeyFromContext(MCRUser context, String attributeName) { + context.getAttributes().removeIf(ua -> ua.getName().equals(attributeName)); + MCRUserManager.updateUser(context); + } + + @Override + protected String getAccessKeyFromContext(MCRUser context, String attributeName) { + return context.getUserAttribute(attributeName); + } + + @Override + String getAccessKeyAttributePrefix() { + return ACCESS_KEY_USER_ATTRIBUTE_PREFIX; + } + + /** + * Cleans up user attributes related to access keys by checking their validity. + * + * This method iterates over all users who have access key attributes and ensures that only valid access key + * attributes are retained. It removes access key attributes if they are invalid or have been deleted. + */ + public void cleanUpUserAttributes() { + final Set validAttributes = new HashSet<>(); + final Set deadAttributes = new HashSet<>(); + final int limit = 1024; + int offset = 0; + List users; + do { + users = listUsersWithAccessKey(offset, limit); + for (MCRUser user : users) { + cleanUpAttributesForUser(user, validAttributes, deadAttributes); + } + offset += limit; + } while (users.size() == limit); + } + + private static List listUsersWithAccessKey(int offset, int limit) { + return MCRUserManager.listUsers(null, null, null, null, ACCESS_KEY_USER_ATTRIBUTE_PREFIX + "*", null, offset, + limit); + } + + private String extractReference(MCRUserAttribute attribute) { + return attribute.getName().substring(attribute.getName().indexOf('_') + 1); + } + + private void cleanUpAttributesForUser(MCRUser user, Set validAttributes, + Set deadAttributes) { + final List attributes = user.getAttributes().stream() + .filter(attr -> attr.getName().startsWith(ACCESS_KEY_USER_ATTRIBUTE_PREFIX)) + .filter(attr -> !validAttributes.contains(attr)).collect(Collectors.toList()); + for (MCRUserAttribute attribute : attributes) { + final String reference = extractReference(attribute); + if (deadAttributes.contains(attribute)) { + removeAccessKeyFromContext(user, ACCESS_KEY_USER_ATTRIBUTE_PREFIX + reference); + } else if (getService().findAccessKeyByReferenceAndSecret(reference, attribute.getValue()) != null) { + validAttributes.add(attribute); + } else { + removeAccessKeyFromContext(user, ACCESS_KEY_USER_ATTRIBUTE_PREFIX + reference); + deadAttributes.add(attribute); + } + } + } + +} diff --git a/mycore-acl/src/test/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeySessionServiceImplTest.java b/mycore-acl/src/test/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeySessionServiceImplTest.java new file mode 100644 index 0000000000..feee6a4247 --- /dev/null +++ b/mycore-acl/src/test/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeySessionServiceImplTest.java @@ -0,0 +1,198 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.mcr.acl.accesskey.service; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.mycore.access.MCRAccessException; +import org.mycore.access.MCRAccessManager; +import org.mycore.common.MCRPersistenceException; +import org.mycore.common.MCRSessionMgr; +import org.mycore.common.config.MCRConfiguration2; +import org.mycore.datamodel.metadata.MCRDerivate; +import org.mycore.datamodel.metadata.MCRMetadataManager; +import org.mycore.datamodel.metadata.MCRObject; +import org.mycore.datamodel.metadata.MCRObjectID; +import org.mycore.mcr.acl.accesskey.MCRAccessKeyTestCase; +import org.mycore.mcr.acl.accesskey.config.MCRAccessKeyConfig; +import org.mycore.mcr.acl.accesskey.dto.MCRAccessKeyDto; +import org.mycore.mcr.acl.accesskey.exception.MCRAccessKeyException; + +public class MCRAccessKeySessionServiceImplTest extends MCRAccessKeyTestCase { + + private static final String READ_KEY = "blah"; + + private static final String WRITE_KEY = "blub"; + + private static final String DELETE_KEY = "delete"; + + private static final String UNKNOWN_KEY = "unknown"; + + private MCRObject object; + + private MCRDerivate derivate; + + private MCRObjectID unknownObjectId; + + private MCRAccessKeyService accessKeyServiceMock; + + private MCRAccessKeySessionService sessionService; + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + object = createObject(); + MCRMetadataManager.create(object); + derivate = createDerivate(object.getId()); + MCRMetadataManager.create(derivate); + unknownObjectId = MCRObjectID.getInstance("mcr_object_00000002"); + accessKeyServiceMock = Mockito.mock(MCRAccessKeyService.class); + Mockito.when(accessKeyServiceMock.processSecret(object.getId().toString(), READ_KEY)).thenReturn(READ_KEY); + Mockito.when(accessKeyServiceMock.processSecret(derivate.getId().toString(), WRITE_KEY)).thenReturn(WRITE_KEY); + Mockito.when(accessKeyServiceMock.processSecret(object.getId().toString(), UNKNOWN_KEY)).thenReturn(UNKNOWN_KEY); + Mockito.when(accessKeyServiceMock.processSecret(object.getId().toString(), DELETE_KEY)).thenReturn(DELETE_KEY); + final MCRAccessKeyDto readAccessKeyDto = new MCRAccessKeyDto(); + readAccessKeyDto.setReference(object.getId().toString()); + readAccessKeyDto.setSecret(READ_KEY); + readAccessKeyDto.setPermission(MCRAccessManager.PERMISSION_READ); + final MCRAccessKeyDto writeAccessKeyDto = new MCRAccessKeyDto(); + writeAccessKeyDto.setReference(object.getId().toString()); + writeAccessKeyDto.setSecret(WRITE_KEY); + writeAccessKeyDto.setPermission(MCRAccessManager.PERMISSION_WRITE); + final MCRAccessKeyDto deleteAccessKeyDto = new MCRAccessKeyDto(); + deleteAccessKeyDto.setReference(object.getId().toString()); + deleteAccessKeyDto.setSecret(DELETE_KEY); + deleteAccessKeyDto.setPermission(MCRAccessManager.PERMISSION_DELETE); + Mockito.when(accessKeyServiceMock.findAccessKeyByReferenceAndSecret(object.getId().toString(), READ_KEY)) + .thenReturn(readAccessKeyDto); + Mockito.when(accessKeyServiceMock.findAccessKeyByReferenceAndSecret(derivate.getId().toString(), WRITE_KEY)) + .thenReturn(writeAccessKeyDto); + Mockito.when(accessKeyServiceMock.findAccessKeyByReferenceAndSecret(object.getId().toString(), UNKNOWN_KEY)) + .thenReturn(null); + sessionService = new MCRAccessKeySessionService(accessKeyServiceMock); + } + + @Test + public void testSetup() throws MCRPersistenceException, MCRAccessException { + assertTrue(MCRMetadataManager.exists(object.getId())); + assertTrue(MCRMetadataManager.exists(derivate.getId())); + } + + @Test + public void testActivateAccessKeyForReferenceByRawValue_object() { + sessionService.activateAccessKey(object.getId().toString(), READ_KEY); + assertEquals(READ_KEY, MCRSessionMgr.getCurrentSession() + .get(MCRAccessKeySessionService.ACCESS_KEY_SESSION_ATTRIBUTE_PREFIX + object.getId().toString())); + Mockito.verify(accessKeyServiceMock, Mockito.times(1)).findAccessKeyByReferenceAndSecret( + object.getId().toString(), READ_KEY); + } + + @Test + public void testActivateObjectAccessKeyForReferenceByRawValue_derivate_allowed() { + MCRConfiguration2.set(MCRAccessKeyConfig.ALLOWED_SESSION_PERMISSION_TYPES_PROP, "read,writedb"); + sessionService.activateAccessKey(derivate.getId().toString(), WRITE_KEY); + assertEquals(WRITE_KEY, MCRSessionMgr.getCurrentSession() + .get(MCRAccessKeySessionService.ACCESS_KEY_SESSION_ATTRIBUTE_PREFIX + derivate.getId().toString())); + Mockito.verify(accessKeyServiceMock, Mockito.times(1)) + .findAccessKeyByReferenceAndSecret(derivate.getId().toString(), WRITE_KEY); + } + + @Test + public void testActivateObjectAccessKeyForReferenceByRawValue_derivate_notAllowed() { + MCRConfiguration2.set(MCRAccessKeyConfig.ALLOWED_SESSION_PERMISSION_TYPES_PROP, "read"); + assertThrows(MCRAccessKeyException.class, + () -> sessionService.activateAccessKey(derivate.getId().toString(), WRITE_KEY)); + Mockito.verify(accessKeyServiceMock, Mockito.times(1)) + .findAccessKeyByReferenceAndSecret(derivate.getId().toString(), WRITE_KEY); + } + + @Test + public void testActivateObjectAccessKeyForReferenceByRawValue_unknownObject() { + assertThrows(MCRAccessKeyException.class, + () -> sessionService.activateAccessKey(unknownObjectId.toString(), WRITE_KEY)); + Mockito.verify(accessKeyServiceMock, Mockito.times(0)).findAccessKeyByReferenceAndSecret(Mockito.anyString(), + Mockito.anyString()); + } + + @Test + public void testActivateObjectAccessKeyForReferenceByRawValue_unknownAccessKey() { + assertThrows(MCRAccessKeyException.class, + () -> sessionService.activateAccessKey(object.getId().toString(), UNKNOWN_KEY)); + Mockito.verify(accessKeyServiceMock, Mockito.times(1)).findAccessKeyByReferenceAndSecret( + object.getId().toString(), UNKNOWN_KEY); + } + + @Test + public void testActivateObjectAccessKeyForReferenceByRawValue_permissionNotAllowed() { + assertThrows(MCRAccessKeyException.class, + () -> sessionService.activateAccessKey(object.getId().toString(), DELETE_KEY)); + Mockito.verify(accessKeyServiceMock, Mockito.times(1)).findAccessKeyByReferenceAndSecret( + object.getId().toString(), DELETE_KEY); + } + + @Test + public void testActivateObjectAccessKeyForReferenceByRawValue_override() { + MCRSessionMgr.getCurrentSession() + .put(MCRAccessKeySessionService.ACCESS_KEY_SESSION_ATTRIBUTE_PREFIX + object.getId().toString(), WRITE_KEY); + sessionService.activateAccessKey(object.getId().toString(), READ_KEY); + assertEquals(READ_KEY, MCRSessionMgr.getCurrentSession() + .get(MCRAccessKeySessionService.ACCESS_KEY_SESSION_ATTRIBUTE_PREFIX + object.getId().toString())); + Mockito.verify(accessKeyServiceMock, Mockito.times(1)) + .findAccessKeyByReferenceAndSecret(object.getId().toString(), READ_KEY); + } + + @Test + public void testGetActivatedAccessKeyForReference() { + MCRSessionMgr.getCurrentSession() + .put(MCRAccessKeySessionService.ACCESS_KEY_SESSION_ATTRIBUTE_PREFIX + object.getId().toString(), READ_KEY); + final MCRAccessKeyDto accessKey = sessionService.findActiveAccessKey(object.getId().toString()); + assertNotNull(accessKey); + assertEquals(READ_KEY, sessionService.findActiveAccessKey(object.getId().toString()).getSecret()); + } + + @Test + public void testGetActivatedAccessKeyForReference_notExists() { + assertNull(sessionService.findActiveAccessKey(object.getId().toString())); + } + + @Test + public void testRemoveAccessKeySecretFromCurrentSession() { + MCRSessionMgr.getCurrentSession() + .put(MCRAccessKeySessionService.ACCESS_KEY_SESSION_ATTRIBUTE_PREFIX + object.getId().toString(), READ_KEY); + sessionService.deactivateAccessKey(object.getId().toString()); + assertNull(MCRSessionMgr.getCurrentSession() + .get(MCRAccessKeySessionService.ACCESS_KEY_SESSION_ATTRIBUTE_PREFIX + object.getId().toString())); + } + + @After + public void teardown() throws Exception { + MCRMetadataManager.delete(derivate); + MCRMetadataManager.delete(object); + super.tearDown(); + } +} diff --git a/mycore-acl/src/test/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeyUserServiceImplTest.java b/mycore-acl/src/test/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeyUserServiceImplTest.java new file mode 100644 index 0000000000..0698a0ad79 --- /dev/null +++ b/mycore-acl/src/test/java/org/mycore/mcr/acl/accesskey/service/MCRAccessKeyUserServiceImplTest.java @@ -0,0 +1,207 @@ +/* + * This file is part of *** M y C o R e *** + * See https://www.mycore.de/ for details. + * + * MyCoRe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MyCoRe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MyCoRe. If not, see . + */ + +package org.mycore.mcr.acl.accesskey.service; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.mycore.access.MCRAccessManager; +import org.mycore.common.MCRSessionMgr; +import org.mycore.common.MCRSystemUserInformation; +import org.mycore.datamodel.metadata.MCRDerivate; +import org.mycore.datamodel.metadata.MCRMetadataManager; +import org.mycore.datamodel.metadata.MCRObject; +import org.mycore.datamodel.metadata.MCRObjectID; +import org.mycore.mcr.acl.accesskey.MCRAccessKeyTestCase; +import org.mycore.mcr.acl.accesskey.dto.MCRAccessKeyDto; +import org.mycore.mcr.acl.accesskey.exception.MCRAccessKeyException; +import org.mycore.user2.MCRUser; +import org.mycore.user2.MCRUserManager; + +public class MCRAccessKeyUserServiceImplTest extends MCRAccessKeyTestCase { + + private static final String READ_KEY = "blah"; + + private static final String WRITE_KEY = "blub"; + + private static final String DELETE_KEY = "delete"; + + private static final String UNKNOWN_KEY = "unknown"; + + private MCRObject object = null; + + private MCRDerivate derivate = null; + + private MCRObjectID unknownObjectId = null; + + private MCRAccessKeyService accessKeyServiceMock; + + private MCRAccessKeyUserService userService; + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + object = createObject(); + MCRMetadataManager.create(object); + derivate = createDerivate(object.getId()); + MCRMetadataManager.create(derivate); + unknownObjectId = MCRObjectID.getInstance("mcr_object_00000002"); + accessKeyServiceMock = Mockito.mock(MCRAccessKeyService.class); + Mockito.when(accessKeyServiceMock.processSecret(object.getId().toString(), READ_KEY)).thenReturn(READ_KEY); + Mockito.when(accessKeyServiceMock.processSecret(derivate.getId().toString(), WRITE_KEY)).thenReturn(WRITE_KEY); + Mockito.when(accessKeyServiceMock.processSecret(object.getId().toString(), UNKNOWN_KEY)).thenReturn(UNKNOWN_KEY); + Mockito.when(accessKeyServiceMock.processSecret(object.getId().toString(), DELETE_KEY)).thenReturn(DELETE_KEY); + final MCRAccessKeyDto readAccessKeyDto = new MCRAccessKeyDto(); + readAccessKeyDto.setReference(object.getId().toString()); + readAccessKeyDto.setSecret(READ_KEY); + readAccessKeyDto.setPermission(MCRAccessManager.PERMISSION_READ); + final MCRAccessKeyDto writeAccessKeyDto = new MCRAccessKeyDto(); + writeAccessKeyDto.setReference(object.getId().toString()); + writeAccessKeyDto.setSecret(WRITE_KEY); + writeAccessKeyDto.setPermission(MCRAccessManager.PERMISSION_WRITE); + final MCRAccessKeyDto deleteAccessKeyDto = new MCRAccessKeyDto(); + deleteAccessKeyDto.setReference(object.getId().toString()); + deleteAccessKeyDto.setSecret(DELETE_KEY); + deleteAccessKeyDto.setPermission(MCRAccessManager.PERMISSION_DELETE); + Mockito.when(accessKeyServiceMock.findAccessKeyByReferenceAndSecret(object.getId().toString(), READ_KEY)) + .thenReturn(readAccessKeyDto); + Mockito.when(accessKeyServiceMock.findAccessKeyByReferenceAndSecret(derivate.getId().toString(), WRITE_KEY)) + .thenReturn(writeAccessKeyDto); + Mockito.when(accessKeyServiceMock.findAccessKeyByReferenceAndSecret(object.getId().toString(), UNKNOWN_KEY)) + .thenReturn(null); + userService = new MCRAccessKeyUserService(accessKeyServiceMock); + final MCRUser user = new MCRUser("junit"); + MCRUserManager.createUser(user); + MCRSessionMgr.getCurrentSession().setUserInformation(user); + } + + @Test + public void testSetup() { + assertTrue(MCRMetadataManager.exists(object.getId())); + assertTrue(MCRMetadataManager.exists(derivate.getId())); + } + + @Test + public void testActivateAccessKeyForReferenceByRawValue_object() { + userService.activateAccessKey(object.getId().toString(), READ_KEY); + assertEquals(READ_KEY, MCRUserManager.getCurrentUser() + .getUserAttribute(MCRAccessKeyUserService.ACCESS_KEY_USER_ATTRIBUTE_PREFIX + object.getId().toString())); + Mockito.verify(accessKeyServiceMock, Mockito.times(1)).findAccessKeyByReferenceAndSecret( + object.getId().toString(), READ_KEY); + } + + @Test + public void testActivateAccessKeyForReferenceByRawValue_derivate() { + userService.activateAccessKey(derivate.getId().toString(), WRITE_KEY); + assertEquals(WRITE_KEY, MCRUserManager.getCurrentUser() + .getUserAttribute(MCRAccessKeyUserService.ACCESS_KEY_USER_ATTRIBUTE_PREFIX + derivate.getId().toString())); + Mockito.verify(accessKeyServiceMock, Mockito.times(1)).findAccessKeyByReferenceAndSecret( + derivate.getId().toString(), WRITE_KEY); + } + + @Test + public void testActivateAccessKeyForReferenceByRawValue_unknownObject() { + assertThrows(MCRAccessKeyException.class, + () -> userService.activateAccessKey(unknownObjectId.toString(), WRITE_KEY)); + Mockito.verify(accessKeyServiceMock, Mockito.times(0)).findAccessKeyByReferenceAndSecret(Mockito.anyString(), + Mockito.anyString()); + } + + @Test + public void testActivateAccessKeyForReferenceByRawValue_unknownAccessKey() { + assertThrows(MCRAccessKeyException.class, + () -> userService.activateAccessKey(object.getId().toString(), UNKNOWN_KEY)); + Mockito.verify(accessKeyServiceMock, Mockito.times(1)).findAccessKeyByReferenceAndSecret( + object.getId().toString(), UNKNOWN_KEY); + } + + @Test + public void testActivateAccessKeyForReferenceByRawValue_override() { + MCRUserManager.getCurrentUser().setUserAttribute( + MCRAccessKeyUserService.ACCESS_KEY_USER_ATTRIBUTE_PREFIX + object.getId().toString(), WRITE_KEY); + userService.activateAccessKey(object.getId().toString(), READ_KEY); + assertEquals(READ_KEY, MCRUserManager.getCurrentUser() + .getUserAttribute(MCRAccessKeyUserService.ACCESS_KEY_USER_ATTRIBUTE_PREFIX + object.getId().toString())); + Mockito.verify(accessKeyServiceMock, Mockito.times(1)).findAccessKeyByReferenceAndSecret( + object.getId().toString(), READ_KEY); + } + + @Test + public void testGetActivatedAccessKeyForReference() { + MCRUserManager.getCurrentUser().setUserAttribute( + MCRAccessKeyUserService.ACCESS_KEY_USER_ATTRIBUTE_PREFIX + object.getId().toString(), READ_KEY); + final MCRAccessKeyDto accessKey = userService.findActiveAccessKey(object.getId().toString()); + assertNotNull(accessKey); + assertEquals(READ_KEY, userService.findActiveAccessKey(object.getId().toString()).getSecret()); + } + + @Test + public void testGetActivatedAccessKeyForReference_notExists() { + assertNull(userService.findActiveAccessKey(object.getId().toString())); + } + + @Test + public void testRemoveAccessKeySecretFromCurrentSession() { + MCRUserManager.getCurrentUser().setUserAttribute( + MCRAccessKeyUserService.ACCESS_KEY_USER_ATTRIBUTE_PREFIX + object.getId().toString(), READ_KEY); + userService.deactivateAccessKey(object.getId().toString()); + assertNull(MCRUserManager.getCurrentUser() + .getUserAttribute(MCRAccessKeyUserService.ACCESS_KEY_USER_ATTRIBUTE_PREFIX + object.getId().toString())); + } + + @Test + public void testCleanUpUserAttributes() { + MCRUserManager.getCurrentUser().setUserAttribute( + MCRAccessKeyUserService.ACCESS_KEY_USER_ATTRIBUTE_PREFIX + object.getId().toString(), UNKNOWN_KEY); + final MCRUser user = new MCRUser("junit1"); + MCRUserManager.createUser(user); + MCRSessionMgr.getCurrentSession().setUserInformation(MCRSystemUserInformation.getGuestInstance()); + MCRSessionMgr.getCurrentSession().setUserInformation(user); + MCRUserManager.getCurrentUser().setUserAttribute( + MCRAccessKeyUserService.ACCESS_KEY_USER_ATTRIBUTE_PREFIX + object.getId().toString(), UNKNOWN_KEY); + MCRUserManager.getCurrentUser().setUserAttribute( + MCRAccessKeyUserService.ACCESS_KEY_USER_ATTRIBUTE_PREFIX + derivate.getId().toString(), WRITE_KEY); + userService.cleanUpUserAttributes(); + assertNull(MCRUserManager.getCurrentUser() + .getUserAttribute(MCRAccessKeyUserService.ACCESS_KEY_USER_ATTRIBUTE_PREFIX + object.getId().toString())); + assertNotNull(MCRUserManager.getCurrentUser() + .getUserAttribute(MCRAccessKeyUserService.ACCESS_KEY_USER_ATTRIBUTE_PREFIX + derivate.getId().toString())); + MCRSessionMgr.getCurrentSession().setUserInformation(MCRSystemUserInformation.getGuestInstance()); + MCRSessionMgr.getCurrentSession().setUserInformation(MCRUserManager.getUser("junit")); + assertNull(MCRUserManager.getCurrentUser() + .getUserAttribute(MCRAccessKeyUserService.ACCESS_KEY_USER_ATTRIBUTE_PREFIX + object.getId().toString())); + Mockito.verify(accessKeyServiceMock, Mockito.times(2)).findAccessKeyByReferenceAndSecret(Mockito.anyString(), + Mockito.anyString()); + } + + @After + public void teardown() throws Exception { + MCRMetadataManager.delete(derivate); + MCRMetadataManager.delete(object); + super.tearDown(); + } +}