1: <?php
2:
3: namespace Charcoal\User;
4:
5: use InvalidArgumentException;
6:
7: // Dependencies from `zendframework/zend-permissions`
8: use Zend\Permissions\Acl\Acl;
9:
10: // Dependencies from 'PSR-3' (Logging)
11: use Psr\Log\LoggerAwareInterface;
12: use Psr\Log\LoggerAwareTrait;
13:
14: // Intra-module (`charcoal-base`) dependencies
15: use Charcoal\User\UserInterface;
16:
17: /**
18: * The authorizer service helps with user authorization (permission checking).
19: *
20: * ## Constructor dependencies
21: *
22: * Constructor dependencies are passed as an array of `key=>value` pair.
23: * The required dependencies are:
24: *
25: * - `logger` A PSR3 logger instance.
26: * - `acl` A Zend ACL (Access-Control-List) instance.
27: * - `resource` The ACL resource identifier (string).
28: *
29: * ## Checking permissions
30: *
31: * To check if a given ACL (passed in constructor) allows a list of permissions (aka privileges):
32: *
33: * - `userAllowed(UserInterface $user, string[] $aclPermissions)`
34: * - `rolesAllowed(string[] $roles, string[] $aclPermissions)`
35: */
36: class Authorizer implements LoggerAwareInterface
37: {
38: use LoggerAwareTrait;
39:
40: /**
41: * @var Acl $acl
42: */
43: private $acl;
44:
45: /**
46: * The ACL resource identifier
47: * @var string $resource
48: */
49: private $resource;
50:
51: /**
52: * @param array $data Class dependencies.
53: */
54: public function __construct(array $data)
55: {
56: $this->setLogger($data['logger']);
57: $this->setAcl($data['acl']);
58: $this->setResource($data['resource']);
59: }
60:
61: /**
62: * @param Acl $acl The ACL instance.
63: * @return void
64: */
65: private function setAcl(Acl $acl)
66: {
67: $this->acl = $acl;
68: }
69:
70: /**
71: * @return Acl
72: */
73: protected function acl()
74: {
75: return $this->acl;
76: }
77:
78: /**
79: * @param string $resource The ACL resource identifier.
80: * @throws InvalidArgumentException If the resource identifier is not a string.
81: * @return void
82: */
83: private function setResource($resource)
84: {
85: if (!is_string($resource)) {
86: throw new InvalidArgumentException(
87: 'ACL resource identifier must be a string.'
88: );
89: }
90: $this->resource = $resource;
91: }
92:
93: /**
94: * @return string
95: */
96: protected function resource()
97: {
98: return $this->resource;
99: }
100:
101: /**
102: * @param string[] $aclRoles The ACL roles to validate against.
103: * @param string[] $aclPermissions The acl permissions to validate.
104: * @return boolean Wether the permissions are allowed for a given list of roles.
105: */
106: public function rolesAllowed(array $aclRoles, array $aclPermissions)
107: {
108: $acl = $this->acl();
109: $aclResource = $this->resource();
110:
111: foreach ($aclRoles as $aclRole) {
112: foreach ($aclPermissions as $aclPermission) {
113: if (!$acl->isAllowed($aclRole, $aclResource, $aclPermission)) {
114: $this->logger->error(
115: sprintf('Role "%s" is not allowed permission "%s"', $aclRole, $aclPermission)
116: );
117: return false;
118: }
119: }
120: }
121: return true;
122: }
123:
124: /**
125: * @param UserInterface $user The user to validate against.
126: * @param string[] $aclPermissions The acl permissions to validate.
127: * @return boolean Whether the permissions are allowed for a given user.
128: */
129: public function userAllowed(UserInterface $user, array $aclPermissions)
130: {
131: return $this->rolesAllowed($user->roles(), $aclPermissions);
132: }
133: }
134: