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

TPermissionsManager::addRoleChildren()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 14
c 1
b 0
f 0
nc 4
nop 2
dl 0
loc 20
rs 9.2222
1
<?php
2
/**
3
 * TPermissionsManager 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\Exceptions\TConfigurationException;
14
use Prado\Exceptions\TInvalidOperationException;
15
use Prado\Exceptions\TInvalidDataValueException;
16
use Prado\Prado;
17
use Prado\Security\TAuthorizationRule;
18
use Prado\Security\TAuthorizationRuleCollection;
19
use Prado\TApplication;
20
use Prado\TComponent;
21
use Prado\TPropertyValue;
22
use Prado\Util\TDbParameterModule;
23
use Prado\Xml\TXmlDocument;
24
use Prado\Xml\TXmlElement;
25
26
/**
27
 * TPermissionsManager class.
28
 *
29
 * TPermissionsManager handles Permissions authorization and  Roll Based
30
 * Access Control (RBAC).  Each registered Permission is given a set of
31
 * {@link \Prado\Security\TAuthorizationRule}s.  The RBAC is based on roles
32
 * having children roles and permissions, with permissions being thought of
33
 * as special roles themselves.
34
 *
35
 * TPermissionsManager attaches {@link TPermissionsBehavior} to all classes
36
 * that implement {@link IPermissions}.  This is the main mechanism
37
 * by which application permissions are registered.
38
 *
39
 * The role hierarchy and permission rules are unique to each application.  The
40
 * permissions configuration is defined in the TPermissionsManager application
41
 * configuration or in a separate {@link PermissionsFile}. {@link TPermissionsConfigurationBehavior}
42
 * enables a page configuration to have Permission Configurations as well.
43
 * A {@link TDbParameterModule} can be specified for loading dynamic roles and
44
 * permissions.
45
 *
46
 * Module XML configurations (and similarly PermissionFile) follows the format, eg:
47
 * <code>
48
 * <module id="permissions" class="Prado\Security\Permissions\TPermissionsManager" DefaultRoles="Default" SuperRoles="Administrator">
49
 *	<role name="Developer" children="all, param_shell_permission, cron" />
50
 *	<role name="Manager" children="editor, change_user_role_permission, cron_shell" />
51
 *	<role name="cron_shell" children="cron_add_task, cron_update_task, cron_remove_task" />
52
 *	<role name="cron" children="cron_shell, cron_manage_log, cron_add_task, cron_update_task, cron_remove_task" />
53
 *  <role name="Default" children="register_user, blog_read_posts, blog_comment">
54
 *	<permissionrule name="param_shell_permission" action="deny" users="*" roles="" verb="*" IPs="" />
55
 *	<permissionrule name="cron_shell" action="allow" users="*" roles="Developer,cron_shell,cron_manage_log" verb="*" IPs="" />
56
 *	<permissionrule name="register_user" action="allow" users="?" />
57
 *	<permissionrule name="register_user" action="allow" roles="Manager" />
58
 *	<permissionrule name="change_profile" action="deny" users="?" priority="0" />
59
 *	<permissionrule name="blog_update_posts" class="Prado\Security\Permissions\TUserOwnerRule" Priority="5" />
60
 *	<permissionrule name="cron" action="allow" users="admin, user1, user2" roles="*" verb="*" IPs="*"  />
61
 *	<permissionrule name="blog_*" action="allow" users="admin, user1, user2" roles="*" verb="*" IPs="*"  />
62
 *	<permissionrule name="*" action="deny" priority="1000" />
63
 * </module>
64
 * </code>
65
 *
66
 * and in PHP the same file would follow the following format, eg:
67
 * 'modules' => [
68
 * 'permissions' =>[class => 'Prado\Security\Permissions\TPermissionsManager',
69
 * 		'properties' => ['DefaultRoles' => 'Default', 'SuperRoles' => "Administrator"],
70
 *		'roles' => [
71
 *			'Developer' => ['all', 'param_shell_permission', 'cron'],
72
 *			'Manager' => ['editor', 'change_user_role_permission', 'cron_shell'],
73
 *			'cron_shell' => ['cron_add_task', 'cron_update_task', 'cron_remove_task'],
74
 *			'cron' => ['cron_shell', 'cron_manage_log', 'cron_add_task', 'cron_update_task', 'cron_remove_task'],
75
 *			'Default' => ['register_user', 'blog_read_posts', 'blog_comment'],
76
 *		],
77
 * 		'permissionRules' => [
78
 *			[name => 'param_shell_permission', 'action' => 'deny', 'users' => '*', roles => '*', 'verb' => '*', 'IPs' =>''],
79
 *			[name => 'cron_shell', 'action' => 'allow', 'users' => 'Developer,cron_shell,cron_manage_log', roles => 'cron_shell', 'verb' => '*', 'IPs' =>''],
80
 *			[name => 'register_user', 'action' => 'allow', 'users' => '?'],
81
 *			[name => 'register_user', 'action' => 'allow', 'roles' => 'Manager'],
82
 *			[name => 'change_profile', 'action' => 'deny', 'users' => '?', 'priority' => '0'],
83
 *			[name => 'blog_update_posts', 'class' => 'Prado\Security\Permissions\TUserOwnerRule', 'priority' => '5'],
84
 *			[name => 'cron', 'action' => 'allow', 'users' => 'admin, user1, user2'],
85
 *			[name => 'blog_*', 'action' => 'allow', 'users' => 'admin, user1, user2'],
86
 *			[name => '*', 'action' => 'deny', 'priority' => 1000]
87
 * </module>
88
 *
89
 * In this example, "cron" is not a permission, but when used as a permission,
90
 * all children roles/permissions will receive the rule.  Permissions with children,
91
 * such as 'cron_shell' (above), will give all its children the rule as
92
 * well.
93
 *
94
 * A special role "All" is automatically created to contain all the permissions.
95
 * Specifying "all" as a child, is the same as specifying a role as a super role
96
 * via {@link setSuperRoles}.
97
 *
98
 * All users get the roles specified by {@link getDefaultRoles}.  This changes
99
 * the default Prado behavior for guest users having no roles.
100
 *
101
 * Intermediate roles, that are not defined in the user system, may be defined in
102
 * the hierarchy, in the above example the "cron" role is not defined by the system,
103
 * but is defined in the role hierarchy.
104
 *
105
 * Permission Rules can have multiple rules. they are
106
 * ordered by natural specified configuration order unless the rule property
107
 * {@link TAuthorizationRule::setPriority} is set.
108
 *
109
 * Permissions authorization rules may use the '*' or 'perm_*' to add the rules to all
110
 * matching permission names.  Anything before the * is matched as a permission.
111
 * This does not traverse the hierarchy roles matching the name, just the permissions
112
 * are matched for the TAuthorizationRule.
113
 *
114
 * A permission name  must list itself as a role in TAuthorizationRule for the user to be
115
 * validated for that permission name for authorization.  This is handled automatically
116
 * by TPermissionManager with the {@link getAutoAllowWithPermission} property.
117
 * By default getAutoAllowWithPermission is true, and allows any user with
118
 * that permission in their hierarchy to allow access to the functionality.
119
 * This rule priority can be set with {@link getAutoRulePriority},
120
 * where the default is 5, and -thus- before user defined rules.
121
 *
122
 * The second automatic rules includes Modules have their own preset rules that can
123
 * be automatically added via {@link getAutoPresetRules}.  By default this
124
 * is true. These rules typically allow owners of the data to be permitted without having
125
 * a permission-role.  Preset roles can define their own priorities but those
126
 * without set priorities receive the priority from {@link getAutoRulePriority}.
127
 *
128
 * The third, and last, auto-Rule is the final {@link getAutoDenyAll DenyAll}
129
 * rule. This is the last rule that denies all by default.  The AutoDenyAll
130
 * gets its rule priority from {@link getAutoDenyAllPriority}.  By default,
131
 * deny all to all permissions is enabled and thus blocking all permissions.
132
 *
133
 * Recursive hierarchy is gracefully handled, in case of any loop structures.
134
 *
135
 * @author Brad Anderson <[email protected]>
136
 * @package Prado\Security\Permissions
137
 * @method bool dyRegisterShellAction($returnValue)
138
 * @method bool dyAddRoleChildren(bool $return, string $role, string[] $children)
139
 * @method bool dyRemoveRoleChildren(bool $return, string $role, string[] $children)
140
 * @method bool dyAddPermissionRule(bool $return, string $permission, \Prado\Security\TAuthorizationRule $rule)
141
 * @method bool dyRemovePermissionRule(bool $return, string $permission, \Prado\Security\TAuthorizationRule $rule)
142
 * @since 4.2.0
143
 */
144
class TPermissionsManager extends \Prado\TModule implements IPermissions
145
{
146
	public const PERMISSIONS_BEHAVIOR = 'permissions';
147
	
148
	public const USER_PERMISSIONS_BEHAVIOR = 'usercan';
149
	
150
	public const PERMISSIONS_CONFIG_BEHAVIOR = 'permissionsConfig';
151
	
152
	public const PERM_PERMISSIONS_SHELL = 'permissions_shell';
153
	
154
	public const PERM_PERMISSIONS_MANAGE_ROLES = 'permissions_manage_roles';
155
	
156
	public const PERM_PERMISSIONS_MANAGE_RULES = 'permissions_manage_rules';
157
	
158
	/** @var string[] roles that get all permissions, default [] */
159
	private $_superRoles;
160
	
161
	/** @var string[] Default roles to give all users, default [] */
162
	private $_defaultRoles;
163
	
164
	/** @var array<string, \Prado\Security\TAuthorizationRuleCollection> contains the rules for each permission */
165
	private $_permissionRules = [];
166
	
167
	/** @var array<string, string> contains the short descriptions for each permission */
168
	private $_descriptions = [];
169
	
170
	/** @var array<string, \Prado\Security\TAuthorizationRule[]> the rules to apply to newly registered Permissions */
171
	private $_autoRules = [];
172
	
173
	/** @var array<string, string[]> contains the hierarchy of roles and children roles/permissions */
174
	private $_hierarchy = [];
175
	
176
	/** @var bool is the module initialized */
177
	private $_initialized = false;
178
	
179
	/** @var string role hierarchy and permission rules information file */
180
	private $_permissionFile;
181
	
182
	/** @var numeric the priority of the Allow With Permission Rule, default 5 */
0 ignored issues
show
Bug introduced by
The type Prado\Security\Permissions\numeric 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...
183
	private $_autoRulePriority = 5;
184
	
185
	/** @var bool add allow users with permission-role, default true  */
186
	private $_autoAllowWithPermission = true;
187
	
188
	/** @var bool add module rules, allows User's data, default true */
189
	private $_autoRulePresetRules = true;
190
	
191
	/** @var bool add Deny All rule to every permissions as the last rule, default true */
192
	private $_autoDenyAll = true;
193
	
194
	/** @var numeric the priority of the module Rule, usually these are Allow User As Owner, default 1000000 */
195
	private $_autoDenyAllPriority = 1000000;
196
	
197
	/** @var \Prado\Util\TDbParameterModule the database module providing runtime roles and rules */
198
	private $_dbParameter;
199
	
200
	/** @var numeric the priority of the module Rule, usually these are Allow User As Owner */
201
	private $_parameter = 'configuration:TPermissionsManager:runtime';
202
	
203
	// hierarchy from parameter
204
	
205
	/**
206
	 * @param \Prado\Security\Permissions\TPermissionsManager $manager
207
	 * @return TPermissionEvent[] the dynamic events to have authorization
208
	 */
209
	public function getPermissions($manager)
210
	{
211
		return [
212
			new TPermissionEvent(static::PERM_PERMISSIONS_SHELL, 'Activates permissions shell commands.', 'dyRegisterShellAction'),
213
			new TPermissionEvent(static::PERM_PERMISSIONS_MANAGE_ROLES, 'Manages Db Permissions Role Children.', ['dyAddRoleChildren', 'dyRemoveRoleChildren']),
214
			new TPermissionEvent(static::PERM_PERMISSIONS_MANAGE_RULES, 'Manages Db Permissions Rules.', ['dyAddPermissionRule', 'dyRemovePermissionRule'])
215
		];
216
	}
217
	
218
	/**
219
	 * @param array|TXmlElement $config the application configuration
220
	 */
221
	public function init($config)
222
	{
223
		$app = $this->getApplication();
224
		if (is_string($this->_dbParameter)) {
0 ignored issues
show
introduced by
The condition is_string($this->_dbParameter) is always false.
Loading history...
225
			if (($dbParameter = $app->getModule($this->_dbParameter)) === null) {
226
				throw new TConfigurationException('permissions_dbparameter_nonexistent', $this->_dbParameter);
227
			}
228
			if (!($dbParameter instanceof TDbParameterModule)) {
229
				throw new TConfigurationException('permissions_dbparameter_invalid', $this->_dbParameter);
230
			}
231
			$this->_dbParameter = $dbParameter;
232
		}
233
		
234
		if ($this->_initialized) {
235
			throw new TInvalidOperationException('permissions_init_once');
236
		}
237
		$this->_initialized = true;
238
		
239
		$manager = class_exists('\WeakReference') ? \WeakReference::create($this) : $this;
240
		TComponent::attachClassBehavior(static::PERMISSIONS_BEHAVIOR, ['class' => 'Prado\\Security\\Permissions\\TPermissionsBehavior', 'manager' => $manager], 'Prado\\Security\\Permissions\\IPermissions', -10);
0 ignored issues
show
Bug introduced by
array('class' => 'Prado\... 'manager' => $manager) of type array<string,Prado\Secur...r|WeakReference|string> is incompatible with the type object|string expected by parameter $behavior of Prado\TComponent::attachClassBehavior(). ( Ignorable by Annotation )

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

240
		TComponent::attachClassBehavior(static::PERMISSIONS_BEHAVIOR, /** @scrutinizer ignore-type */ ['class' => 'Prado\\Security\\Permissions\\TPermissionsBehavior', 'manager' => $manager], 'Prado\\Security\\Permissions\\IPermissions', -10);
Loading history...
Bug introduced by
-10 of type integer is incompatible with the type Prado\numeric|null expected by parameter $priority of Prado\TComponent::attachClassBehavior(). ( Ignorable by Annotation )

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

240
		TComponent::attachClassBehavior(static::PERMISSIONS_BEHAVIOR, ['class' => 'Prado\\Security\\Permissions\\TPermissionsBehavior', 'manager' => $manager], 'Prado\\Security\\Permissions\\IPermissions', /** @scrutinizer ignore-type */ -10);
Loading history...
241
		TComponent::attachClassBehavior(static::USER_PERMISSIONS_BEHAVIOR, ['class' => 'Prado\\Security\\Permissions\\TUserPermissionsBehavior', 'manager' => $manager], 'Prado\\Security\\IUser', -10);
242
		TComponent::attachClassBehavior(static::PERMISSIONS_CONFIG_BEHAVIOR, ['class' => 'Prado\\Security\\Permissions\\TPermissionsConfigurationBehavior', 'manager' => $manager], 'Prado\\Web\\Services\\TPageConfiguration', -10);
243
		
244
		$this->loadPermissionsData($config);
245
		if ($this->_permissionFile !== null) {
246
			if ($this->getApplication()->getConfigurationType() == TApplication::CONFIG_TYPE_PHP) {
247
				$userFile = include $this->_permissionFile;
248
				$this->loadPermissionsData($userFile);
249
			} else {
250
				$dom = new TXmlDocument;
251
				$dom->loadFromFile($this->_permissionFile);
252
				$this->loadPermissionsData($dom);
253
			}
254
		}
255
		if ($this->_dbParameter) {
256
			$this->loadPermissionsData($this->_dbParameter->get($this->_parameter));
257
		}
258
		
259
		foreach (array_map('strtolower', $this->getSuperRoles() ?? []) as $role) {
260
			$this->_hierarchy[$role] = array_merge(['all'], $this->_hierarchy[$role] ?? []);
261
		}
262
		
263
		$app->attachEventHandler('onAuthenticationComplete', [$this, 'registerShellAction']);
264
		
265
		parent::init($config);
0 ignored issues
show
Bug introduced by
It seems like $config can also be of type array; however, parameter $config of Prado\TModule::init() does only seem to accept Prado\Xml\TXmlElement, maybe add an additional type check? ( Ignorable by Annotation )

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

265
		parent::init(/** @scrutinizer ignore-type */ $config);
Loading history...
266
	}
267
	
268
	/**
269
	 * Registers a permission name with description and preset rules.
270
	 * @param string $permissionName name of the permission
271
	 * @param string $description description of the permission
272
	 * @param null|\Prado\Security\TAuthorizationRule[] $rules
273
	 */
274
	public function registerPermission($permissionName, $description, $rules = null)
275
	{
276
		$permission = strtolower($permissionName);
277
		$this->_descriptions[$permission] = TPropertyValue::ensureString($description);
278
		
279
		if ($this->_autoDenyAll === true) {
280
			$this->_autoDenyAll = 2;
0 ignored issues
show
Documentation Bug introduced by
The property $_autoDenyAll was declared of type boolean, but 2 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
281
			$this->addPermissionRuleInternal('*', new TAuthorizationRule('deny', '*', '*', '*', '*', $this->_autoDenyAllPriority));
282
		}
283
		
284
		$this->_hierarchy['all'][] = $permission;
285
		
286
		if (!isset($this->_permissionRules[$permission])) {
287
			$this->_permissionRules[$permission] = new TAuthorizationRuleCollection();
288
		} else {
289
			throw new TInvalidOperationException('permissions_duplicate_permission', $permissionName);
290
		}
291
		if ($this->_autoAllowWithPermission) {
292
			$this->_permissionRules[$permission]->add(new TAuthorizationRule('allow', '*', $permission, '*', '*', $this->_autoRulePriority));
293
		}
294
		if ($this->_autoRulePresetRules && $rules) {
295
			if (!is_array($rules)) {
0 ignored issues
show
introduced by
The condition is_array($rules) is always true.
Loading history...
296
				$rules = [$rules];
297
			}
298
			foreach ($rules as $rule) {
299
				$this->_permissionRules[$permission]->add($rule, is_numeric($p = $rule->getPriority()) ? $p : $this->_autoRulePriority);
300
			}
301
		}
302
		foreach ($this->_autoRules as $rulePerm => $rules) {
303
			$pos = strpos($rulePerm, '*');
304
			if (($pos !== false && strncmp($permission, $rulePerm, $pos) === 0) || $this->isInHierarchy($rulePerm, $permission)) {
0 ignored issues
show
Bug introduced by
$rulePerm of type string is incompatible with the type string[] expected by parameter $roles of Prado\Security\Permissio...anager::isInHierarchy(). ( Ignorable by Annotation )

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

304
			if (($pos !== false && strncmp($permission, $rulePerm, $pos) === 0) || $this->isInHierarchy(/** @scrutinizer ignore-type */ $rulePerm, $permission)) {
Loading history...
305
				$this->_permissionRules[$permission]->mergeWith($rules);
306
				if ($rulePerm === $permission) {
307
					unset($this->_autoRules[$rulePerm]);
308
				}
309
			}
310
		}
311
	}
312
	
313
	/**
314
	 * gets the short description of the permission
315
	 * @param string $permissionName name of the permission
316
	 * @return string short description of the permission
317
	 */
318
	public function getPermissionDescription($permissionName)
319
	{
320
		return $this->_descriptions[strtolower($permissionName)];
321
	}
322
	
323
	/**
324
	 * Loads the roles, children, and permission rules.
325
	 * @param array|Prado\Xml\TXMLElement configurations to parse
0 ignored issues
show
Bug introduced by
The type Prado\Security\Permissions\configurations 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...
326
	 * @param mixed $config
327
	 */
328
	public function loadPermissionsData($config)
329
	{
330
		$isXml = false;
331
		if (!$config || empty($config)) {
332
			return;
333
		}
334
		$permissions = $roles = [];
335
		if ($config instanceof TXmlElement) {
336
			$isXml = true;
337
			$roles = $config->getElementsByTagName('role');
338
			$permissions = $config->getElementsByTagName('permissionrule');
339
		} elseif (is_array($config)) {
340
			$roles = $config['roles'] ?? [];
341
			$permissions = $config['permissionrules'] ?? [];
342
		}
343
		foreach ($roles as $role => $properties) {
344
			if ($isXml) {
345
				$properties = array_change_key_case($properties->getAttributes()->toArray());
346
				$role = $properties['name'] ?? '';
347
				$children = array_map('trim', explode(',', $properties['children'] ?? ''));
348
			} else {
349
				$children = $properties;
350
				if (is_string($children)) {
351
					$children = array_map('trim', explode(',', $children));
352
				}
353
				if (!is_array($children)) {
354
					throw new TConfigurationException('permissions_role_children_invalid', $role, is_object($children) ? get_class($children) : $children);
355
				}
356
			}
357
			
358
			$role = strtolower($role);
359
			$children = array_map('strtolower', array_filter($children));
360
			
361
			$this->_hierarchy[$role] = array_merge($this->_hierarchy[$role] ?? [], $children);
362
		}
363
		foreach ($permissions as $name => $properties) {
364
			if ($isXml) {
365
				$properties = array_change_key_case($properties->getAttributes()->toArray());
366
			} else {
367
				if (!is_array($properties)) {
368
					throw new TConfigurationException('permissions_rule_invalid', $name);
369
				}
370
			}
371
			if (is_numeric($name) && (!isset($properties[0]) || !$properties[0] instanceof TAuthorizationRule)) {
372
				$name = strtolower($properties['name'] ?? '');
373
				if (!$name) {
374
					throw new TConfigurationException('permissions_rules_require_name');
375
				}
376
				$class = $properties['class'] ?? 'Prado\\Security\\TAuthorizationRule';
377
				$action = $properties['action'] ?? '';
378
				$users = $properties['users'] ?? '';
379
				$roles = $properties['roles'] ?? '';
380
				$verb = $properties['verb'] ?? '';
381
				$ips = $properties['ips'] ?? '';
382
				$priority = $properties['priority'] ?? '';
383
				
384
				$rule = new $class($action, $users, $roles, $verb, $ips, $priority);
385
			} else {
386
				$rule = $properties;
387
			}
388
			$this->addPermissionRuleInternal($name, $rule);
389
		}
390
	}
391
	
392
	/**
393
	 * Adds a permission rule to a permission name. Names can contain the '*' character
394
	 * and every permission with a matching name before the '*' will get the rule
395
	 * @param string $name Permission name
396
	 * @param \Prado\Security\TAuthorizationRule|\Prado\Security\TAuthorizationRule[] $rule
397
	 */
398
	protected function addPermissionRuleInternal($name, $rule)
399
	{
400
		if (!is_array($rule)) {
401
			$rule = [$rule];
402
		}
403
		if (($pos = strpos($name, '*')) !== false) {
404
			foreach ($this->_permissionRules as $perm => $rules) {
405
				if (strncmp($perm, $name, $pos) === 0) {
406
					$rules->mergeWith($rule);
407
				}
408
			}
409
			$this->_autoRules[$name] = array_merge($this->_autoRules[$name] ?? [], $rule);
410
		} elseif (isset($this->_permissionRules[$name])) {
411
			$this->_permissionRules[$name]->mergeWith($rule);
412
		} else {
413
			$this->_autoRules[$name] = array_merge($this->_autoRules[$name] ?? [], $rule);
414
		}
415
		if (isset($this->_hierarchy[$name])) {
416
			//Push the rule down the hierarchy to any children permissions.
417
			$set = [$name => true];
418
			$hierarchy = $this->_hierarchy[$name];
419
			while (count($hierarchy)) {
420
				$role = array_pop($hierarchy);
421
				if (!isset($set[$role])) { // stop recursive hierarchy and duplicate permissions
422
					$set[$role] = true;
423
					if (isset($this->_permissionRules[$role])) {
424
						$this->_permissionRules[$role]->mergeWith($rule);
425
					}
426
					if (isset($this->_hierarchy[$role])) {
427
						$hierarchy = array_merge($this->_hierarchy[$role], $hierarchy);
428
					}
429
				}
430
			}
431
		}
432
	}
433
	
434
	/**
435
	 * Removes a permission rule from a permission name.
436
	 * @param string $name
437
	 * @param \Prado\Security\TAuthorizationRule $rule
438
	 */
439
	protected function removePermissionRuleInternal($name, $rule)
440
	{
441
		if (($pos = strpos($name, '*')) !== false) {
442
			foreach ($this->_permissionRules as $perm => $rules) {
443
				if (strncmp($perm, $name, $pos) === 0) {
444
					$rules->remove($rule);
445
				}
446
			}
447
		} elseif (isset($this->_permissionRules[$name])) {
448
			$this->_permissionRules[$name]->remove($rule);
449
		}
450
		if (isset($this->_hierarchy[$name])) {
451
			//Push the rule down the hierarchy to any children permissions.
452
			$set = [$name => true];
453
			$hierarchy = $this->_hierarchy[$name];
454
			while (count($hierarchy)) {
455
				$role = array_pop($hierarchy);
456
				if (!isset($set[$role])) { // stop recursive hierarchy and duplicate permissions
457
					$set[$role] = true;
458
					if (isset($this->_permissionRules[$role])) {
459
						$this->_permissionRules[$role]->remove($rule);
460
					}
461
					if (isset($this->_hierarchy[$role])) {
462
						$hierarchy = array_merge($this->_hierarchy[$role], $hierarchy);
463
					}
464
				}
465
			}
466
		}
467
	}
468
	
469
	/**
470
	 * @param object $sender sender of this event handler
471
	 * @param null|mixed $param parameter for the event
472
	 */
473
	public function registerShellAction($sender, $param)
0 ignored issues
show
Unused Code introduced by
The parameter $param is not used and could be removed. ( Ignorable by Annotation )

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

473
	public function registerShellAction($sender, /** @scrutinizer ignore-unused */ $param)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $sender is not used and could be removed. ( Ignorable by Annotation )

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

473
	public function registerShellAction(/** @scrutinizer ignore-unused */ $sender, $param)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
474
	{
475
		if ($this->dyRegisterShellAction(false) !== true && ($app = $this->getApplication())->isa('Prado\\Shell\\TShellApplication')) {
476
			$app->addShellActionClass(['class' => 'Prado\\Security\\Permissions\\TPermissionsAction', 'PermissionsManager' => $this]);
0 ignored issues
show
Bug introduced by
The method addShellActionClass() does not exist on Prado\TApplication. 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

476
			$app->/** @scrutinizer ignore-call */ 
477
         addShellActionClass(['class' => 'Prado\\Security\\Permissions\\TPermissionsAction', 'PermissionsManager' => $this]);
Loading history...
477
		}
478
	}
479
	
480
	/**
481
	 * checks if the $permission is in the $roles hierarchy.
482
	 * @param string[] $roles the roles to check the permission
483
	 * @param string $permission the permission-role being checked for in the hierarchy
484
	 * @param &array<string, bool> $checked the roles already checked
485
	 */
486
	public function isInHierarchy($roles, $permission, &$checked = [])
487
	{
488
		if (!$roles) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $roles of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
489
			return false;
490
		}
491
		if (!$checked) {
492
			if (!is_array($roles)) {
0 ignored issues
show
introduced by
The condition is_array($roles) is always true.
Loading history...
493
				$roles = array_filter(array_map('trim', explode(',', $roles)));
494
			}
495
			$roles = array_map('strtolower', $roles);
496
			$permission = strtolower($permission);
497
		}
498
		if (in_array($permission, $roles)) {
499
			return true;
500
		}
501
		foreach ($roles as $role) {
502
			if (!isset($checked[$role])) {
503
				$checked[$role] = true;
504
				if (isset($this->_hierarchy[$role]) && $this->isInHierarchy($this->_hierarchy[$role], $permission, $checked)) {
505
					return true;
506
				}
507
			}
508
		}
509
		return false;
510
	}
511
	
512
	/**
513
	 * Get the roles that are runtime from the database
514
	 * @return array<string, string[]> roles and children from the database
515
	 */
516
	public function getDbConfigRoles()
517
	{
518
		if (!$this->_dbParameter || !$this->_parameter) {
519
			return [];
520
		}
521
		$runtimeData = $this->_dbParameter->get($this->_parameter) ?? [];
522
		return $runtimeData['roles'] ?? [];
523
	}
524
	
525
	/**
526
	 * Get the permission rules that are runtime from the database
527
	 * @return array<string, \Prado\Security\TAuthorizationRule[]>
528
	 */
529
	public function getDbConfigPermissionRules()
530
	{
531
		if (!$this->_dbParameter || !$this->_parameter) {
532
			return [];
533
		}
534
		$runtimeData = $this->_dbParameter->get($this->_parameter) ?? [];
535
		return $runtimeData['permissionrules'] ?? [];
536
	}
537
	
538
	/**
539
	 * This adds children to a role within the runtime context.  The children
540
	 * can be a single comma separated string.
541
	 * @param string $role the role to add children
542
	 * @param string|string[] $children the children to add to the role
543
	 * @throws TInvalidDataValueException when children is not an array
544
	 * @return bool was the method successful
545
	 */
546
	public function addRoleChildren($role, $children)
547
	{
548
		if ($this->dyAddRoleChildren(false, $role, $children) === true || !$this->_dbParameter) {
0 ignored issues
show
Bug introduced by
It seems like $children can also be of type string; however, parameter $children of Prado\Security\Permissio...er::dyAddRoleChildren() does only seem to accept string[], maybe add an additional type check? ( Ignorable by Annotation )

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

548
		if ($this->dyAddRoleChildren(false, $role, /** @scrutinizer ignore-type */ $children) === true || !$this->_dbParameter) {
Loading history...
549
			return false;
550
		}
551
		if (is_string($children)) {
552
			$children = array_map('trim', explode(',', $children));
553
		} elseif (!is_array($children)) {
0 ignored issues
show
introduced by
The condition is_array($children) is always true.
Loading history...
554
			throw new TInvalidDataValueException('permissions_children_invalid', is_object($children) ? get_class($children) : $children);
555
		}
556
		$role = strtolower($role);
557
		$children = array_map('strtolower', array_filter($children));
558
		$this->_hierarchy[$role] = array_merge($this->_hierarchy[$role] ?? [], $children);
559
		
560
		$runtimeData = $this->_dbParameter->get($this->_parameter) ?? [];
561
		$runtimeData['roles'] = $runtimeData['roles'] ?? [];
562
		$runtimeData['roles'][$role] = array_unique(array_merge($runtimeData['roles'][$role] ?? [], $children));
563
		$this->_dbParameter->set($this->_parameter, $runtimeData);
564
		
565
		return true;
566
	}
567
	
568
	/**
569
	 * This removes children from a role within the runtime context.  The children
570
	 * can be a single comma separated string.
571
	 * @param string $role the role to add children
572
	 * @param string|string[] $children the children to add to the role
573
	 * @throws TInvalidDataValueException when children is not an array
574
	 * @return bool was the method successful
575
	 */
576
	public function removeRoleChildren($role, $children)
577
	{
578
		if ($this->dyRemoveRoleChildren(false, $role, $children) === true || !$this->_dbParameter) {
0 ignored issues
show
Bug introduced by
It seems like $children can also be of type string; however, parameter $children of Prado\Security\Permissio...:dyRemoveRoleChildren() does only seem to accept string[], maybe add an additional type check? ( Ignorable by Annotation )

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

578
		if ($this->dyRemoveRoleChildren(false, $role, /** @scrutinizer ignore-type */ $children) === true || !$this->_dbParameter) {
Loading history...
579
			return false;
580
		}
581
		if (is_string($children)) {
582
			$children = array_map('trim', explode(',', $children));
583
		} elseif (!is_array($children)) {
0 ignored issues
show
introduced by
The condition is_array($children) is always true.
Loading history...
584
			throw new TInvalidDataValueException('permissions_children_invalid', is_object($children) ? get_class($children) : $children);
585
		}
586
		$role = strtolower($role);
587
		$children = array_map('strtolower', array_filter($children));
588
		$this->_hierarchy[$role] = array_values(array_diff($this->_hierarchy[$role] ?? [], $children));
589
		if (!$this->_hierarchy[$role]) {
590
			unset($this->_hierarchy[$role]);
591
		}
592
		
593
		$runtimeData = $this->_dbParameter->get($this->_parameter) ?? [];
594
		$runtimeData['roles'][$role] = array_values(array_diff($runtimeData['roles'][$role] ?? [], $children));
595
		if (!$runtimeData['roles'][$role]) {
596
			unset($runtimeData['roles'][$role]);
597
		}
598
		$this->_dbParameter->set($this->_parameter, $runtimeData);
599
		return true;
600
	}
601
	
602
	/**
603
	 * This method adds permission rules with in the runtime context.
604
	 * @param string $permission
605
	 * @param \Prado\Security\TAuthorizationRule $rule
606
	 * @return bool was the method successful
607
	 */
608
	public function addPermissionRule($permission, $rule)
609
	{
610
		$permission = strtolower($permission);
611
		
612
		if ($this->dyAddPermissionRule(false, $permission, $rule) === true || !$this->_dbParameter) {
613
			return false;
614
		}
615
		$this->addPermissionRuleInternal($permission, $rule);
616
		
617
		$runtimeData = $this->_dbParameter->get($this->_parameter) ?? [];
618
		$runtimeData['permissionrules'] = $runtimeData['permissionrules'] ?? [];
619
		$runtimeData['permissionrules'][$permission][] = $rule;
620
		$this->_dbParameter->set($this->_parameter, $runtimeData);
621
		
622
		return true;
623
	}
624
	
625
	/**
626
	 * This method removes permission rules with in the runtime context.
627
	 * @param string $permission a permission or role to remove the rule from
628
	 * @param \Prado\Security\TAuthorizationRule $rule
629
	 * @return bool was the method successful
630
	 */
631
	public function removePermissionRule($permission, $rule)
632
	{
633
		$permission = strtolower($permission);
634
		
635
		if ($this->dyRemovePermissionRule(false, $permission, $rule) === true || !$this->_dbParameter) {
636
			return false;
637
		}
638
		
639
		$this->removePermissionRuleInternal($permission, $rule);
640
		
641
		$runtimeData = $this->_dbParameter->get($this->_parameter) ?? [];
642
		$runtimeData['permissionrules'] = $runtimeData['permissionrules'] ?? [];
643
		
644
		if (($index = array_search($rule, $runtimeData['permissionrules'][$permission] ?? [], true)) === false) {
645
			return false;
646
		}
647
		unset($runtimeData['permissionrules'][$permission][$index]);
648
		if (!$runtimeData['permissionrules'][$permission]) {
649
			unset($runtimeData['permissionrules'][$permission]);
650
		} else {
651
			$runtimeData['permissionrules'][$permission] = array_values($runtimeData['permissionrules'][$permission]);
652
		}
653
		$this->_dbParameter->set($this->_parameter, $runtimeData);
654
		
655
		return true;
656
	}
657
	
658
	/**
659
	 * Gets all the roles in the hierarchy, though may not be valid roles in the application.
660
	 * @return string[] the roles in the hierarchy.
661
	 */
662
	public function getHierarchyRoles()
663
	{
664
		return array_keys($this->_hierarchy);
665
	}
666
	
667
	/**
668
	 * Gets the children for a specific role in the hierarchy.
669
	 * @param string $role the role to return its children
670
	 * @return null|string[] the children of a specific role.
671
	 */
672
	public function getHierarchyRoleChildren($role)
673
	{
674
		if (!$role) {
675
			return $this->_hierarchy;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_hierarchy returns the type array<string,string[]> which is incompatible with the documented return type null|string[].
Loading history...
676
		}
677
		return $this->_hierarchy[strtolower(TPropertyValue::ensureString($role))] ?? null;
678
	}
679
	
680
	/**
681
	 * @param null|string $permission
682
	 * @return null|array<string, TAuthorizationRuleCollection>|TAuthorizationRuleCollection
683
	 */
684
	public function getPermissionRules($permission)
685
	{
686
		if (is_string($permission)) {
687
			return $this->_permissionRules[strtolower($permission)] ?? null;
688
		} else {
689
			return $this->_permissionRules;
690
		}
691
	}
692
	
693
	/**
694
	 * All super roles will get "all" roles and thus all permissions on module init.
695
	 * @return null|string[] array of rolls that get all permissions
696
	 */
697
	public function getSuperRoles()
698
	{
699
		return $this->_superRoles;
700
	}
701
	
702
	/**
703
	 * sets the super roles to get all permissions.
704
	 * @param string|string[] $roles  of rolls that get all permissions
705
	 * @throws \Prado\Exceptions\TInvalidOperationException when the module is initialized
706
	 */
707
	public function setSuperRoles($roles)
708
	{
709
		if ($this->_initialized) {
710
			throw new TInvalidOperationException('permissions_property_unchangeable', 'SuperRoles');
711
		}
712
		if (!is_array($roles)) {
713
			$roles = array_map('trim', explode(',', $roles));
714
		}
715
		$this->_superRoles = array_filter($roles);
716
		;
717
	}
718
	
719
	/**
720
	 * Gets the default roles of all users.
721
	 * @return null|string[] the default roles of all users
722
	 */
723
	public function getDefaultRoles()
724
	{
725
		return $this->_defaultRoles;
726
	}
727
	
728
	/**
729
	 * @param string|string[] $roles the default roles of all users
730
	 * @throws \Prado\Exceptions\TInvalidOperationException when the module is initialized
731
	 */
732
	public function setDefaultRoles($roles)
733
	{
734
		if ($this->_initialized) {
735
			throw new TInvalidOperationException('permissions_property_unchangeable', 'DefaultRoles');
736
		}
737
		if (!is_array($roles)) {
738
			$roles = array_filter(array_map('trim', explode(',', $roles)));
739
		}
740
		$this->_defaultRoles = $roles;
741
	}
742
743
	/**
744
	 * @return string the full path to the file storing role/rule information
745
	 */
746
	public function getPermissionFile()
747
	{
748
		return $this->_permissionFile;
749
	}
750
751
	/**
752
	 * @param string $value role/rule data file path (in namespace form). The file format is configuration format
753
	 * whose content is similar to that role/rule block in the module configuration.
754
	 * @throws \Prado\Exceptions\TInvalidOperationException if the module is already initialized
755
	 * @throws \Prado\Exceptions\TConfigurationException if the file is not in proper namespace format
756
	 */
757
	public function setPermissionFile($value)
758
	{
759
		if ($this->_initialized) {
760
			throw new TInvalidOperationException('permissions_property_unchangeable', 'PermissionFile');
761
		} elseif (($this->_permissionFile = Prado::getPathOfNamespace($value, $this->getApplication()->getConfigurationFileExt())) === null || !is_file($this->_permissionFile)) {
762
			throw new TConfigurationException('permissions_permissionfile_invalid', $value);
763
		}
764
	}
765
	
766
	/**
767
	 * @return numeric the priority of Allow With Permission and Preset Rules, default 5
768
	 */
769
	public function getAutoRulePriority()
770
	{
771
		return $this->_autoRulePriority;
772
	}
773
	
774
	/**
775
	 * @param numeric $priority the priority of Allow With Permission and Preset Rules
776
	 * @throws \Prado\Exceptions\TInvalidOperationException if the module is already initialized
777
	 */
778
	public function setAutoRulePriority($priority)
779
	{
780
		if ($this->_initialized) {
781
			throw new TInvalidOperationException('permissions_property_unchangeable', 'AutoRulePriority');
782
		}
783
		$this->_autoRulePriority = is_numeric($priority) ? $priority : (float) $priority;
0 ignored issues
show
introduced by
The condition is_numeric($priority) is always false.
Loading history...
Documentation Bug introduced by
It seems like is_numeric($priority) ? ...ity : (double)$priority of type double is incompatible with the declared type Prado\Security\Permissions\numeric of property $_autoRulePriority.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
784
	}
785
	
786
	/**
787
	 * @return bool enable Allow With Permission rule, default true
788
	 */
789
	public function getAutoAllowWithPermission()
790
	{
791
		return $this->_autoAllowWithPermission;
792
	}
793
	
794
	/**
795
	 * @param bool $enable enable Allow With Permission rule
796
	 * @throws \Prado\Exceptions\TInvalidOperationException if the module is already initialized
797
	 */
798
	public function setAutoAllowWithPermission($enable)
799
	{
800
		if ($this->_initialized) {
801
			throw new TInvalidOperationException('permissions_property_unchangeable', 'AutoAllowWithPermission');
802
		}
803
		$this->_autoAllowWithPermission = TPropertyValue::ensureBoolean($enable);
804
	}
805
	
806
	/**
807
	 * @return bool enable Module Rules, default true
808
	 */
809
	public function getAutoPresetRules()
810
	{
811
		return $this->_autoRulePresetRules;
812
	}
813
	
814
	/**
815
	 * @param bool $enable the priority of Allow With Permission
816
	 * @throws \Prado\Exceptions\TInvalidOperationException if the module is already initialized
817
	 */
818
	public function setAutoPresetRules($enable)
819
	{
820
		if ($this->_initialized) {
821
			throw new TInvalidOperationException('permissions_property_unchangeable', 'AutoPresetRules');
822
		}
823
		$this->_autoRulePresetRules = TPropertyValue::ensureBoolean($enable);
824
	}
825
	
826
	/**
827
	 * @return bool the priority of Allow With Permission, default true
828
	 */
829
	public function getAutoDenyAll()
830
	{
831
		return $this->_autoDenyAll > 0;
832
	}
833
	
834
	/**
835
	 * @param bool $enable the priority of Allow With Permission
836
	 * @throws \Prado\Exceptions\TInvalidOperationException if the module is already initialized
837
	 */
838
	public function setAutoDenyAll($enable)
839
	{
840
		if ($this->_initialized) {
841
			throw new TInvalidOperationException('permissions_property_unchangeable', 'AutoDenyAll');
842
		}
843
		$this->_autoDenyAll = TPropertyValue::ensureBoolean($enable);
844
	}
845
	
846
	/**
847
	 * @return numeric the priority of Deny All rule, default 999999
848
	 */
849
	public function getAutoDenyAllPriority()
850
	{
851
		return $this->_autoDenyAllPriority;
852
	}
853
	
854
	/**
855
	 * @param numeric $priority the priority of Deny All rule
856
	 * @throws \Prado\Exceptions\TInvalidOperationException if the module is already initialized
857
	 */
858
	public function setAutoDenyAllPriority($priority)
859
	{
860
		if ($this->_initialized) {
861
			throw new TInvalidOperationException('permissions_property_unchangeable', 'AutoDenyAllPriority');
862
		}
863
		$this->_autoDenyAllPriority = is_numeric($priority) ? $priority : (float) $priority;
0 ignored issues
show
Documentation Bug introduced by
It seems like is_numeric($priority) ? ...ity : (double)$priority of type double is incompatible with the declared type Prado\Security\Permissions\numeric of property $_autoDenyAllPriority.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
introduced by
The condition is_numeric($priority) is always false.
Loading history...
864
	}
865
866
	/**
867
	 * @return Prado\Util\TDbParameterModule DbParameter instance
0 ignored issues
show
Bug introduced by
The type Prado\Prado\Util\TDbParameterModule 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...
868
	 */
869
	public function getDbParameter()
870
	{
871
		return $this->_dbParameter;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_dbParameter returns the type Prado\Util\TDbParameterModule which is incompatible with the documented return type Prado\Prado\Util\TDbParameterModule.
Loading history...
872
	}
873
874
	/**
875
	 * @param IUserManager|string $provider the user manager module ID or the DbParameter object
0 ignored issues
show
Bug introduced by
The type Prado\Security\Permissions\IUserManager 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...
876
	 * @throws \Prado\Exceptions\TInvalidOperationException if the module is already initialized
877
	 * @throws \Prado\Exceptions\TConfigurationException if the $provider is not a TDbParameterModule
878
	 */
879
	public function setDbParameter($provider)
880
	{
881
		if ($this->_initialized) {
882
			throw new TInvalidOperationException('permissions_property_unchangeable', 'DbParameter');
883
		}
884
		if ($provider !== null && !is_string($provider) && !($provider instanceof TDbParameterModule)) {
885
			throw new TConfigurationException('permissions_dbparameter_invalid', is_object($provider) ? get_class($provider) : $provider);
886
		}
887
		$this->_dbParameter = $provider;
0 ignored issues
show
Documentation Bug introduced by
It seems like $provider of type string is incompatible with the declared type Prado\Util\TDbParameterModule of property $_dbParameter.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
888
	}
889
890
	/**
891
	 * @return string name of the parameter to load
892
	 */
893
	public function getLoadParameter()
894
	{
895
		return $this->_parameter;
896
	}
897
898
	/**
899
	 * @param string $value name of the parameter to load
900
	 * @throws \Prado\Exceptions\TInvalidOperationException if the module is already initialized
901
	 */
902
	public function setLoadParameter($value)
903
	{
904
		if ($this->_initialized) {
905
			throw new TInvalidOperationException('permissions_property_unchangeable', 'LoadParameter');
906
		}
907
		$this->_parameter = $value;
0 ignored issues
show
Documentation Bug introduced by
It seems like $value of type string is incompatible with the declared type Prado\Security\Permissions\numeric of property $_parameter.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
908
	}
909
	
910
	/**
911
	 * detaches the automatic class behaviors
912
	 */
913
	public function __destruct()
914
	{
915
		TComponent::detachClassBehavior(static::PERMISSIONS_BEHAVIOR, 'Prado\\Security\\Permissions\\IPermissions');
916
		TComponent::detachClassBehavior(static::USER_PERMISSIONS_BEHAVIOR, 'Prado\\Security\\IUser');
917
		TComponent::detachClassBehavior(static::PERMISSIONS_CONFIG_BEHAVIOR, 'Prado\\Web\\Services\\TPageConfiguration');
918
		parent::__destruct();
919
	}
920
}
921