Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplified management of roles and allowed to made them inheritable. #2241

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions application/config/module.config.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,47 @@
<?php
namespace Omeka;

use \Omeka\Permissions\Acl;

return [
'roles' => [
Acl::ROLE_GLOBAL_ADMIN => [
'role' => Acl::ROLE_GLOBAL_ADMIN,
'label' => 'Global Administrator', // @translate
'admin' => true,
'parents' => [],
],
Acl::ROLE_SITE_ADMIN => [
'role' => Acl::ROLE_SITE_ADMIN,
'label' => 'Supervisor', // @translate
'admin' => true,
'parents' => [],
],
Acl::ROLE_EDITOR => [
'role' => Acl::ROLE_EDITOR,
'label' => 'Editor', // @translate
'admin' => false,
'parents' => [],
],
Acl::ROLE_REVIEWER => [
'role' => Acl::ROLE_REVIEWER,
'label' => 'Reviewer', // @translate
'admin' => false,
'parents' => [],
],
Acl::ROLE_AUTHOR => [
'role' => Acl::ROLE_AUTHOR,
'label' => 'Author', // @translate
'admin' => false,
'parents' => [],
],
Acl::ROLE_RESEARCHER => [
'role' => Acl::ROLE_RESEARCHER,
'label' => 'Researcher', // @translate
'admin' => false,
'parents' => [],
],
],
'password' => [
'min_length' => 6,
'min_lowercase' => null,
Expand Down
79 changes: 31 additions & 48 deletions application/src/Permissions/Acl.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

use Omeka\Api\ResourceInterface;
use Laminas\Authentication\AuthenticationServiceInterface;
use Laminas\Permissions\Acl\Acl as ZendAcl;
use Laminas\Permissions\Acl\Acl as LaminasAcl;

class Acl extends ZendAcl
class Acl extends LaminasAcl
{
const ROLE_GLOBAL_ADMIN = 'global_admin';
const ROLE_SITE_ADMIN = 'site_admin';
Expand All @@ -15,107 +15,90 @@ class Acl extends ZendAcl
const ROLE_RESEARCHER = 'researcher';

/**
* @var array
* @var AuthenticationServiceInterface
*/
protected $roleLabels = [
self::ROLE_GLOBAL_ADMIN => 'Global Administrator', // @translate
self::ROLE_SITE_ADMIN => 'Supervisor', // @translate
self::ROLE_EDITOR => 'Editor', // @translate
self::ROLE_REVIEWER => 'Reviewer', // @translate
self::ROLE_AUTHOR => 'Author', // @translate
self::ROLE_RESEARCHER => 'Researcher', // @translate
];
protected $auth;

/**
* Roles that are "admins" and restricted for editing.
*
* @var array
*/
protected $adminRoles = [
self::ROLE_GLOBAL_ADMIN,
self::ROLE_SITE_ADMIN,
];

/**
* @var AuthenticationServiceInterface
*/
protected $auth;
protected $configRoles;

public function setAuthenticationService(AuthenticationServiceInterface $auth)
{
$this->auth = $auth;
}

public function getAuthenticationService()
public function getAuthenticationService(): AuthenticationServiceInterface
{
return $this->auth;
}

public function setConfigRoles(array $configRoles)
{
$this->configRoles = $configRoles;
}

/**
* Get role names and their labels.
*
* @param bool $excludeAdminRoles Whether to only return the non-admin
* roles. False by default, so all roles are returned.
* @return array
*/
public function getRoleLabels($excludeAdminRoles = false)
public function getRoleLabels($excludeAdminRoles = false): array
{
$labels = $this->roleLabels;

if ($excludeAdminRoles) {
foreach ($this->adminRoles as $role) {
unset($labels[$role]);
}
}
return $labels;
$labels = array_column($this->configRoles, 'label', 'role');
return $excludeAdminRoles
? array_diff_key($labels, array_filter(array_column($this->configRoles, 'admin', 'role')))
: $labels;
}

/**
* Authorize the current user.
*
* @param ResourceInterface|string $resource
* @param string $privilege
* @return bool
*/
public function userIsAllowed($resource = null, $privilege = null)
public function userIsAllowed($resource = null, $privilege = null): bool
{
$auth = $this->auth;
$role = null;
if ($auth) {
$role = $auth->getIdentity();
}
$role = $this->auth
? $this->auth->getIdentity()
: null;
return $this->isAllowed($role, $resource, $privilege);
}

/**
* Determine whether the admin role is an "admin" role that carries
* restrictions beyond other roles.
*
* @return bool
*/
public function isAdminRole($role)
public function isAdminRole($role): bool
{
return in_array($role, $this->adminRoles);
return !empty($this->configRoles[$role]['admin']);
}

/**
* Add a role label to the ACL
*
* @param $roleId
* @param $roleLabel
* @param string $roleId
* @param string $roleLabel
*
* @deprecated Use main config acl labels.
*/
public function addRoleLabel($roleId, $roleLabel)
{
$this->roleLabels[$roleId] = $roleLabel;
$this->configRoles[$roleId]['label'] = $roleLabel;
}

/**
* Remove a role label from the ACL
*
* @param $roleId
*
* @deprecated Will be removed in a future version.
* @todo Check the purpose of this method that is never used.
*/
public function removeRoleLabel($roleId)
{
unset($this->roleLabels[$roleId]);
$this->configRoles[$roleId]['label'] = null;
}
}
33 changes: 22 additions & 11 deletions application/src/Service/AclFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
*/
class AclFactory implements FactoryInterface
{
/**
* @var \Laminas\ServiceManager\ServiceLocatorInterface
*/
protected $services;

/**
* @var array
*/
protected $configRoles;

/**
* Create the access control list.
*
Expand All @@ -28,11 +38,16 @@ public function __invoke(ContainerInterface $serviceLocator, $requestedName, arr
{
$acl = new Acl;

$this->services = $serviceLocator;

$auth = $serviceLocator->get('Omeka\AuthenticationService');
$acl->setAuthenticationService($auth);

$this->configRoles = $serviceLocator->get('Config')['roles'];
$acl->setConfigRoles($this->configRoles);

$this->addRoles($acl);
$this->addResources($acl, $serviceLocator);
$this->addResources($acl);

$status = $serviceLocator->get('Omeka\Status');
if (!$status->isInstalled()
Expand All @@ -54,12 +69,9 @@ public function __invoke(ContainerInterface $serviceLocator, $requestedName, arr
*/
protected function addRoles(Acl $acl)
{
$acl->addRole(Acl::ROLE_RESEARCHER)
->addRole(Acl::ROLE_AUTHOR)
->addRole(Acl::ROLE_REVIEWER)
->addRole(Acl::ROLE_EDITOR)
->addRole(Acl::ROLE_SITE_ADMIN)
->addRole(Acl::ROLE_GLOBAL_ADMIN);
foreach ($this->configRoles as $role => $roleData) {
$acl->addRole($role, empty($roleData['parents']) ? null : $roleData['parents']);
}
}

/**
Expand All @@ -72,11 +84,10 @@ protected function addRoles(Acl $acl)
* - Controller classes
*
* @param Acl $acl
* @param ContainerInterface $serviceLocator
*/
protected function addResources(Acl $acl, ContainerInterface $serviceLocator)
protected function addResources(Acl $acl)
{
$config = $serviceLocator->get('Config');
$config = $this->services->get('Config');

// Add resources from configuration.
if (isset($config['permissions']['acl_resources'])
Expand All @@ -100,7 +111,7 @@ protected function addResources(Acl $acl, ContainerInterface $serviceLocator)

// Add Doctrine entities as ACL resources. These resources are used to
// set rules for access to specific entities.
$entities = $serviceLocator->get('Omeka\EntityManager')->getConfiguration()
$entities = $this->services->get('Omeka\EntityManager')->getConfiguration()
->getMetadataDriverImpl()->getAllClassNames();
foreach ($entities as $entityClass) {
if (is_subclass_of($entityClass, 'Laminas\Permissions\Acl\Resource\ResourceInterface')) {
Expand Down