Passed
Pull Request — master (#883)
by
unknown
04:51
created

TPermissionsBehavior::setPermissionsManager()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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

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

104
			$this->_events = $owner->/** @scrutinizer ignore-call */ getPermissions($manager) ?? [];
Loading history...
105
			foreach ($this->_events as $permEvent) {
106
				$perm = $permEvent->getName();
107
				foreach ($permEvent->getEvents() as $event) {
108
					$this->_permissionEvents[$event][] = $perm;
109
				}
110
				$manager->registerPermission($perm, $permEvent->getDescription(), $permEvent->getRules());
111
			}
112
		}
113
	}
114
115
	/**
116
	 * If in a proper dynamic event, checks if the application user
117
	 * can perform a permission, if it can't, flag as handled.
118
	 * @param string $method the dynamic event method being called
119
	 * @param array $args the arguments to the method
120
	 * @return bool|mixed if the $method is handled (where appropriate)
121
	 */
122
	public function __dycall($method, $args)
123
	{
124
		$callchain = array_pop($args);
125
		if (!$callchain instanceof \Prado\Util\TCallChain) {
126
			array_push($args, $callchain);
127
			return $args[0] ?? null;
128
		}
129
130
		$event = strtolower($method);
131
		/** @var TUserPermissionsBehavior $user */
132
		$user = Prado::getApplication()->getUser();
133
		if (isset($this->_permissionEvents[$event]) && $user) {
134
			$can = true;
135
			$extra = array_slice($args, -1)[0] ?? null;
136
			if (is_array($extra) && isset($extra['extra'])) {
137
				$extra = $extra['extra'];
138
			} else {
139
				$extra = null;
140
			}
141
			foreach ($this->_permissionEvents[$event] as $permission) {
142
				$can &= $user->can($permission, $extra);
143
			}
144
			return call_user_func_array([$callchain, $method], $args) === true || !$can;
145
		}
146
		return call_user_func_array([$callchain, $method], $args);
147
	}
148
149
	/**
150
	 * @return \Prado\Security\Permissions\TPermissionEvent[]
151
	 */
152
	public function getPermissionEvents()
153
	{
154
		return $this->_events ?? [];
155
	}
156
157
	/**
158
	 * Gets the TPermissionsManager for the behavior
159
	 * @return \Prado\Security\Permissions\TPermissionsManager manages application permissions
160
	 */
161
	public function getPermissionsManager()
162
	{
163
		return $this->_manager;
164
	}
165
166
	/**
167
	 * Sets the TPermissionsManager for the behavior
168
	 * @param \Prado\Security\Permissions\TPermissionsManager|\WeakReference $manager manages application permissions
169
	 */
170
	public function setPermissionsManager($manager)
171
	{
172
		if (class_exists('\WeakReference', false) && $manager instanceof \WeakReference) {
173
			$manager = $manager->get();
174
		}
175
		$this->_manager = $manager;
0 ignored issues
show
Documentation Bug introduced by
It seems like $manager can also be of type WeakReference. However, the property $_manager is declared as type Prado\Security\Permissions\TPermissionsManager. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
176
	}
177
}
178