From b68e805a3745f7e5844dadd1092b8f73a8785428 Mon Sep 17 00:00:00 2001 From: Jamal Kaabi-Mofrad Date: Tue, 17 Aug 2021 12:43:33 +0100 Subject: [PATCH] REPO-5659: Added ALFRESCO_SYSTEM_ADMINISTRATORS group authority. (#668) --- .../alfresco/rest/api/tests/GroupsTest.java | 4 +- .../authority/AuthorityServiceImpl.java | 198 ++++++++++-------- .../cmr/security/AuthorityService.java | 67 +++--- .../bootstrap/alfrescoAuthorityStore.xml | 23 +- .../alfrescoAuthorityStoreDefaultMembers.xml | 11 +- .../public-services-security-context.xml | 1 + .../authority/AuthorityServiceTest.java | 50 ++++- 7 files changed, 227 insertions(+), 127 deletions(-) diff --git a/remote-api/src/test/java/org/alfresco/rest/api/tests/GroupsTest.java b/remote-api/src/test/java/org/alfresco/rest/api/tests/GroupsTest.java index e84ee463e1..99c57462dd 100644 --- a/remote-api/src/test/java/org/alfresco/rest/api/tests/GroupsTest.java +++ b/remote-api/src/test/java/org/alfresco/rest/api/tests/GroupsTest.java @@ -836,13 +836,13 @@ public class GroupsTest extends AbstractSingleNetworkSiteTest // Get network admin's groups by explicit ID. { ListResponse groups = groupsProxy.getGroupsByPersonId(networkAdmin, null, "Couldn't get user's groups", 200); - assertEquals(6L, (long) groups.getPaging().getTotalItems()); + assertEquals(7L, (long) groups.getPaging().getTotalItems()); } // test -me- alias (as network admin) { ListResponse groups = groupsProxy.getGroupsByPersonId("-me-", null, "Couldn't get user's groups", 200); - assertEquals(6L, (long) groups.getPaging().getCount()); + assertEquals(7L, (long) groups.getPaging().getCount()); Iterator it = groups.getList().iterator(); assertEquals("GROUP_ALFRESCO_ADMINISTRATORS", it.next().getId()); } diff --git a/repository/src/main/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java b/repository/src/main/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java index 84bafac0a8..0d4ecf8e2a 100644 --- a/repository/src/main/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java +++ b/repository/src/main/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java @@ -1,61 +1,61 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.security.authority; -import java.util.AbstractSet; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; - -import org.alfresco.model.ContentModel; -import org.alfresco.query.PagingRequest; -import org.alfresco.query.PagingResults; -import org.alfresco.repo.policy.ClassPolicyDelegate; -import org.alfresco.repo.policy.PolicyComponent; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authority.AuthorityServicePolicies.OnAuthorityAddedToGroup; -import org.alfresco.repo.security.authority.AuthorityServicePolicies.OnAuthorityRemovedFromGroup; -import org.alfresco.repo.security.authority.AuthorityServicePolicies.OnGroupDeleted; -import org.alfresco.repo.security.permissions.PermissionServiceSPI; -import org.alfresco.repo.security.person.UserNameMatcher; -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.security.AuthenticationService; -import org.alfresco.service.cmr.security.AuthorityService; -import org.alfresco.service.cmr.security.AuthorityType; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.util.Pair; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.extensions.surf.util.ParameterCheck; +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import org.alfresco.model.ContentModel; +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.policy.ClassPolicyDelegate; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authority.AuthorityServicePolicies.OnAuthorityAddedToGroup; +import org.alfresco.repo.security.authority.AuthorityServicePolicies.OnAuthorityRemovedFromGroup; +import org.alfresco.repo.security.authority.AuthorityServicePolicies.OnGroupDeleted; +import org.alfresco.repo.security.permissions.PermissionServiceSPI; +import org.alfresco.repo.security.person.UserNameMatcher; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.util.Pair; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.extensions.surf.util.ParameterCheck; /** * The default implementation of the authority service. @@ -64,6 +64,8 @@ import org.springframework.extensions.surf.util.ParameterCheck; */ public class AuthorityServiceImpl implements AuthorityService, InitializingBean { + public static final String GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS_AUTHORITY = PermissionService.GROUP_PREFIX + "ALFRESCO_SYSTEM_ADMINISTRATORS"; + private static Set DEFAULT_ZONES = new HashSet(); static @@ -83,11 +85,11 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean private Set guestSet = Collections.singleton(PermissionService.GUEST_AUTHORITY); private Set allSet = Collections.singleton(PermissionService.ALL_AUTHORITIES); private Set adminGroups = Collections.emptySet(); - private Set guestGroups = Collections.emptySet(); - - private ClassPolicyDelegate onAuthorityAddedToGroups; - private ClassPolicyDelegate onAuthorityRemovedFromGroup; - private ClassPolicyDelegate onGroupDeletedDelegate; + private Set guestGroups = Collections.emptySet(); + + private ClassPolicyDelegate onAuthorityAddedToGroups; + private ClassPolicyDelegate onAuthorityRemovedFromGroup; + private ClassPolicyDelegate onGroupDeletedDelegate; private PolicyComponent policyComponent; public AuthorityServiceImpl() @@ -133,18 +135,18 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean public void setGuestGroups(Set guestGroups) { this.guestGroups = guestGroups; - } - - public void setPolicyComponent(PolicyComponent policyComponent) - { - this.policyComponent = policyComponent; - } - - public void init() - { - onAuthorityAddedToGroups = policyComponent.registerClassPolicy(AuthorityServicePolicies.OnAuthorityAddedToGroup.class); - onAuthorityRemovedFromGroup = policyComponent.registerClassPolicy(AuthorityServicePolicies.OnAuthorityRemovedFromGroup.class); - onGroupDeletedDelegate = policyComponent.registerClassPolicy(AuthorityServicePolicies.OnGroupDeleted.class); + } + + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + public void init() + { + onAuthorityAddedToGroups = policyComponent.registerClassPolicy(AuthorityServicePolicies.OnAuthorityAddedToGroup.class); + onAuthorityRemovedFromGroup = policyComponent.registerClassPolicy(AuthorityServicePolicies.OnAuthorityRemovedFromGroup.class); + onGroupDeletedDelegate = policyComponent.registerClassPolicy(AuthorityServicePolicies.OnGroupDeleted.class); } @Override @@ -482,12 +484,12 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean */ public void addAuthority(Collection parentNames, String childName) { - authorityDAO.addAuthority(parentNames, childName); - - OnAuthorityAddedToGroup policy = onAuthorityAddedToGroups.get(ContentModel.TYPE_AUTHORITY); - for (String parentGroup : parentNames) - { - policy.onAuthorityAddedToGroup(parentGroup, childName); + authorityDAO.addAuthority(parentNames, childName); + + OnAuthorityAddedToGroup policy = onAuthorityAddedToGroups.get(ContentModel.TYPE_AUTHORITY); + for (String parentGroup : parentNames) + { + policy.onAuthorityAddedToGroup(parentGroup, childName); } } @@ -565,18 +567,18 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean } } authorityDAO.deleteAuthority(name); - permissionServiceSPI.deletePermissions(name); - - if (isGroup(type)) - { - OnGroupDeleted onGroupDelete = onGroupDeletedDelegate.get(ContentModel.TYPE_AUTHORITY); - onGroupDelete.onGroupDeleted(name, cascade); + permissionServiceSPI.deletePermissions(name); + + if (isGroup(type)) + { + OnGroupDeleted onGroupDelete = onGroupDeletedDelegate.get(ContentModel.TYPE_AUTHORITY); + onGroupDelete.onGroupDeleted(name, cascade); } - } - - private boolean isGroup(AuthorityType authorityType) - { - return AuthorityType.GROUP == authorityType || AuthorityType.EVERYONE == authorityType; + } + + private boolean isGroup(AuthorityType authorityType) + { + return AuthorityType.GROUP == authorityType || AuthorityType.EVERYONE == authorityType; } /** @@ -622,9 +624,9 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean @Override public void removeAuthority(String parentName, String childName) { - authorityDAO.removeAuthority(parentName, childName); - - OnAuthorityRemovedFromGroup policy = onAuthorityRemovedFromGroup.get(ContentModel.TYPE_AUTHORITY); + authorityDAO.removeAuthority(parentName, childName); + + OnAuthorityRemovedFromGroup policy = onAuthorityRemovedFromGroup.get(ContentModel.TYPE_AUTHORITY); policy.onAuthorityRemovedFromGroup(parentName, childName); } @@ -770,6 +772,16 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean return authorityDAO.getShortName(name); } + @Override + public boolean hasSysAdminAuthority() + { + final String currentUserName = AuthenticationUtil.getRunAsUser(); + if (currentUserName == null) + { + return false; + } + return getAuthoritiesForUser(currentUserName).contains(GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS_AUTHORITY); + } /** * Lazy load set of authorities. Try not to iterate or ask for the size. Needed for the case where there diff --git a/repository/src/main/java/org/alfresco/service/cmr/security/AuthorityService.java b/repository/src/main/java/org/alfresco/service/cmr/security/AuthorityService.java index 6d6964b62d..5094302539 100644 --- a/repository/src/main/java/org/alfresco/service/cmr/security/AuthorityService.java +++ b/repository/src/main/java/org/alfresco/service/cmr/security/AuthorityService.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.service.cmr.security; import java.util.Collection; @@ -511,4 +511,19 @@ public interface AuthorityService */ @Auditable(parameters = {"type"}) public Set findAuthorities(AuthorityType type, String parentAuthority, boolean immediate, String displayNamePattern, String zoneName); -} + + /** + * Check the current user has system administration authority. + * + * @return true if the currently authenticated user has the system administration authority, otherwise false + * @throws UnsupportedOperationException if the implementing class (i.e. external clients) doesn't provide an implementation for the {@code hasSysAdminAuthority} operation + * + * @since 7.1 + */ + @Auditable + // See PRODMAN-493 -> REPO-5659 + default boolean hasSysAdminAuthority() + { + throw new UnsupportedOperationException("hasSysAdminAuthority"); + } +} diff --git a/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStore.xml b/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStore.xml index 0e327634ac..50f259bf56 100644 --- a/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStore.xml +++ b/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStore.xml @@ -63,6 +63,17 @@ GROUP_ALFRESCO_MODEL_ADMINISTRATORS + + + + + + + GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS + GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS + GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS + + @@ -110,6 +121,11 @@ view:pathref="${system.authorities_container.childname}/cm:GROUP_ALFRESCO_MODEL_ADMINISTRATORS" view:childName="cm:GROUP_ALFRESCO_MODEL_ADMINISTRATORS" /> + + + @@ -146,10 +162,15 @@ view:pathref="${system.authorities_container.childname}/cm:GROUP_ALFRESCO_MODEL_ADMINISTRATORS" view:childName="cm:GROUP_ALFRESCO_MODEL_ADMINISTRATORS" /> + + + - \ No newline at end of file + diff --git a/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStoreDefaultMembers.xml b/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStoreDefaultMembers.xml index 9cdf49e989..b9b2a6cd9e 100644 --- a/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStoreDefaultMembers.xml +++ b/repository/src/main/resources/alfresco/bootstrap/alfrescoAuthorityStoreDefaultMembers.xml @@ -40,6 +40,15 @@ view:childName="cm:${alfresco_user_store.adminusername}" /> + + + + + + + + @@ -66,4 +75,4 @@ - \ No newline at end of file + diff --git a/repository/src/main/resources/alfresco/public-services-security-context.xml b/repository/src/main/resources/alfresco/public-services-security-context.xml index b6e32f9d37..23d4349a9f 100644 --- a/repository/src/main/resources/alfresco/public-services-security-context.xml +++ b/repository/src/main/resources/alfresco/public-services-security-context.xml @@ -792,6 +792,7 @@ org.alfresco.service.cmr.security.AuthorityService.hasAdminAuthority=ACL_ALLOW org.alfresco.service.cmr.security.AuthorityService.hasGuestAuthority=ACL_ALLOW + org.alfresco.service.cmr.security.AuthorityService.hasSysAdminAuthority=ACL_ALLOW org.alfresco.service.cmr.security.AuthorityService.isAdminAuthority=ACL_ALLOW org.alfresco.service.cmr.security.AuthorityService.isGuestAuthority=ACL_ALLOW org.alfresco.service.cmr.security.AuthorityService.countUsers=ACL_ALLOW diff --git a/repository/src/test/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java b/repository/src/test/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java index 568a9dd526..adbaa5f8c1 100644 --- a/repository/src/test/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java +++ b/repository/src/test/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java @@ -25,6 +25,7 @@ */ package org.alfresco.repo.security.authority; +import static org.alfresco.repo.security.authority.AuthorityServiceImpl.GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS_AUTHORITY; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -117,9 +118,10 @@ public class AuthorityServiceTest extends TestCase private static final int DEFAULT_SITE_GRP_CNT = 5; // default number of groups per site private static final int DEFAULT_SITE_ROOT_GRP_CNT = 1; // default number of root groups per site - private static final int DEFAULT_GRP_CNT = 5; // default (non-site) bootstrap groups - + private static final int DEFAULT_GRP_CNT = 6; // default (non-site) bootstrap groups - // eg. GROUP_ALFRESCO_ADMINISTRATORS, GROUP_EMAIL_CONTRIBUTORS, GROUP_SITE_ADMINISTRATORS, - // GROUP_ALFRESCO_SEARCH_ADMINISTRATORS, GROUP_ALFRESCO_MODEL_ADMINISTRATORS + // GROUP_ALFRESCO_SEARCH_ADMINISTRATORS, GROUP_ALFRESCO_MODEL_ADMINISTRATORS, + // GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS private int SITE_CNT = 0; private int GRP_CNT = 0; @@ -467,8 +469,8 @@ public class AuthorityServiceTest extends TestCase assertTrue(authorityService.hasAdminAuthority()); assertTrue(pubAuthorityService.hasAdminAuthority()); Set authorities = authorityService.getAuthorities(); - // 6 => [GROUP_ALFRESCO_ADMINISTRATORS, GROUP_EMAIL_CONTRIBUTORS, GROUP_EVERYONE, GROUP_SITE_ADMINISTRATORS, ROLE_ADMINISTRATOR, GROUP_ALFRESCO_SEARCH_ADMINISTRATORS, GROUP_ALFRESCO_MODEL_ADMINISTRATORS] - assertEquals("Unexpected result: " + authorities, 7 + (SITE_CNT*2), authorityService.getAuthorities().size()); + // 8 => [GROUP_ALFRESCO_ADMINISTRATORS, GROUP_EMAIL_CONTRIBUTORS, GROUP_EVERYONE, GROUP_SITE_ADMINISTRATORS, ROLE_ADMINISTRATOR, GROUP_ALFRESCO_SEARCH_ADMINISTRATORS, GROUP_ALFRESCO_MODEL_ADMINISTRATORS, GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS] + assertEquals("Unexpected result: " + authorities, 8 + (SITE_CNT*2), authorityService.getAuthorities().size()); } public void testNoUser() @@ -1773,6 +1775,46 @@ public class AuthorityServiceTest extends TestCase personService.deletePerson(username); } + public void testAdminHasSysAdminAuthority() + { + authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + assertTrue(authorityService.hasAdminAuthority()); + assertTrue("By default, Admin should be member of Alfresco_System_Administrators group.", + pubAuthorityService.hasSysAdminAuthority()); + } + + public void testSysAdminGroup() + { + personService.getPerson("andy"); + // Make sure Andy is not part of ALFRESCO_ADMINISTRATORS group + String adminGroup = authorityService.getName(AuthorityType.GROUP, "ALFRESCO_ADMINISTRATORS"); + authorityService.removeAuthority(adminGroup, "andy"); + assertFalse(authorityService.isAdminAuthority("andy")); + + // Set the current authentication to Andy, so we can check the runAsUser + authenticationComponent.setCurrentUser("andy"); + assertFalse("Andy hasn't been added to the Alfresco_System_Administrators group yet.", + pubAuthorityService.hasSysAdminAuthority()); + + // Set the current authentication to admin in order to add Andy to the group + authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + pubAuthorityService.addAuthority(GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS_AUTHORITY, "andy"); + + // Set the current authentication to Andy, so we can check the runAsUser + authenticationComponent.setCurrentUser("andy"); + assertTrue("Andy is a member of the Alfresco_System_Administrators group", + pubAuthorityService.hasSysAdminAuthority()); + + // Set the current authentication to admin in order to remove Andy from the group + authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + pubAuthorityService.removeAuthority(GROUP_ALFRESCO_SYSTEM_ADMINISTRATORS_AUTHORITY, "andy"); + + // Set the current authentication to Andy, so we can check the runAsUser + authenticationComponent.setCurrentUser("andy"); + assertFalse("Andy has been removed from the Alfresco_System_Administrators group.", + pubAuthorityService.hasSysAdminAuthority()); + } + private T createClassPolicy(Class policyInterface, QName policyQName, QName triggerOnClass) { T policy = mock(policyInterface);