Merge pull request #2661 from Alfresco/feature/PRODENG-276_implement_service_accounts

PRODENG-276: Implement service accounts
This commit is contained in:
Tom Page
2024-06-03 11:24:12 +01:00
committed by GitHub
16 changed files with 1388 additions and 489 deletions

View File

@@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Data model classes * Alfresco Data model classes
* %% * %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited * Copyright (C) 2005 - 2024 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
@@ -78,6 +78,28 @@ public interface PermissionService
*/ */
public static final String GUEST_AUTHORITY = "ROLE_GUEST"; public static final String GUEST_AUTHORITY = "ROLE_GUEST";
/**
* The dynamic authority for the Admin service account.
*/
String ADMIN_SVC_AUTHORITY = "ROLE_ADMIN_SERVICE_ACCOUNT";
/**
* The dynamic authority for the Collaborator service account.
*/
String COLLABORATOR_SVC_AUTHORITY = "ROLE_COLLABORATOR_SERVICE_ACCOUNT";
/**
* The dynamic authority for the Editor service account.
*/
String EDITOR_SVC_AUTHORITY = "ROLE_EDITOR_SERVICE_ACCOUNT";
/**
* A convenient set of service account authorities to simplify checks
* for whether a given authority is a service account authority or not.
*/
Set<String> SVC_AUTHORITIES_SET = Set.of(ADMIN_SVC_AUTHORITY, COLLABORATOR_SVC_AUTHORITY,
EDITOR_SVC_AUTHORITY);
/** /**
* The permission for all - not defined in the model. Repsected in the code. * The permission for all - not defined in the model. Repsected in the code.
*/ */

View File

@@ -0,0 +1,83 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2024 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.security.permissions.dynamic;
import java.util.Optional;
import java.util.Set;
import org.alfresco.repo.serviceaccount.ServiceAccountRegistry;
import org.alfresco.repo.security.permissions.DynamicAuthority;
import org.alfresco.repo.security.permissions.PermissionReference;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.PropertyCheck;
import org.springframework.beans.factory.InitializingBean;
/**
* This class represents a dynamic authority for service accounts in the system.
*
* @author Jamal Kaabi-Mofrad
*/
public class ServiceAccountAuthority implements DynamicAuthority, InitializingBean
{
private String authority;
private ServiceAccountRegistry serviceAccountRegistry;
public void setAuthority(String authority)
{
this.authority = authority;
}
public void setServiceAccountRegistry(ServiceAccountRegistry serviceAccountRegistry)
{
this.serviceAccountRegistry = serviceAccountRegistry;
}
@Override
public void afterPropertiesSet() throws Exception
{
PropertyCheck.mandatory(this, "authority", authority);
PropertyCheck.mandatory(this, "serviceAccountRegistry", serviceAccountRegistry);
}
@Override
public boolean hasAuthority(NodeRef nodeRef, String userName)
{
Optional<String> role = serviceAccountRegistry.getServiceAccountRole(userName);
return role.isPresent() && role.get().equals(this.getAuthority());
}
@Override
public String getAuthority()
{
return this.authority;
}
@Override
public Set<PermissionReference> requiredFor()
{
return null;
}
}

View File

@@ -0,0 +1,61 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2024 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.serviceaccount;
import java.util.Optional;
import java.util.Set;
/**
* A service account registry that allows service accounts to be registered
* with their corresponding roles.
*
* @author Jamal Kaabi-Mofrad
*/
public interface ServiceAccountRegistry
{
/**
* Registers a service account with its corresponding role.
*
* @param serviceAccountName The name of the service account to be registered.
* @param serviceAccountRole The role of the service account to be registered.
*/
void register(String serviceAccountName, String serviceAccountRole);
/**
* Retrieves the role of a specific service account.
*
* @param serviceAccountName The name of the service account.
* @return An Optional containing the role of the service account if it exists, otherwise an empty Optional.
*/
Optional<String> getServiceAccountRole(String serviceAccountName);
/**
* Retrieves the names of all service accounts.
*
* @return A set of service account names. If no service accounts are present, an empty set is returned.
*/
Set<String> getServiceAccountNames();
}

View File

@@ -0,0 +1,157 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2024 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.serviceaccount;
import java.util.Locale;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
/**
* Processes the <b>alfresco-global</b> properties file and applies a naming convention to distinguish the service
* account's name and role.
* <p>
* The naming convention adheres to the following format:
* <p>
* <pre>
* {@code
* serviceaccount.role.<service-account-name>=<service-account-role>
* }
* </pre>
* <p>
* Please note, any property with an invalid role value will be disregarded and the corresponding service account
* will not be registered.
* <p>
* For instance, to register a service account named 'custom-app-sa' with the 'Editor' role (which allows it to
* update node properties), the following should be defined in the <b>alfresco-global</b> properties file:
* <ul>
* <li>serviceaccount.role.custom-app-sa=EDITOR_SERVICE_ACCOUNT</li>
* <li>or</li>
* <li>serviceaccount.role.custom-app-sa=ROLE_EDITOR_SERVICE_ACCOUNT</li>
* </ul>
*
* @author Jamal Kaabi-Mofrad
*/
public class ServiceAccountRegistryImpl implements ServiceAccountRegistry, InitializingBean
{
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceAccountRegistryImpl.class);
public static final String KEY_PREFIX = "serviceaccount.role.";
private Properties globalProperties;
private final ConcurrentMap<String, String> saRoleMap = new ConcurrentHashMap<>();
public void setGlobalProperties(Properties globalProperties)
{
this.globalProperties = globalProperties;
}
@Override
public void register(String serviceAccountName, String serviceAccountRole)
{
saRoleMap.put(serviceAccountName, serviceAccountRole);
LOGGER.info("Service account '{}' is registered with the role '{}'.", serviceAccountName, serviceAccountRole);
}
@Override
public Optional<String> getServiceAccountRole(String serviceAccountName)
{
return Optional.ofNullable(saRoleMap.get(serviceAccountName));
}
@Override
public Set<String> getServiceAccountNames()
{
return Set.copyOf(saRoleMap.keySet());
}
private void init()
{
globalProperties.stringPropertyNames()
.stream()
.filter(key -> key.startsWith(KEY_PREFIX))
.forEach(key -> {
String name = key.substring(KEY_PREFIX.length());
if (isNotValidProperty(key, name, "name"))
{
return;
}
String role = globalProperties.getProperty(key);
if (isNotValidProperty(key, role, "role"))
{
return;
}
// Ensure the role is in uppercase and has the prefix
role = role.toUpperCase(Locale.ENGLISH);
role = getRoleWithPrefix(role);
if (!PermissionService.SVC_AUTHORITIES_SET.contains(role))
{
LOGGER.warn("Invalid service account role '{}'. The role is not recognized.", role);
return;
}
// Register the service account name with the corresponding role.
register(name, role);
});
}
@Override
public void afterPropertiesSet() throws Exception
{
PropertyCheck.mandatory(this, "globalProperties", globalProperties);
init();
}
private String getRoleWithPrefix(String saRole)
{
if (!saRole.startsWith(PermissionService.ROLE_PREFIX))
{
saRole = PermissionService.ROLE_PREFIX + saRole;
}
return saRole;
}
private boolean isNotValidProperty(String key, String value, String valueType)
{
if (StringUtils.isBlank(value))
{
LOGGER.warn("Invalid service account {} defined in the property '{}'. The {} cannot be an empty string.",
valueType, key, valueType);
return true;
}
return false;
}
}

View File

@@ -143,4 +143,8 @@
</property> </property>
</bean> </bean>
<bean id="serviceAccountRegistry" class="org.alfresco.repo.serviceaccount.ServiceAccountRegistryImpl">
<property name="globalProperties" ref="global-properties"/>
</bean>
</beans> </beans>

View File

@@ -13,462 +13,487 @@
<permissions> <permissions>
<!-- Namespaces used in type references --> <!-- Namespaces used in type references -->
<namespaces>
<namespace uri="http://www.alfresco.org/model/system/1.0" prefix="sys"/>
<namespace uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
</namespaces>
<!-- --> <namespaces>
<!-- Permission sets link permissions and groups of permissions to types and aspects --> <namespace uri="http://www.alfresco.org/model/system/1.0" prefix="sys"/>
<!-- defined in the model. Permissions defined against a type apply to all objects --> <namespace uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
<!-- that inherit from that type. Permissions defined against aspects apply to all --> </namespaces>
<!-- objects or only objects that have the aspect applied. For example, the permission -->
<!-- to lock an object could apply to any object but the permission to unlock an -->
<!-- object would only apply to objects that have the lockable aspect. -->
<!-- -->
<!-- =============================================== -->
<!-- Base permissions available on all types of node -->
<!-- =============================================== -->
<permissionSet type="sys:base" expose="all" >
<!-- ================= -->
<!-- Permission groups -->
<!-- ================= -->
<!-- -->
<!-- Permission groups are convenient groups of permissions. They may be used in -->
<!-- their own right or as the effective set of permissions. If an authority has -->
<!-- all the permissions that make up a permission group they also have that -->
<!-- permission group even though it has not been explicitly granted. -->
<!-- -->
<!-- =========== --> <!-- -->
<!-- Full access --> <!-- Permission sets link permissions and groups of permissions to types and aspects -->
<!-- =========== --> <!-- defined in the model. Permissions defined against a type apply to all objects -->
<!-- that inherit from that type. Permissions defined against aspects apply to all -->
<!-- --> <!-- objects or only objects that have the aspect applied. For example, the permission -->
<!-- By default this is exposed for all objects unless inherited objects choose to --> <!-- to lock an object could apply to any object but the permission to unlock an -->
<!-- expose only selected objects at the object level. --> <!-- object would only apply to objects that have the lockable aspect. -->
<!-- --> <!-- -->
<permissionGroup name="FullControl" expose="true" allowFullControl="true" /> <!-- =============================================== -->
<!-- Base permissions available on all types of node -->
<!-- ============================================= --> <!-- =============================================== -->
<!-- Convenient groupings of low level permissions -->
<!-- ============================================= --> <permissionSet type="sys:base" expose="all">
<permissionGroup name="Read" expose="true" allowFullControl="false"> <!-- ================= -->
<includePermissionGroup type="sys:base" permissionGroup="ReadProperties"/> <!-- Permission groups -->
<includePermissionGroup type="sys:base" permissionGroup="ReadChildren"/> <!-- ================= -->
<includePermissionGroup type="sys:base" permissionGroup="ReadContent"/>
</permissionGroup> <!-- -->
<!-- Permission groups are convenient groups of permissions. They may be used in -->
<permissionGroup name="Write" expose="true" allowFullControl="false"> <!-- their own right or as the effective set of permissions. If an authority has -->
<includePermissionGroup type="sys:base" permissionGroup="WriteProperties"/> <!-- all the permissions that make up a permission group they also have that -->
<includePermissionGroup type="sys:base" permissionGroup="WriteContent"/> <!-- permission group even though it has not been explicitly granted. -->
</permissionGroup> <!-- -->
<permissionGroup name="Delete" expose="true" allowFullControl="false"> <!-- =========== -->
<includePermissionGroup type="sys:base" permissionGroup="DeleteNode"/> <!-- Full access -->
<includePermissionGroup type="sys:base" permissionGroup="DeleteChildren"/> <!-- =========== -->
</permissionGroup>
<!-- -->
<permissionGroup name="AddChildren" expose="true" allowFullControl="false"> <!-- By default this is exposed for all objects unless inherited objects choose to -->
<includePermissionGroup type="sys:base" permissionGroup="CreateChildren"/> <!-- expose only selected objects at the object level. -->
<includePermissionGroup type="sys:base" permissionGroup="LinkChildren"/> <!-- -->
</permissionGroup>
<permissionGroup name="FullControl" expose="true" allowFullControl="true"/>
<permissionGroup name="Execute" allowFullControl="false" expose="false">
<includePermissionGroup type="sys:base" permissionGroup="ExecuteContent"/> <permissionGroup name="AdminServiceAccount" expose="false" allowFullControl="false">
</permissionGroup> <includePermissionGroup type="sys:base" permissionGroup="Read"/>
<includePermissionGroup type="sys:base" permissionGroup="Write"/>
<!-- Groups for low level permissions --> <includePermissionGroup type="sys:base" permissionGroup="AddChildren"/>
<includePermissionGroup type="sys:base" permissionGroup="Delete"/>
<permissionGroup name="ReadProperties" expose="true" allowFullControl="false" /> <includePermissionGroup type="sys:base" permissionGroup="ReadAssociations"/>
<permissionGroup name="ReadChildren" expose="true" allowFullControl="false" /> <includePermissionGroup type="sys:base" permissionGroup="CreateAssociations"/>
<permissionGroup name="WriteProperties" expose="true" allowFullControl="false" /> <includePermissionGroup type="sys:base" permissionGroup="DeleteAssociations"/>
<permissionGroup name="ReadContent" expose="false" allowFullControl="false" /> <includePermissionGroup type="sys:base" permissionGroup="ReadPermissions"/>
<permissionGroup name="WriteContent" expose="false" allowFullControl="false" /> </permissionGroup>
<permissionGroup name="ExecuteContent" expose="false" allowFullControl="false" /> <permissionGroup name="CollaboratorServiceAccount" expose="false" allowFullControl="false">
<permissionGroup name="DeleteNode" expose="true" allowFullControl="false" /> <includePermissionGroup type="sys:base" permissionGroup="Read"/>
<permissionGroup name="DeleteChildren" expose="true" allowFullControl="false" /> <includePermissionGroup type="sys:base" permissionGroup="Write"/>
<permissionGroup name="CreateChildren" expose="true" allowFullControl="false" /> <includePermissionGroup type="sys:base" permissionGroup="AddChildren"/>
<permissionGroup name="LinkChildren" expose="true" allowFullControl="false" /> </permissionGroup>
<permissionGroup name="DeleteAssociations" expose="true" allowFullControl="false" /> <permissionGroup name="EditorServiceAccount" expose="false" allowFullControl="false">
<permissionGroup name="ReadAssociations" expose="true" allowFullControl="false" /> <includePermissionGroup type="sys:base" permissionGroup="Read"/>
<permissionGroup name="CreateAssociations" expose="true" allowFullControl="false" /> <includePermissionGroup type="sys:base" permissionGroup="Write"/>
<permissionGroup name="ReadPermissions" expose="true" allowFullControl="false" /> </permissionGroup>
<permissionGroup name="ChangePermissions" expose="true" allowFullControl="false" />
<!-- ============================================= -->
<!-- =========== --> <!-- Convenient groupings of low level permissions -->
<!-- Permissions --> <!-- ============================================= -->
<!-- =========== -->
<permissionGroup name="Read" expose="true" allowFullControl="false">
<!-- The permission to read properties on a node --> <includePermissionGroup type="sys:base" permissionGroup="ReadProperties"/>
<!-- --> <includePermissionGroup type="sys:base" permissionGroup="ReadChildren"/>
<!-- The properties of a node may ony be read if there is read access to the parent --> <includePermissionGroup type="sys:base" permissionGroup="ReadContent"/>
<!-- node. ReadChildren access to the parent node is recursive for all nodes from --> </permissionGroup>
<!-- which the node inherits permissions. Access is required down the permission -->
<!-- tree at all points. --> <permissionGroup name="Write" expose="true" allowFullControl="false">
<!-- --> <includePermissionGroup type="sys:base" permissionGroup="WriteProperties"/>
<includePermissionGroup type="sys:base" permissionGroup="WriteContent"/>
<permission name="_ReadProperties" expose="false" > </permissionGroup>
<grantedToGroup permissionGroup="ReadProperties" />
<!-- Commented out parent permission check ... <permissionGroup name="Delete" expose="true" allowFullControl="false">
<requiredPermission on="parent" name="_ReadChildren" implies="false"/> <includePermissionGroup type="sys:base" permissionGroup="DeleteNode"/>
--> <includePermissionGroup type="sys:base" permissionGroup="DeleteChildren"/>
</permission> </permissionGroup>
<!-- The permission to read the children of a node --> <permissionGroup name="AddChildren" expose="true" allowFullControl="false">
<!-- --> <includePermissionGroup type="sys:base" permissionGroup="CreateChildren"/>
<!-- This permission is recursive. It requires the same permission is granted to --> <includePermissionGroup type="sys:base" permissionGroup="LinkChildren"/>
<!-- all of the parent nodes from which this node inherits permissions --> </permissionGroup>
<!-- -->
<permissionGroup name="Execute" allowFullControl="false" expose="false">
<permission name="_ReadChildren" expose="false" > <includePermissionGroup type="sys:base" permissionGroup="ExecuteContent"/>
<grantedToGroup permissionGroup="ReadChildren" /> </permissionGroup>
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/> <!-- Groups for low level permissions -->
-->
</permission> <permissionGroup name="ReadProperties" expose="true" allowFullControl="false"/>
<permissionGroup name="ReadChildren" expose="true" allowFullControl="false"/>
<!-- The permission to write to the properties of a node --> <permissionGroup name="WriteProperties" expose="true" allowFullControl="false"/>
<!-- --> <permissionGroup name="ReadContent" expose="false" allowFullControl="false"/>
<!-- This permission includes adding aspects to a node as they are stored as --> <permissionGroup name="WriteContent" expose="false" allowFullControl="false"/>
<!-- a property. --> <permissionGroup name="ExecuteContent" expose="false" allowFullControl="false"/>
<!-- --> <permissionGroup name="DeleteNode" expose="true" allowFullControl="false"/>
<permissionGroup name="DeleteChildren" expose="true" allowFullControl="false"/>
<permission name="_WriteProperties" expose="false" > <permissionGroup name="CreateChildren" expose="true" allowFullControl="false"/>
<grantedToGroup permissionGroup="WriteProperties" /> <permissionGroup name="LinkChildren" expose="true" allowFullControl="false"/>
<!-- Commented out parent permission check ... <permissionGroup name="DeleteAssociations" expose="true" allowFullControl="false"/>
<requiredPermission on="parent" name="_ReadChildren" implies="false"/> <permissionGroup name="ReadAssociations" expose="true" allowFullControl="false"/>
--> <permissionGroup name="CreateAssociations" expose="true" allowFullControl="false"/>
</permission> <permissionGroup name="ReadPermissions" expose="true" allowFullControl="false"/>
<permissionGroup name="ChangePermissions" expose="true" allowFullControl="false"/>
<!-- The permission to delete a node -->
<!-- --> <!-- =========== -->
<!-- A node can only be deleted if there is delete permission on the node, if the --> <!-- Permissions -->
<!-- node is accessible via its parent, and if the node can be deleted from its --> <!-- =========== -->
<!-- parent. Currently, there is no check that all the children can be deleted. -->
<!-- This check can be added but requires more work so the UI is not checking this --> <!-- The permission to read properties on a node -->
<!-- permission just to show the delete icon. --> <!-- -->
<!-- --> <!-- The properties of a node may ony be read if there is read access to the parent -->
<!-- node. ReadChildren access to the parent node is recursive for all nodes from -->
<!-- The permission to read content. --> <!-- which the node inherits permissions. Access is required down the permission -->
<!-- tree at all points. -->
<permission name="_ReadContent" expose="false"> <!-- -->
<grantedToGroup permissionGroup="ReadContent"/>
<!-- Commented out parent permission check ... <permission name="_ReadProperties" expose="false">
<requiredPermission on="parent" name="_ReadChildren" implies="false"/> <grantedToGroup permissionGroup="ReadProperties"/>
--> <!-- Commented out parent permission check ...
</permission> <requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<!-- The permission to read the children of a node -->
<!-- -->
<!-- This permission is recursive. It requires the same permission is granted to -->
<!-- all of the parent nodes from which this node inherits permissions -->
<!-- -->
<permission name="_ReadChildren" expose="false">
<grantedToGroup permissionGroup="ReadChildren"/>
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<!-- The permission to write to the properties of a node -->
<!-- -->
<!-- This permission includes adding aspects to a node as they are stored as -->
<!-- a property. -->
<!-- -->
<permission name="_WriteProperties" expose="false">
<grantedToGroup permissionGroup="WriteProperties"/>
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<!-- The permission to delete a node -->
<!-- -->
<!-- A node can only be deleted if there is delete permission on the node, if the -->
<!-- node is accessible via its parent, and if the node can be deleted from its -->
<!-- parent. Currently, there is no check that all the children can be deleted. -->
<!-- This check can be added but requires more work so the UI is not checking this -->
<!-- permission just to show the delete icon. -->
<!-- -->
<!-- The permission to read content. -->
<permission name="_ReadContent" expose="false">
<grantedToGroup permissionGroup="ReadContent"/>
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<!-- The permission to write content. -->
<permission name="_WriteContent" expose="false">
<grantedToGroup permissionGroup="WriteContent"/>
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<!-- Execute permission on content. -->
<permission name="_ExecuteContent" expose="false">
<grantedToGroup permissionGroup="ExecuteContent"/>
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<permission name="_DeleteNode" expose="false">
<grantedToGroup permissionGroup="DeleteNode"/>
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
<requiredPermission on="parent" name="_DeleteChildren" implies="false"/>
<requiredPermission on="node" name="_DeleteChildren" implies="false"/>
-->
<!-- Remove the recursive check for now for performance -->
<!-- TODO: have one permission to check for delete on an item and one to check -->
<!-- child permissions when delete is called on the node service -->
<!-- <requiredPermission on="children" name="_DeleteNode" implies="false"/> -->
</permission>
<!-- The permission to delete children of a node -->
<!-- -->
<!-- At the moment this includes both unlink and delete -->
<!-- -->
<permission name="_DeleteChildren" expose="false">
<grantedToGroup permissionGroup="DeleteChildren"/>
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<!-- The permission to create new nodes -->
<permission name="_CreateChildren" expose="false">
<grantedToGroup permissionGroup="CreateChildren"/>
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false" />
-->
</permission>
<!-- The permission to link nodes -->
<permission name="_LinkChildren" expose="false">
<grantedToGroup permissionGroup="LinkChildren"/>
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<!-- The permission to delete associations between nodes (not children) -->
<permission name="_DeleteAssociations" expose="false">
<grantedToGroup permissionGroup="DeleteAssociations"/>
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<!-- The permission to read associations -->
<permission name="_ReadAssociations" expose="false">
<grantedToGroup permissionGroup="ReadAssociations"/>
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false" />
-->
</permission>
<!-- The permission to create associations -->
<permission name="_CreateAssociations" expose="false">
<grantedToGroup permissionGroup="CreateAssociations"/>
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false" />
-->
</permission>
<!-- ==================================================== -->
<!-- Permissions related to the management of permissions -->
<!-- ==================================================== -->
<!-- The permission to read the permissions on a node -->
<permission name="_ReadPermissions" expose="false">
<grantedToGroup permissionGroup="ReadPermissions"/>
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<!-- The permission to the change the permissions associated with a node -->
<permission name="_ChangePermissions" expose="false">
<grantedToGroup permissionGroup="ChangePermissions"/>
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
</permissionSet>
<!-- ================================================ -->
<!-- Permissions available to all content and folders -->
<!-- ================================================ -->
<permissionSet type="cm:cmobject" expose="selected">
<!-- Kept for backward compatibility - the administrator permission has -->
<!-- been removed to avoid confusion -->
<permissionGroup name="Administrator" allowFullControl="true" expose="false"/>
<!-- A coordinator can do anything to the object or its children unless the -->
<!-- permissions are set not to inherit or permission is denied. -->
<permissionGroup name="Coordinator" allowFullControl="true" expose="true"/>
<!-- A collaborator can do anything that an editor and a contributor can do -->
<permissionGroup name="Collaborator" allowFullControl="false" expose="true">
<includePermissionGroup permissionGroup="Editor" type="cm:cmobject"/>
<includePermissionGroup permissionGroup="Contributor" type="cm:cmobject"/>
</permissionGroup>
<!-- A contributor can create content and then they have full permission on what -->
<!-- they have created - via the permissions assigned to the owner. -->
<permissionGroup name="Contributor" allowFullControl="false" expose="true">
<!-- Contributor is a consumer who can add content, and then can modify via the -->
<!-- owner permissions. -->
<includePermissionGroup permissionGroup="Consumer" type="cm:cmobject"/>
<includePermissionGroup permissionGroup="AddChildren" type="sys:base"/>
<includePermissionGroup permissionGroup="ReadPermissions" type="sys:base"/>
</permissionGroup>
<!-- An editor can read and write to the object; they can not create -->
<!-- new nodes. They can check out content into a space to which they have -->
<!-- create permission. -->
<permissionGroup name="Editor" expose="true" allowFullControl="false">
<includePermissionGroup type="cm:cmobject" permissionGroup="Consumer"/>
<includePermissionGroup type="sys:base" permissionGroup="Write"/>
<includePermissionGroup type="cm:lockable" permissionGroup="CheckOut"/>
<includePermissionGroup type="sys:base" permissionGroup="ReadPermissions"/>
</permissionGroup>
<!-- The Consumer permission allows read to everything by default. -->
<permissionGroup name="Consumer" allowFullControl="false" expose="true">
<includePermissionGroup permissionGroup="Read" type="sys:base"/>
</permissionGroup>
<!-- records permission -->
<!-- Should be tied to the aspect -->
<!-- ownership should be removed when using this permission -->
<permissionGroup name="RecordAdministrator" allowFullControl="false" expose="false">
<includePermissionGroup type="sys:base" permissionGroup="ReadProperties"/>
<includePermissionGroup type="sys:base" permissionGroup="ReadChildren"/>
<includePermissionGroup type="sys:base" permissionGroup="WriteProperties"/>
<includePermissionGroup type="sys:base" permissionGroup="ReadContent"/>
<includePermissionGroup type="sys:base" permissionGroup="DeleteChildren"/>
<includePermissionGroup type="sys:base" permissionGroup="CreateChildren"/>
<includePermissionGroup type="sys:base" permissionGroup="LinkChildren"/>
<includePermissionGroup type="sys:base" permissionGroup="DeleteAssociations"/>
<includePermissionGroup type="sys:base" permissionGroup="CreateAssociations"/>
</permissionGroup>
</permissionSet>
<!-- =============================== -->
<!-- Permissions specific to content -->
<!-- =============================== -->
<permissionSet type="cm:content" expose="selected">
<!-- Content specific roles. -->
<permissionGroup name="Coordinator" extends="true" expose="true"/>
<permissionGroup name="Collaborator" extends="true" expose="true"/>
<permissionGroup name="Contributor" extends="true" expose="true"/>
<permissionGroup name="Editor" extends="true" expose="true"/>
<permissionGroup name="Consumer" extends="true" expose="true"/>
<permissionGroup name="RecordAdministrator" extends="true" expose="false"/>
</permissionSet>
<!-- The permission to write content. -->
<permission name="_WriteContent" expose="false">
<grantedToGroup permissionGroup="WriteContent" />
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<!-- Execute permission on content. -->
<permission name="_ExecuteContent" expose="false">
<grantedToGroup permissionGroup="ExecuteContent" />
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<permission name="_DeleteNode" expose="false" >
<grantedToGroup permissionGroup="DeleteNode" />
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
<requiredPermission on="parent" name="_DeleteChildren" implies="false"/>
<requiredPermission on="node" name="_DeleteChildren" implies="false"/>
-->
<!-- Remove the recursive check for now for performance -->
<!-- TODO: have one permission to check for delete on an item and one to check -->
<!-- child permissions when delete is called on the node service -->
<!-- <requiredPermission on="children" name="_DeleteNode" implies="false"/> -->
</permission>
<!-- The permission to delete children of a node -->
<!-- -->
<!-- At the moment this includes both unlink and delete -->
<!-- -->
<permission name="_DeleteChildren" expose="false" >
<grantedToGroup permissionGroup="DeleteChildren" />
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<!-- The permission to create new nodes -->
<permission name="_CreateChildren" expose="false" >
<grantedToGroup permissionGroup="CreateChildren" />
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false" />
-->
</permission>
<!-- The permission to link nodes -->
<permission name="_LinkChildren" expose="false" >
<grantedToGroup permissionGroup="LinkChildren" />
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<!-- The permission to delete associations between nodes (not children) -->
<permission name="_DeleteAssociations" expose="false" >
<grantedToGroup permissionGroup="DeleteAssociations" />
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<!-- The permission to read associations -->
<permission name="_ReadAssociations" expose="false" >
<grantedToGroup permissionGroup="ReadAssociations" />
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false" />
-->
</permission>
<!-- The permission to create associations -->
<permission name="_CreateAssociations" expose="false" >
<grantedToGroup permissionGroup="CreateAssociations" />
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false" />
-->
</permission>
<!-- ==================================================== -->
<!-- Permissions related to the management of permissions -->
<!-- ==================================================== -->
<!-- The permission to read the permissions on a node -->
<permission name="_ReadPermissions" expose="false" >
<grantedToGroup permissionGroup="ReadPermissions" />
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
<!-- The permission to the change the permissions associated with a node -->
<permission name="_ChangePermissions" expose="false" >
<grantedToGroup permissionGroup="ChangePermissions" />
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" implies="false"/>
-->
</permission>
</permissionSet>
<!-- ================================================ -->
<!-- Permissions available to all content and folders -->
<!-- ================================================ -->
<permissionSet type="cm:cmobject" expose="selected">
<!-- Kept for backward compatibility - the administrator permission has -->
<!-- been removed to avoid confusion -->
<permissionGroup name="Administrator" allowFullControl="true" expose="false" />
<!-- A coordinator can do anything to the object or its children unless the -->
<!-- permissions are set not to inherit or permission is denied. -->
<permissionGroup name="Coordinator" allowFullControl="true" expose="true" />
<!-- A collaborator can do anything that an editor and a contributor can do -->
<permissionGroup name="Collaborator" allowFullControl="false" expose="true">
<includePermissionGroup permissionGroup="Editor" type="cm:cmobject" />
<includePermissionGroup permissionGroup="Contributor" type="cm:cmobject" />
</permissionGroup>
<!-- A contributor can create content and then they have full permission on what -->
<!-- they have created - via the permissions assigned to the owner. -->
<permissionGroup name="Contributor" allowFullControl="false" expose="true" >
<!-- Contributor is a consumer who can add content, and then can modify via the -->
<!-- owner permissions. -->
<includePermissionGroup permissionGroup="Consumer" type="cm:cmobject"/>
<includePermissionGroup permissionGroup="AddChildren" type="sys:base"/>
<includePermissionGroup permissionGroup="ReadPermissions" type="sys:base" />
</permissionGroup>
<!-- An editor can read and write to the object; they can not create -->
<!-- new nodes. They can check out content into a space to which they have -->
<!-- create permission. -->
<permissionGroup name="Editor" expose="true" allowFullControl="false" >
<includePermissionGroup type="cm:cmobject" permissionGroup="Consumer"/>
<includePermissionGroup type="sys:base" permissionGroup="Write"/>
<includePermissionGroup type="cm:lockable" permissionGroup="CheckOut"/>
<includePermissionGroup type="sys:base" permissionGroup="ReadPermissions"/>
</permissionGroup>
<!-- The Consumer permission allows read to everything by default. -->
<permissionGroup name="Consumer" allowFullControl="false" expose="true" >
<includePermissionGroup permissionGroup="Read" type="sys:base" />
</permissionGroup>
<!-- records permission -->
<!-- Should be tied to the aspect -->
<!-- ownership should be removed when using this permission -->
<permissionGroup name="RecordAdministrator" allowFullControl="false" expose="false">
<includePermissionGroup type="sys:base" permissionGroup="ReadProperties"/>
<includePermissionGroup type="sys:base" permissionGroup="ReadChildren"/>
<includePermissionGroup type="sys:base" permissionGroup="WriteProperties"/>
<includePermissionGroup type="sys:base" permissionGroup="ReadContent"/>
<includePermissionGroup type="sys:base" permissionGroup="DeleteChildren"/>
<includePermissionGroup type="sys:base" permissionGroup="CreateChildren"/>
<includePermissionGroup type="sys:base" permissionGroup="LinkChildren"/>
<includePermissionGroup type="sys:base" permissionGroup="DeleteAssociations"/>
<includePermissionGroup type="sys:base" permissionGroup="CreateAssociations"/>
</permissionGroup>
</permissionSet>
<!-- =============================== -->
<!-- Permissions specific to content -->
<!-- =============================== -->
<permissionSet type="cm:content" expose="selected">
<!-- Content specific roles. -->
<permissionGroup name="Coordinator" extends="true" expose="true"/>
<permissionGroup name="Collaborator" extends="true" expose="true"/>
<permissionGroup name="Contributor" extends="true" expose="true"/>
<permissionGroup name="Editor" extends="true" expose="true"/>
<permissionGroup name="Consumer" extends="true" expose="true"/>
<permissionGroup name="RecordAdministrator" extends="true" expose="false"/>
</permissionSet>
<permissionSet type="cm:folder" expose="selected"> <permissionSet type="cm:folder" expose="selected">
<!-- Content folder specific roles. --> <!-- Content folder specific roles. -->
<permissionGroup name="Coordinator" extends="true" expose="true"/>
<permissionGroup name="Collaborator" extends="true" expose="true"/>
<permissionGroup name="Contributor" extends="true" expose="true"/>
<permissionGroup name="Editor" extends="true" expose="true"/>
<permissionGroup name="Consumer" extends="true" expose="true"/>
<permissionGroup name="RecordAdministrator" extends="true" expose="false"/>
</permissionSet>
<!-- ============================================== -->
<!-- Permissions associated with the Ownable aspect -->
<!-- ============================================== -->
<permissionSet type="cm:ownable" expose="selected">
<!-- Permission control to allow ownership of the node to be taken from others -->
<permissionGroup name="TakeOwnership" requiresType="false" expose="false">
<includePermissionGroup permissionGroup="SetOwner" type="cm:ownable" />
</permissionGroup>
<permissionGroup name="SetOwner" requiresType="false" expose="false"/>
<!-- The low level permission to control setting the owner of a node -->
<permission name="_SetOwner" expose="false" requiresType="false">
<grantedToGroup permissionGroup="SetOwner" />
<!-- require to be able to reach the node and set properties in the node -->
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" />
-->
<requiredPermission on="node" type="sys:base" name="_WriteProperties" />
</permission>
</permissionSet>
<!-- =================================================== -->
<!-- Permission related to check in and cancel check out. -->
<!-- =================================================== -->
<permissionSet type="cm:workingcopy" expose="selected"> <permissionGroup name="Coordinator" extends="true" expose="true"/>
<permissionGroup name="Collaborator" extends="true" expose="true"/>
<permissionGroup name="Contributor" extends="true" expose="true"/>
<permissionGroup name="Editor" extends="true" expose="true"/>
<permissionGroup name="Consumer" extends="true" expose="true"/>
<permissionGroup name="RecordAdministrator" extends="true" expose="false"/>
<!-- Cancel Check Out permission - only exposed for the workingcopy aspect is present --> </permissionSet>
<permissionGroup name="CancelCheckOut" requiresType="true" expose="false">
<includePermissionGroup permissionGroup="Unlock" type="cm:lockable" /> <!-- ============================================== -->
</permissionGroup> <!-- Permissions associated with the Ownable aspect -->
<!-- ============================================== -->
<permissionSet type="cm:ownable" expose="selected">
<!-- Permission control to allow ownership of the node to be taken from others -->
<permissionGroup name="TakeOwnership" requiresType="false" expose="false">
<includePermissionGroup permissionGroup="SetOwner" type="cm:ownable"/>
</permissionGroup>
<permissionGroup name="SetOwner" requiresType="false" expose="false"/>
<!-- The low level permission to control setting the owner of a node -->
<permission name="_SetOwner" expose="false" requiresType="false">
<grantedToGroup permissionGroup="SetOwner"/>
<!-- require to be able to reach the node and set properties in the node -->
<!-- Commented out parent permission check ...
<requiredPermission on="parent" name="_ReadChildren" />
-->
<requiredPermission on="node" type="sys:base" name="_WriteProperties"/>
</permission>
</permissionSet>
<!-- =================================================== -->
<!-- Permission related to check in and cancel check out. -->
<!-- =================================================== -->
<permissionSet type="cm:workingcopy" expose="selected">
<!-- Cancel Check Out permission - only exposed for the workingcopy aspect is present -->
<permissionGroup name="CancelCheckOut" requiresType="true" expose="false">
<includePermissionGroup permissionGroup="Unlock" type="cm:lockable"/>
</permissionGroup>
<!-- Check In permission - only exposed when the workingcopy aspect is present -->
<permissionGroup name="CheckIn" requiresType="true" expose="false">
<includePermissionGroup permissionGroup="Unlock" type="cm:lockable"/>
</permissionGroup>
</permissionSet>
<!-- =================================================== -->
<!-- Permission related to lock, check out and check in. -->
<!-- =================================================== -->
<permissionSet type="cm:lockable" expose="selected">
<!-- At the moment these permissions are hidden so they do not appear in the list -->
<!-- of permissions. -->
<!-- Check Out permission - exposed for all object types -->
<permissionGroup name="CheckOut" requiresType="false" expose="false">
<includePermissionGroup permissionGroup="Lock" type="cm:lockable"/>
</permissionGroup>
<permissionGroup name="Lock" requiresType="false" expose="false"/>
<permissionGroup name="Unlock" requiresType="true" expose="false"/>
<!-- Low level lock permission -->
<permission name="_Lock" requiresType="false" expose="false">
<grantedToGroup permissionGroup="Lock"/>
<requiredPermission on="node" type="sys:base" name="Write"/>
</permission>
<!-- Low level unlock permission -->
<permission name="_Unlock" requiresType="true" expose="false">
<grantedToGroup permissionGroup="Unlock"/>
</permission>
</permissionSet>
<!-- ================== -->
<!-- Global permissions -->
<!-- ================== -->
<!-- -->
<!-- Global permissions apply regardless of any particular node context. -->
<!-- They can not be denied by the permissions set on any node. -->
<!-- -->
<!-- Admin can do anything to any node -->
<globalPermission permission="FullControl" authority="ROLE_ADMINISTRATOR"/>
<!-- For now, owners can always see, find and manipulate their stuff -->
<globalPermission permission="FullControl" authority="ROLE_OWNER"/>
<!-- Unlock is granted to the lock owner -->
<globalPermission permission="Unlock" authority="ROLE_LOCK_OWNER"/>
<!-- Check in is granted to the lock owner -->
<globalPermission permission="CheckIn" authority="ROLE_LOCK_OWNER"/>
<!-- Cancel check out is granted to the lock owner -->
<globalPermission permission="CancelCheckOut" authority="ROLE_LOCK_OWNER"/>
<!-- Service Account roles -->
<globalPermission permission="AdminServiceAccount" authority="ROLE_ADMIN_SERVICE_ACCOUNT"/>
<globalPermission permission="CollaboratorServiceAccount" authority="ROLE_COLLABORATOR_SERVICE_ACCOUNT"/>
<globalPermission permission="EditorServiceAccount" authority="ROLE_EDITOR_SERVICE_ACCOUNT"/>
<!-- Check In permission - only exposed when the workingcopy aspect is present -->
<permissionGroup name="CheckIn" requiresType="true" expose="false">
<includePermissionGroup permissionGroup="Unlock" type="cm:lockable" />
</permissionGroup>
</permissionSet>
<!-- =================================================== -->
<!-- Permission related to lock, check out and check in. -->
<!-- =================================================== -->
<permissionSet type="cm:lockable" expose="selected">
<!-- At the moment these permissions are hidden so they do not appear in the list -->
<!-- of permissions. -->
<!-- Check Out permission - exposed for all object types -->
<permissionGroup name="CheckOut" requiresType="false" expose="false">
<includePermissionGroup permissionGroup="Lock" type="cm:lockable" />
</permissionGroup>
<permissionGroup name="Lock" requiresType="false" expose="false"/>
<permissionGroup name="Unlock" requiresType="true" expose="false"/>
<!-- Low level lock permission -->
<permission name="_Lock" requiresType="false" expose="false">
<grantedToGroup permissionGroup="Lock" />
<requiredPermission on="node" type="sys:base" name="Write"/>
</permission>
<!-- Low level unlock permission -->
<permission name="_Unlock" requiresType="true" expose="false">
<grantedToGroup permissionGroup="Unlock" />
</permission>
</permissionSet>
<!-- ================== -->
<!-- Global permissions -->
<!-- ================== -->
<!-- -->
<!-- Global permissions apply regardless of any particular node context. -->
<!-- They can not be denied by the permissions set on any node. -->
<!-- -->
<!-- Admin can do anything to any node -->
<globalPermission permission="FullControl" authority="ROLE_ADMINISTRATOR"/>
<!-- For now, owners can always see, find and manipulate their stuff -->
<globalPermission permission="FullControl" authority="ROLE_OWNER"/>
<!-- Unlock is granted to the lock owner -->
<globalPermission permission="Unlock" authority="ROLE_LOCK_OWNER"/>
<!-- Check in is granted to the lock owner -->
<globalPermission permission="CheckIn" authority="ROLE_LOCK_OWNER"/>
<!-- Cancel check out is granted to the lock owner -->
<globalPermission permission="CancelCheckOut" authority="ROLE_LOCK_OWNER"/>
</permissions> </permissions>

View File

@@ -102,6 +102,9 @@
<list> <list>
<ref bean="ownerDynamicAuthority" /> <ref bean="ownerDynamicAuthority" />
<ref bean="lockOwnerDynamicAuthority" /> <ref bean="lockOwnerDynamicAuthority" />
<ref bean="adminServiceAccountAuthority"/>
<ref bean="collaboratorServiceAccountAuthority"/>
<ref bean="editorServiceAccountAuthority"/>
</list> </list>
</property> </property>
<property name="fixedAclUpdater"> <property name="fixedAclUpdater">
@@ -147,6 +150,23 @@
</property> </property>
</bean> </bean>
<!-- Service account authority beans -->
<bean id="baseServiceAccountAuthority" abstract="true">
<property name="serviceAccountRegistry" ref="serviceAccountRegistry"/>
</bean>
<bean id="adminServiceAccountAuthority" class="org.alfresco.repo.security.permissions.dynamic.ServiceAccountAuthority"
parent="baseServiceAccountAuthority">
<property name="authority" value="#{T(org.alfresco.service.cmr.security.PermissionService).ADMIN_SVC_AUTHORITY}"/>
</bean>
<bean id="collaboratorServiceAccountAuthority" class="org.alfresco.repo.security.permissions.dynamic.ServiceAccountAuthority"
parent="baseServiceAccountAuthority">
<property name="authority" value="#{T(org.alfresco.service.cmr.security.PermissionService).COLLABORATOR_SVC_AUTHORITY}"/>
</bean>
<bean id="editorServiceAccountAuthority" class="org.alfresco.repo.security.permissions.dynamic.ServiceAccountAuthority"
parent="baseServiceAccountAuthority">
<property name="authority" value="#{T(org.alfresco.service.cmr.security.PermissionService).EDITOR_SVC_AUTHORITY}"/>
</bean>
<!-- =========================== --> <!-- =========================== -->
<!-- Permissions Model Bootstrap --> <!-- Permissions Model Bootstrap -->
<!-- =========================== --> <!-- =========================== -->

View File

@@ -261,7 +261,8 @@ import org.junit.runners.Suite;
org.alfresco.repo.event2.RepoEvent2UnitSuite.class, org.alfresco.repo.event2.RepoEvent2UnitSuite.class,
org.alfresco.util.schemacomp.SchemaDifferenceHelperUnitTest.class, org.alfresco.util.schemacomp.SchemaDifferenceHelperUnitTest.class,
org.alfresco.repo.tagging.TaggingServiceImplUnitTest.class org.alfresco.repo.tagging.TaggingServiceImplUnitTest.class,
org.alfresco.repo.serviceaccount.ServiceAccountRegistryImplTest.class
}) })
public class AllUnitTestsSuite public class AllUnitTestsSuite
{ {

View File

@@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2023 Alfresco Software Limited * Copyright (C) 2005 - 2024 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
@@ -85,6 +85,7 @@ import org.junit.runners.Suite;
org.alfresco.repo.model.ModelTestSuite.class, org.alfresco.repo.model.ModelTestSuite.class,
org.alfresco.repo.tenant.MultiTNodeServiceInterceptorTest.class, org.alfresco.repo.tenant.MultiTNodeServiceInterceptorTest.class,
org.alfresco.repo.transfer.RepoTransferReceiverImplTest.class, org.alfresco.repo.transfer.RepoTransferReceiverImplTest.class,
org.alfresco.repo.security.permissions.dynamic.ServiceAccountRoleTest.class
}) })
public class AppContext05TestSuite public class AppContext05TestSuite
{ {

View File

@@ -45,6 +45,7 @@ import org.alfresco.repo.security.authority.AuthorityServiceTest;
import org.alfresco.repo.security.authority.DuplicateAuthorityTest; import org.alfresco.repo.security.authority.DuplicateAuthorityTest;
import org.alfresco.repo.security.authority.ExtendedPermissionServiceTest; import org.alfresco.repo.security.authority.ExtendedPermissionServiceTest;
import org.alfresco.repo.security.permissions.dynamic.LockOwnerDynamicAuthorityTest; import org.alfresco.repo.security.permissions.dynamic.LockOwnerDynamicAuthorityTest;
import org.alfresco.repo.security.permissions.dynamic.ServiceAccountRoleTest;
import org.alfresco.repo.security.permissions.impl.AclDaoComponentTest; import org.alfresco.repo.security.permissions.impl.AclDaoComponentTest;
import org.alfresco.repo.security.permissions.impl.PermissionServiceTest; import org.alfresco.repo.security.permissions.impl.PermissionServiceTest;
import org.alfresco.repo.security.permissions.impl.ReadPermissionTest; import org.alfresco.repo.security.permissions.impl.ReadPermissionTest;
@@ -108,6 +109,7 @@ public class SecurityTestSuite extends TestSuite
suite.addTest(new JUnit4TestAdapter(LocalAuthenticationServiceTest.class)); suite.addTest(new JUnit4TestAdapter(LocalAuthenticationServiceTest.class));
suite.addTest(new JUnit4TestAdapter(ResetPasswordServiceImplTest.class)); suite.addTest(new JUnit4TestAdapter(ResetPasswordServiceImplTest.class));
suite.addTest(new JUnit4TestAdapter(ServiceAccountRoleTest.class));
return suite; return suite;
} }
} }

View File

@@ -0,0 +1,355 @@
/*
* #%L
* Alfresco Data model classes
* %%
* Copyright (C) 2005 - 2024 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.security.permissions.dynamic;
import static java.lang.System.currentTimeMillis;
import static org.junit.Assert.assertEquals;
import java.io.Serializable;
import java.util.Map;
import java.util.Properties;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.cmr.site.SiteVisibility;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.test.junitrules.AlfrescoPerson;
import org.alfresco.util.test.junitrules.ApplicationContextInit;
import org.alfresco.util.test.junitrules.TemporaryNodes;
import org.alfresco.util.test.junitrules.TemporarySites;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.springframework.context.ApplicationContext;
/**
* This test class demonstrates the permissions of the service account authorities.
* The service account authorities are used to grant permissions to service accounts.
* <p>
* The service account authorities are defined in the <i>alfresco-global.properties</i> file.
* Using the following naming convention:
* <pre>
* {@code
* serviceaccount.role.<service-account-name>=<service-account-role>
* }
* </pre>
* The service account roles that currently supported are:
* <ul>
* <li>{@link PermissionService#EDITOR_SVC_AUTHORITY}</li>
* <li>{@link PermissionService#COLLABORATOR_SVC_AUTHORITY}</li>
* <li>{@link PermissionService#ADMIN_SVC_AUTHORITY}</li>
* </ul>
* The test class relies on the following service accounts defined in the <i>alfresco-global.properties</i> file:
* <ul>
* <li>serviceaccount.role.test-editor-sa=ROLE_EDITOR_SERVICE_ACCOUNT</li>
* <li>serviceaccount.role.test-collaborator-sa=ROLE_COLLABORATOR_SERVICE_ACCOUNT</li>
* <li>serviceaccount.role.test-admin-sa=ROLE_ADMIN_SERVICE_ACCOUNT</li>
* </ul>
* <p>
* <b>Note:</b> There is no need to use public services (i.e., beans that start with a capital letter, such as NodeService)
* to validate roles permissions. This is because the security enforcement of public services (i.e., ACL checks) ultimately relies on
* the {@code permissionService.hasPermission()} method. Therefore, we can directly use the {@code permissionService.hasPermission()} method.
*
* @author Jamal Kaabi-Mofrad
*/
// Ignore the PMD warning about having too many test methods in this class; it makes the tests easier to read and maintain.
@SuppressWarnings("PMD.TooManyMethods")
public class ServiceAccountRoleTest
{
// Rule to initialise the default Alfresco spring configuration
private static final ApplicationContextInit APP_CONTEXT = new ApplicationContextInit();
// A rule to manage a test site
private static final TemporarySites TEST_SITES = new TemporarySites(APP_CONTEXT);
// A rule to manage test nodes reused across all the test methods
private static final TemporaryNodes TEST_NODES = new TemporaryNodes(APP_CONTEXT);
// Rules to create the users for the tests
private static final AlfrescoPerson NORMAL_USER = getAlfrescoPerson("john.doe" + currentTimeMillis());
private static final AlfrescoPerson EDITOR_SA = getAlfrescoPerson("test-editor-sa");
private static final AlfrescoPerson COLLABORATOR_SA = getAlfrescoPerson("test-collaborator-sa");
private static final AlfrescoPerson ADMIN_SA = getAlfrescoPerson("test-admin-sa");
private static final String TEST_TEXT_FILE_NAME = "testTextFile.txt";
// Tie them together in a static Rule Chain
@ClassRule
public static final RuleChain STATIC_RULE_CHAIN = RuleChain.outerRule(APP_CONTEXT)
.around(TEST_SITES)
.around(TEST_NODES)
.around(NORMAL_USER)
.around(EDITOR_SA)
.around(COLLABORATOR_SA)
.around(ADMIN_SA);
private static NodeService nodeService;
private static SiteService siteService;
private static PermissionService permissionService;
private static Properties globalProperties;
private static NodeRef testTextFile;
@BeforeClass
public static void initStaticData() throws Exception
{
ApplicationContext context = APP_CONTEXT.getApplicationContext();
nodeService = context.getBean("NodeService", NodeService.class);
siteService = context.getBean("SiteService", SiteService.class);
permissionService = context.getBean("permissionService", PermissionService.class);
globalProperties = context.getBean("global-properties", Properties.class);
// Check that the service account roles are defined in the global properties before starting the tests.
serviceAccountsShouldExistInGlobalProperties();
// Create a test site
SiteInfo testSite = createTestSite();
// Create a test text file in the test site
createTestFile(testSite);
// Clear the current security context to avoid any issues with the test setup
AuthenticationUtil.clearCurrentSecurityContext();
}
private static AlfrescoPerson getAlfrescoPerson(String username)
{
return new AlfrescoPerson(APP_CONTEXT, username);
}
private static SiteInfo createTestSite()
{
// Create a private test site to make sure no other non-members or
// non-site-admins can access the test site.
return TEST_SITES.createSite("sitePreset", "saTestSite" + currentTimeMillis(), "SA Test Site",
"sa test site desc", SiteVisibility.PRIVATE,
AuthenticationUtil.getAdminUserName());
}
private static void createTestFile(SiteInfo testSite)
{
// Create a test text file in the test site as the admin user
AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser();
final NodeRef docLib = siteService.getContainer(testSite.getShortName(), SiteService.DOCUMENT_LIBRARY);
final NodeRef testFolder = TEST_NODES.createFolder(docLib, "testFolder", AuthenticationUtil.getAdminUserName());
testTextFile = TEST_NODES.createNodeWithTextContent(testFolder, TEST_TEXT_FILE_NAME, ContentModel.TYPE_CONTENT,
AuthenticationUtil.getAdminUserName(),
"The quick brown fox jumps over the lazy dog.");
Map<QName, Serializable> props = Map.of(ContentModel.PROP_NAME, TEST_TEXT_FILE_NAME,
ContentModel.PROP_DESCRIPTION, "Desc added by Admin.");
nodeService.setProperties(testTextFile, props);
}
private static void serviceAccountsShouldExistInGlobalProperties()
{
assertServiceAccountIsDefined(PermissionService.EDITOR_SVC_AUTHORITY, EDITOR_SA.getUsername());
assertServiceAccountIsDefined(PermissionService.COLLABORATOR_SVC_AUTHORITY, COLLABORATOR_SA.getUsername());
assertServiceAccountIsDefined(PermissionService.ADMIN_SVC_AUTHORITY, ADMIN_SA.getUsername());
}
private static void assertServiceAccountIsDefined(String expectedRole, String username)
{
assertEquals(expectedRole, globalProperties.getProperty("serviceaccount.role." + username));
}
@After
public void tearDown() throws Exception
{
AuthenticationUtil.clearCurrentSecurityContext();
}
@Test
public void normalUserReadAccessShouldBeDenied()
{
assertAccessDenied(NORMAL_USER, PermissionService.READ);
}
@Test
public void editorSaReadAccessShouldBeAllowed()
{
assertAccessAllowed(EDITOR_SA, PermissionService.READ);
}
@Test
public void collaboratorSaReadAccessShouldBeAllowed()
{
assertAccessAllowed(COLLABORATOR_SA, PermissionService.READ);
}
@Test
public void adminSaReadAccessShouldBeAllowed()
{
assertAccessAllowed(ADMIN_SA, PermissionService.READ);
}
@Test
public void normalUserWriteAccessShouldBeDenied()
{
assertAccessDenied(NORMAL_USER, PermissionService.WRITE);
}
@Test
public void editorSaWriteAccessShouldBeAllowed()
{
assertAccessAllowed(EDITOR_SA, PermissionService.WRITE);
}
@Test
public void collaboratorSaWriteAccessShouldBeAllowed()
{
assertAccessAllowed(COLLABORATOR_SA, PermissionService.WRITE);
}
@Test
public void adminSaWriteAccessShouldBeAllowed()
{
assertAccessAllowed(ADMIN_SA, PermissionService.WRITE);
}
@Test
public void normalUserAddChildrenAccessShouldBeDenied()
{
assertAccessDenied(NORMAL_USER, PermissionService.ADD_CHILDREN);
}
@Test
public void editorSaAddChildrenAccessShouldBeDenied()
{
assertAccessDenied(EDITOR_SA, PermissionService.ADD_CHILDREN);
}
@Test
public void collaboratorSaAddChildrenAccessShouldBeAllowed()
{
assertAccessAllowed(COLLABORATOR_SA, PermissionService.ADD_CHILDREN);
}
@Test
public void adminSaAddChildrenAccessShouldBeAllowed()
{
assertAccessAllowed(ADMIN_SA, PermissionService.ADD_CHILDREN);
}
@Test
public void normalUserDeleteAccessShouldBeDenied()
{
assertAccessDenied(NORMAL_USER, PermissionService.DELETE);
}
@Test
public void editorSaDeleteAccessShouldBeDenied()
{
assertAccessDenied(EDITOR_SA, PermissionService.DELETE);
}
@Test
public void collaboratorSaDeleteAccessShouldBeDenied()
{
assertAccessDenied(COLLABORATOR_SA, PermissionService.DELETE);
}
@Test
public void adminSaDeleteAccessShouldBeAllowed()
{
assertAccessAllowed(ADMIN_SA, PermissionService.DELETE);
}
@Test
public void normalUserAssociationAccessShouldBeDenied()
{
assertAccessDenied(NORMAL_USER, PermissionService.READ_ASSOCIATIONS);
assertAccessDenied(NORMAL_USER, PermissionService.CREATE_ASSOCIATIONS);
assertAccessDenied(NORMAL_USER, PermissionService.DELETE_ASSOCIATIONS);
}
@Test
public void editorSaAssociationAccessShouldBeDenied()
{
assertAccessDenied(EDITOR_SA, PermissionService.READ_ASSOCIATIONS);
assertAccessDenied(EDITOR_SA, PermissionService.CREATE_ASSOCIATIONS);
assertAccessDenied(EDITOR_SA, PermissionService.DELETE_ASSOCIATIONS);
}
@Test
public void collaboratorSaAssociationAccessShouldBeDenied()
{
assertAccessDenied(COLLABORATOR_SA, PermissionService.READ_ASSOCIATIONS);
assertAccessDenied(COLLABORATOR_SA, PermissionService.CREATE_ASSOCIATIONS);
assertAccessDenied(COLLABORATOR_SA, PermissionService.DELETE_ASSOCIATIONS);
}
@Test
public void adminSaAssociationAccessShouldBeAllowed()
{
assertAccessAllowed(ADMIN_SA, PermissionService.READ_ASSOCIATIONS);
assertAccessAllowed(ADMIN_SA, PermissionService.CREATE_ASSOCIATIONS);
assertAccessAllowed(ADMIN_SA, PermissionService.DELETE_ASSOCIATIONS);
}
@Test
public void normalUserReadPermissionsAccessShouldBeDenied()
{
assertAccessDenied(NORMAL_USER, PermissionService.READ_PERMISSIONS);
}
@Test
public void editorSaReadPermissionsAccessShouldBeDenied()
{
assertAccessDenied(EDITOR_SA, PermissionService.READ_PERMISSIONS);
}
@Test
public void collaboratorSaReadPermissionsAccessShouldBeDenied()
{
assertAccessDenied(COLLABORATOR_SA, PermissionService.READ_PERMISSIONS);
}
@Test
public void adminSaReadPermissionsAccessShouldBeAllowed()
{
assertAccessAllowed(ADMIN_SA, PermissionService.READ_PERMISSIONS);
}
private static void assertAccessDenied(AlfrescoPerson user, String permission)
{
AuthenticationUtil.setFullyAuthenticatedUser(user.getUsername());
assertEquals(AccessStatus.DENIED, permissionService.hasPermission(testTextFile, permission));
}
private static void assertAccessAllowed(AlfrescoPerson user, String permission)
{
AuthenticationUtil.setFullyAuthenticatedUser(user.getUsername());
assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(testTextFile, permission));
}
}

View File

@@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2023 Alfresco Software Limited * Copyright (C) 2005 - 2024 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
@@ -66,6 +66,8 @@ import org.springframework.context.ApplicationContext;
public abstract class AbstractPermissionTest extends TestCase public abstract class AbstractPermissionTest extends TestCase
{ {
public static final int NUMBER_OF_GLOBAL_PERMISSIONS = 8;
protected static final String USER2_LEMUR = "lemur"; protected static final String USER2_LEMUR = "lemur";
protected static final String USER1_ANDY = "andy"; protected static final String USER1_ANDY = "andy";

View File

@@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited * Copyright (C) 2005 - 2024 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
@@ -73,6 +73,10 @@ import org.junit.runners.MethodSorters;
@FixMethodOrder(MethodSorters.NAME_ASCENDING) @FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class PermissionServiceTest extends AbstractPermissionTest public class PermissionServiceTest extends AbstractPermissionTest
{ {
// The number of permissions in the system.
// See permissionDefinitions.xml and PermissionService interface
private static final int NUM_OF_PERMISSIONS = 39;
private SimplePermissionEntry denyAndyAll; private SimplePermissionEntry denyAndyAll;
private SimplePermissionEntry allowAndyAll; private SimplePermissionEntry allowAndyAll;
@@ -1764,7 +1768,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
public void testGetSettablePermissionsForType() public void testGetSettablePermissionsForType()
{ {
Set<String> answer = permissionService.getSettablePermissions(QName.createQName("sys", "base", namespacePrefixResolver)); Set<String> answer = permissionService.getSettablePermissions(QName.createQName("sys", "base", namespacePrefixResolver));
assertEquals(36, answer.size()); assertEquals(NUM_OF_PERMISSIONS, answer.size());
answer = permissionService.getSettablePermissions(QName.createQName("cm", "ownable", namespacePrefixResolver)); answer = permissionService.getSettablePermissions(QName.createQName("cm", "ownable", namespacePrefixResolver));
assertEquals(0, answer.size()); assertEquals(0, answer.size());
@@ -1784,15 +1788,15 @@ public class PermissionServiceTest extends AbstractPermissionTest
QName ownable = QName.createQName("cm", "ownable", namespacePrefixResolver); QName ownable = QName.createQName("cm", "ownable", namespacePrefixResolver);
Set<String> answer = permissionService.getSettablePermissions(rootNodeRef); Set<String> answer = permissionService.getSettablePermissions(rootNodeRef);
assertEquals(36, answer.size()); assertEquals(NUM_OF_PERMISSIONS, answer.size());
nodeService.addAspect(rootNodeRef, ownable, null); nodeService.addAspect(rootNodeRef, ownable, null);
answer = permissionService.getSettablePermissions(rootNodeRef); answer = permissionService.getSettablePermissions(rootNodeRef);
assertEquals(36, answer.size()); assertEquals(NUM_OF_PERMISSIONS, answer.size());
nodeService.removeAspect(rootNodeRef, ownable); nodeService.removeAspect(rootNodeRef, ownable);
answer = permissionService.getSettablePermissions(rootNodeRef); answer = permissionService.getSettablePermissions(rootNodeRef);
assertEquals(36, answer.size()); assertEquals(NUM_OF_PERMISSIONS, answer.size());
} }
@@ -1816,7 +1820,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
{ {
runAs("andy"); runAs("andy");
assertEquals(36, permissionService.getPermissions(rootNodeRef).size()); assertEquals(NUM_OF_PERMISSIONS, permissionService.getPermissions(rootNodeRef).size());
assertEquals(0, countGranted(permissionService.getPermissions(rootNodeRef))); assertEquals(0, countGranted(permissionService.getPermissions(rootNodeRef)));
assertEquals(0, permissionService.getAllSetPermissions(rootNodeRef).size()); assertEquals(0, permissionService.getAllSetPermissions(rootNodeRef).size());
@@ -1828,7 +1832,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
assertEquals(1, permissionService.getAllSetPermissions(rootNodeRef).size()); assertEquals(1, permissionService.getAllSetPermissions(rootNodeRef).size());
runAs("andy"); runAs("andy");
assertEquals(36, permissionService.getPermissions(rootNodeRef).size()); assertEquals(NUM_OF_PERMISSIONS, permissionService.getPermissions(rootNodeRef).size());
assertEquals(2, countGranted(permissionService.getPermissions(rootNodeRef))); assertEquals(2, countGranted(permissionService.getPermissions(rootNodeRef)));
assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED); assertTrue(permissionService.hasPermission(rootNodeRef, getPermission(PermissionService.READ_PROPERTIES)) == AccessStatus.ALLOWED);
@@ -1933,7 +1937,7 @@ public class PermissionServiceTest extends AbstractPermissionTest
permissionService.setPermission(allowAndyRead); permissionService.setPermission(allowAndyRead);
runAs("andy"); runAs("andy");
assertEquals(36, permissionService.getPermissions(rootNodeRef).size()); assertEquals(NUM_OF_PERMISSIONS, permissionService.getPermissions(rootNodeRef).size());
assertEquals(7, countGranted(permissionService.getPermissions(rootNodeRef))); assertEquals(7, countGranted(permissionService.getPermissions(rootNodeRef)));
assertEquals(1, permissionService.getAllSetPermissions(rootNodeRef).size()); assertEquals(1, permissionService.getAllSetPermissions(rootNodeRef).size());

View File

@@ -1,28 +1,28 @@
/* /*
* #%L * #%L
* Alfresco Repository * Alfresco Repository
* %% * %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited * Copyright (C) 2005 - 2024 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* *
* Alfresco is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.security.permissions.impl.model; package org.alfresco.repo.security.permissions.impl.model;
import java.util.Collections; import java.util.Collections;
@@ -94,7 +94,7 @@ public class PermissionModelTest extends AbstractPermissionTest
Set<PermissionReference> grantees = permissionModelDAO.getGranteePermissions(SimplePermissionReference.getPermissionReference(QName.createQName("cm", "cmobject", Set<PermissionReference> grantees = permissionModelDAO.getGranteePermissions(SimplePermissionReference.getPermissionReference(QName.createQName("cm", "cmobject",
namespacePrefixResolver), "Coordinator")); namespacePrefixResolver), "Coordinator"));
assertEquals(69, grantees.size()); assertEquals(72, grantees.size());
} }
public void testIncludePermissionGroups6() public void testIncludePermissionGroups6()
@@ -109,17 +109,17 @@ public class PermissionModelTest extends AbstractPermissionTest
{ {
Set<PermissionReference> granters = permissionModelDAO.getGrantingPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base", Set<PermissionReference> granters = permissionModelDAO.getGrantingPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base",
namespacePrefixResolver), "ReadProperties")); namespacePrefixResolver), "ReadProperties"));
assertEquals(14, granters.size()); assertEquals(17, granters.size());
granters = permissionModelDAO.getGrantingPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base", namespacePrefixResolver), granters = permissionModelDAO.getGrantingPermissions(SimplePermissionReference.getPermissionReference(QName.createQName("sys", "base", namespacePrefixResolver),
"_ReadProperties")); "_ReadProperties"));
assertEquals(15, granters.size()); assertEquals(18, granters.size());
} }
public void testGlobalPermissions() public void testGlobalPermissions()
{ {
Set<? extends PermissionEntry> globalPermissions = permissionModelDAO.getGlobalPermissionEntries(); Set<? extends PermissionEntry> globalPermissions = permissionModelDAO.getGlobalPermissionEntries();
assertEquals(5, globalPermissions.size()); assertEquals(NUMBER_OF_GLOBAL_PERMISSIONS, globalPermissions.size());
} }
public void testRequiredPermissions() public void testRequiredPermissions()

View File

@@ -0,0 +1,157 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2024 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.serviceaccount;
import static org.alfresco.service.cmr.security.PermissionService.ADMIN_SVC_AUTHORITY;
import static org.alfresco.service.cmr.security.PermissionService.COLLABORATOR_SVC_AUTHORITY;
import static org.alfresco.service.cmr.security.PermissionService.EDITOR_SVC_AUTHORITY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.Optional;
import java.util.Properties;
import org.junit.Before;
import org.junit.Test;
/**
* Unit tests for the {@link ServiceAccountRegistryImpl} class.
*
* @author Jamal Kaabi-Mofrad
*/
public class ServiceAccountRegistryImplTest
{
private ServiceAccountRegistryImpl serviceAccountService;
private Properties globalProperties;
@Before
public void setUp() throws Exception
{
globalProperties = new Properties();
globalProperties.put("system.test.property", "test-prop");
globalProperties.put("repo.events.test.someKey", "test-event-value");
serviceAccountService = new ServiceAccountRegistryImpl();
serviceAccountService.setGlobalProperties(globalProperties);
serviceAccountService.afterPropertiesSet();
}
@Test
public void testNoDefinedServiceAccount()
{
Optional<String> nonExistentSa = serviceAccountService.getServiceAccountRole("nonExistentServiceAccount");
assertTrue(nonExistentSa.isEmpty());
assertTrue(serviceAccountService.getServiceAccountNames().isEmpty());
}
@Test
public void testInvalidServiceAccountName()
{
globalProperties.put("serviceaccount.role. ", ADMIN_SVC_AUTHORITY);
assertTrue("Invalid service account name.", serviceAccountService.getServiceAccountNames().isEmpty());
}
@Test
public void testInvalidServiceAccountRole() throws Exception
{
globalProperties.put("serviceaccount.role.testServiceAccount", "");
serviceAccountService.afterPropertiesSet();
Optional<String> testServiceAccount = serviceAccountService.getServiceAccountRole("testServiceAccount");
assertTrue("Invalid service account role.", testServiceAccount.isEmpty());
assertTrue(serviceAccountService.getServiceAccountNames().isEmpty());
}
@Test
public void testNotSupportedServiceAccountRole() throws Exception
{
globalProperties.put("serviceaccount.role.testServiceAccount", "testRole");
serviceAccountService.afterPropertiesSet();
Optional<String> testServiceAccount = serviceAccountService.getServiceAccountRole("testServiceAccount");
assertTrue("Not supported service account role.", testServiceAccount.isEmpty());
assertTrue(serviceAccountService.getServiceAccountNames().isEmpty());
}
@Test
public void testValidServiceAccount() throws Exception
{
globalProperties.put("serviceaccount.role.testServiceAccount", ADMIN_SVC_AUTHORITY);
serviceAccountService.afterPropertiesSet();
Optional<String> testServiceAccount = serviceAccountService.getServiceAccountRole("testServiceAccount");
assertFalse("The service account role is not empty.", testServiceAccount.isEmpty());
assertEquals(ADMIN_SVC_AUTHORITY, testServiceAccount.get());
assertEquals(1, serviceAccountService.getServiceAccountNames().size());
}
@Test
public void testManyServiceAccounts() throws Exception
{
globalProperties.put("serviceaccount.role.testEditorSA", EDITOR_SVC_AUTHORITY);
globalProperties.put("serviceaccount.role.testCollaboratorSA", COLLABORATOR_SVC_AUTHORITY);
globalProperties.put("serviceaccount.role.testAdminSA", ADMIN_SVC_AUTHORITY);
serviceAccountService.afterPropertiesSet();
assertEquals(3, serviceAccountService.getServiceAccountNames().size());
Optional<String> editorSA = serviceAccountService.getServiceAccountRole("testEditorSA");
assertFalse("The service account role is not empty.", editorSA.isEmpty());
assertEquals(EDITOR_SVC_AUTHORITY, editorSA.get());
Optional<String> collaboratorSA = serviceAccountService.getServiceAccountRole("testCollaboratorSA");
assertFalse("The service account role is not empty.", collaboratorSA.isEmpty());
assertEquals(COLLABORATOR_SVC_AUTHORITY, collaboratorSA.get());
Optional<String> adminSA = serviceAccountService.getServiceAccountRole("testAdminSA");
assertFalse("The service account role is not empty.", adminSA.isEmpty());
assertEquals(ADMIN_SVC_AUTHORITY, adminSA.get());
}
@Test
public void testValidServiceAccountRoleValues() throws Exception
{
globalProperties.put("serviceaccount.role.testEditorSA", "EDITOR_SERVICE_ACCOUNT");
globalProperties.put("serviceaccount.role.testCollaboratorSA", "COLLABORATOR_SERVICE_ACCOUNT");
globalProperties.put("serviceaccount.role.testAdminSA", "ADMIN_SERVICE_ACCOUNT");
serviceAccountService.afterPropertiesSet();
assertEquals(3, serviceAccountService.getServiceAccountNames().size());
Optional<String> editorSA = serviceAccountService.getServiceAccountRole("testEditorSA");
assertFalse("The service account role is not empty.", editorSA.isEmpty());
assertEquals(EDITOR_SVC_AUTHORITY, editorSA.get());
Optional<String> collaboratorSA = serviceAccountService.getServiceAccountRole("testCollaboratorSA");
assertFalse("The service account role is not empty.", collaboratorSA.isEmpty());
assertEquals(COLLABORATOR_SVC_AUTHORITY, collaboratorSA.get());
Optional<String> adminSA = serviceAccountService.getServiceAccountRole("testAdminSA");
assertFalse("The service account role is not empty.", adminSA.isEmpty());
assertEquals(ADMIN_SVC_AUTHORITY, adminSA.get());
}
}

View File

@@ -61,3 +61,8 @@ encryption.keystore.backup.type=JCEKS
# For CI override the default hashing algorithm for password storage to save build time. # For CI override the default hashing algorithm for password storage to save build time.
system.preferred.password.encoding=sha256 system.preferred.password.encoding=sha256
# Test service accounts
serviceaccount.role.test-editor-sa=ROLE_EDITOR_SERVICE_ACCOUNT
serviceaccount.role.test-collaborator-sa=ROLE_COLLABORATOR_SERVICE_ACCOUNT
serviceaccount.role.test-admin-sa=ROLE_ADMIN_SERVICE_ACCOUNT