Passed
Push — master ( 11e3a6...398838 )
by Fabio
06:26
created

TPermissionsBehavior::setManager()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 6
rs 10
1
<?php
2
/**
3
 * TPermissionsBehavior class file.
4
 *
5
 * @author Brad Anderson <[email protected]>
6
 * @link https://github.com/pradosoft/prado
7
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
8
 * @package Prado\Security\Permissions
9
 */
10
11
namespace Prado\Security\Permissions;
12
13
use Prado\Prado;
14
use Prado\Util\IDynamicMethods;
15
use Prado\Util\TBehavior;
16
17
/**
18
 * TPermissionsBehavior class.
19
 *
20
 * TPermissionsBehavior class is a class behavior attached to {@link IPermissions}.
21
 * This class calls getPermissions to get an array of {@link TPermissionEvent}
22
 * and/or to have the implementation register their own permissions.
23
 * Any returned TPermissionEvents will have their permission registered for rules.
24
 *
25
 * This class also handles all dynamic events and when a listed Event from a
26
 * TPermissionEvent is raised, this code checks if the current application
27
 * user permission is checked.
28
 *
29
 * Example getPermissions method:
30
 * <code>
31
 *	public function getPermissions($manager) {
32
 * 		$manager->registerPermission('module_perm_edit', 'Short Description');
33
 *		return [ new TPermissionEvent('module_perm_name', 'Short Description.', ['dyPermissionAction', 'dyOtherAction']) ];
34
 *	}
35
 * </code>
36
 *
37
 * In this example, the methods dyPermissionAction and dyOtherAction would have an
38
 * authorization check on the given permission.
39
 *
40
 * The way to implement a dynamic event is like this, from the example above:
41
 * the first return value parameter is always false.
42
 * <code>
43
 *	public function myFunctionToAuth($param1, $param2) {
44
 *		if ($this->dyPermissionAction(false, $param1, $param2) === true)
45
 *			return false;
46
 *		....
47
 *		return true;
48
 *	}
49
 *	</code>
50
 * Together, TPermissionsBehavior will check the user for the 'module_perm_name'
51
 * permission.
52
 *
53
 * This can be alternatively implemented as a call to the user::can, eg
54
 * <code>
55
 *  	if(!Prado::getApplication()->getUser()->can('module_perm_name'))
56
 *			return false;
57
 * </code>
58
 *
59
 * The application user is available on and after the onAuthenticationComplete
60
 * in the application stack.
61
 *
62
 * The default is to allow without any rules in place.  To automatically
63
 * block functionality, there needs to be a (final) Permission Rule to deny all.
64
 * The TPermissionsManager, by default, adds a final rule to deny all on all
65
 * permissions via {@link TPermissionsManager::setAutoDenyAll}.
66
 *
67
 * The {@link TUserPermissionsBehavior} attaches to {@link TUser} to
68
 * provide {@link TUserPermissionsBehavior::can}, whether or note a user has authorization for a
69
 * permission.
70
 *
71
 * @author Brad Anderson <[email protected]>
72
 * @package Prado\Security\Permissions
73
 * @since 4.2.0
74
 */
75
class TPermissionsBehavior extends TBehavior implements IDynamicMethods
76
{
77
	/** @var TPermissionsManager manager object for the behavior */
78
	private $_manager;
79
	
80
	/** @var array<string, string[]> key is the dynamic event, values are the permission names to check */
81
	private $_permissionEvents;
82
	
83
	/** @var \Prado\Security\Permissions\TPermissionEvent[] */
84
	private $_events;
85
	
86
	/**
87
	 * @param TPermissionsManager
88
	 * @param null|Prado\Security\Permissions\TPermissionsManager $manager
0 ignored issues
show
Bug introduced by
The type Prado\Prado\Security\Per...ons\TPermissionsManager was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
89
	 */
90
	public function __construct($manager = null)
91
	{
92
		if ($manager) {
93
			$this->setManager($manager);
94
		}
95
		parent::__construct();
96
	}
97
	
98
	/**
99
	 * @param Prado\TComponent $owner the object being attached to
0 ignored issues
show
Bug introduced by
The type Prado\Prado\TComponent was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
100
	 */
101
	public function attach($owner)
102
	{
103
		parent::attach($owner);
104
		if (method_exists($owner, 'getPermissions')) {
105
			$this->_permissionEvents = [];
106
			$this->_events = $owner->getPermissions($this->getManager()) ?? [];
107
			foreach ($this->_events as $permEvent) {
108
				$perm = $permEvent->getName();
109
				foreach ($permEvent->getEvents() as $event) {
110
					$this->_permissionEvents[$event][] = $perm;
111
				}
112
				$this->getManager()->registerPermission($perm, $permEvent->getDescription(), $permEvent->getRules());
113
			}
114
		}
115
	}
116
	
117
	/**
118
	 * If in a proper dynamic event, checks if the application user
119
	 * can perform a permission, if it can't, flag as handled.
120
	 * @param string $method the dynamic event method being called
121
	 * @param array $args the arguments to the method
122
	 * @return bool|mixed if the $method is handled (where appropriate)
123
	 */
124
	public function __dycall($method, $args)
125
	{
126
		$callchain = array_pop($args);
127
		if (!$callchain instanceof \Prado\Util\TCallChain) {
128
			array_push($args, $callchain);
129
			return $args[0] ?? null;
130
		}
131
		
132
		$event = strtolower($method);
133
		$user = Prado::getApplication()->getUser();
134
		if (isset($this->_permissionEvents[$event]) && $user) {
135
			$can = true;
136
			$extra = array_slice($args, -1)[0] ?? null;
137
			if (is_array($extra) && isset($extra['extra'])) {
138
				$extra = $extra['extra'];
139
			} else {
140
				$extra = null;
141
			}
142
			foreach ($this->_permissionEvents[$event] as $permission) {
143
				$can &= $user->can($permission, $extra);
0 ignored issues
show
Bug introduced by
The method can() does not exist on Prado\Security\IUser. Since it exists in all sub-types, consider adding an abstract or default implementation to Prado\Security\IUser. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

143
				$can &= $user->/** @scrutinizer ignore-call */ can($permission, $extra);
Loading history...
144
			}
145
			return call_user_func_array([$callchain, $method], $args) === true || !$can;
146
		}
147
		return call_user_func_array([$callchain, $method], $args);
148
	}
149
	
150
	/**
151
	 * @return \Prado\Security\Permissions\TPermissionEvent[]
152
	 */
153
	public function getPermissionEvents()
154
	{
155
		return $this->_events ?? [];
156
	}
157
	
158
	/**
159
	 * Gets the TPermissionsManager for the behavior
160
	 * @return \Prado\Security\Permissions\TPermissionsManager manages application permissions
161
	 */
162
	public function getManager()
163
	{
164
		return $this->_manager;
165
	}
166
	
167
	/**
168
	 * Sets the TPermissionsManager for the behavior
169
	 * @param \Prado\Security\Permissions\TPermissionsManager|\WeakReference $manager manages application permissions
170
	 */
171
	public function setManager($manager)
172
	{
173
		if ($manager instanceof \WeakReference) {
174
			$manager = $manager->get();
175
		}
176
		$this->_manager = $manager;
177
	}
178
}
179