no message
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Bridges\SecurityDI;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Security extension for Nette DI.
|
||||
*/
|
||||
class SecurityExtension extends Nette\DI\CompilerExtension
|
||||
{
|
||||
public $defaults = [
|
||||
'debugger' => true,
|
||||
'users' => [], // of [user => password] or [user => ['password' => password, 'roles' => [role]]]
|
||||
'roles' => [], // of [role => parents]
|
||||
'resources' => [], // of [resource => parents]
|
||||
];
|
||||
|
||||
/** @var bool */
|
||||
private $debugMode;
|
||||
|
||||
|
||||
public function __construct($debugMode = false)
|
||||
{
|
||||
$this->debugMode = $debugMode;
|
||||
}
|
||||
|
||||
|
||||
public function loadConfiguration()
|
||||
{
|
||||
$config = $this->validateConfig($this->defaults);
|
||||
$builder = $this->getContainerBuilder();
|
||||
|
||||
$builder->addDefinition($this->prefix('passwords'))
|
||||
->setFactory(Nette\Security\Passwords::class);
|
||||
|
||||
$builder->addDefinition($this->prefix('userStorage'))
|
||||
->setClass(Nette\Security\IUserStorage::class)
|
||||
->setFactory(Nette\Http\UserStorage::class);
|
||||
|
||||
$user = $builder->addDefinition($this->prefix('user'))
|
||||
->setFactory(Nette\Security\User::class);
|
||||
|
||||
if ($this->debugMode && $config['debugger']) {
|
||||
$user->addSetup('@Tracy\Bar::addPanel', [
|
||||
new Nette\DI\Statement(Nette\Bridges\SecurityTracy\UserPanel::class),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($config['users']) {
|
||||
$usersList = $usersRoles = [];
|
||||
foreach ($config['users'] as $username => $data) {
|
||||
$data = is_array($data) ? $data : ['password' => $data];
|
||||
$this->validateConfig(['password' => null, 'roles' => null], $data, $this->prefix("security.users.$username"));
|
||||
$usersList[$username] = $data['password'];
|
||||
$usersRoles[$username] = isset($data['roles']) ? $data['roles'] : null;
|
||||
}
|
||||
|
||||
$builder->addDefinition($this->prefix('authenticator'))
|
||||
->setClass(Nette\Security\IAuthenticator::class)
|
||||
->setFactory(Nette\Security\SimpleAuthenticator::class, [$usersList, $usersRoles]);
|
||||
|
||||
if ($this->name === 'security') {
|
||||
$builder->addAlias('nette.authenticator', $this->prefix('authenticator'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($config['roles'] || $config['resources']) {
|
||||
$authorizator = $builder->addDefinition($this->prefix('authorizator'))
|
||||
->setClass(Nette\Security\IAuthorizator::class)
|
||||
->setFactory(Nette\Security\Permission::class);
|
||||
|
||||
foreach ($config['roles'] as $role => $parents) {
|
||||
$authorizator->addSetup('addRole', [$role, $parents]);
|
||||
}
|
||||
foreach ($config['resources'] as $resource => $parents) {
|
||||
$authorizator->addSetup('addResource', [$resource, $parents]);
|
||||
}
|
||||
|
||||
if ($this->name === 'security') {
|
||||
$builder->addAlias('nette.authorizator', $this->prefix('authorizator'));
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->name === 'security') {
|
||||
$builder->addAlias('user', $this->prefix('user'));
|
||||
$builder->addAlias('nette.userStorage', $this->prefix('userStorage'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Bridges\SecurityTracy;
|
||||
|
||||
use Nette;
|
||||
use Tracy;
|
||||
|
||||
|
||||
/**
|
||||
* User panel for Debugger Bar.
|
||||
*/
|
||||
class UserPanel implements Tracy\IBarPanel
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var Nette\Security\User */
|
||||
private $user;
|
||||
|
||||
|
||||
public function __construct(Nette\Security\User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders tab.
|
||||
* @return string
|
||||
*/
|
||||
public function getTab()
|
||||
{
|
||||
if (headers_sent() && !session_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ob_start(function () {});
|
||||
$user = $this->user;
|
||||
require __DIR__ . '/templates/UserPanel.tab.phtml';
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders panel.
|
||||
* @return string
|
||||
*/
|
||||
public function getPanel()
|
||||
{
|
||||
ob_start(function () {});
|
||||
$user = $this->user;
|
||||
require __DIR__ . '/templates/UserPanel.panel.phtml';
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Nette\Bridges\SecurityTracy;
|
||||
|
||||
use Tracy\Dumper;
|
||||
|
||||
?>
|
||||
<div class="nette-UserPanel">
|
||||
<h1><?php if ($user->isLoggedIn()): ?>Logged in<?php else: ?>Unlogged<?php endif ?></h1>
|
||||
|
||||
<?php if ($user->getIdentity()): echo Dumper::toHtml($user->getIdentity(), [Dumper::LIVE => true]); else: ?><p>no identity</p><?php endif ?>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Nette\Bridges\SecurityTracy;
|
||||
|
||||
?>
|
||||
<span title="<?= $user->isLoggedIn() ? 'Logged in' : 'Unlogged' ?>">
|
||||
<svg viewBox="0 -50 2048 2048"><path fill="<?= $user->isLoggedIn() ? '#61A519' : '#ababab' ?>" d="m1615 1803.5c-122 17-246 7-369 8-255 1-510 3-765-1-136-2-266-111-273-250-11-192 11-290.5 115-457.5 62-100 192-191 303-147 110 44 201 130 321 149 160 25 317-39 446-130 82-58 200-9 268 51 157 173 186.8 275.49 184 484.49-1.9692 147.11-108.91 271.41-230 293zm-144-1226.5c0 239-208 447-447 447s-447-208-447-447 208-447 447-447c240 1 446 207 447 447z"/></svg>
|
||||
</span>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Security;
|
||||
|
||||
|
||||
/**
|
||||
* Authentication exception.
|
||||
*/
|
||||
class AuthenticationException extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Security;
|
||||
|
||||
|
||||
/**
|
||||
* Performs authentication.
|
||||
*/
|
||||
interface IAuthenticator
|
||||
{
|
||||
/** Credential key */
|
||||
const
|
||||
USERNAME = 0,
|
||||
PASSWORD = 1;
|
||||
|
||||
/** Exception error code */
|
||||
const
|
||||
IDENTITY_NOT_FOUND = 1,
|
||||
INVALID_CREDENTIAL = 2,
|
||||
FAILURE = 3,
|
||||
NOT_APPROVED = 4;
|
||||
|
||||
/**
|
||||
* Performs an authentication against e.g. database.
|
||||
* and returns IIdentity on success or throws AuthenticationException
|
||||
* @return IIdentity
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
function authenticate(array $credentials);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Security;
|
||||
|
||||
|
||||
/**
|
||||
* Authorizator checks if a given role has authorization
|
||||
* to access a given resource.
|
||||
*/
|
||||
interface IAuthorizator
|
||||
{
|
||||
/** Set type: all */
|
||||
const ALL = null;
|
||||
|
||||
/** Permission type: allow */
|
||||
const ALLOW = true;
|
||||
|
||||
/** Permission type: deny */
|
||||
const DENY = false;
|
||||
|
||||
/**
|
||||
* Performs a role-based authorization.
|
||||
* @param string|null
|
||||
* @param string|null
|
||||
* @param string|null
|
||||
* @return bool
|
||||
*/
|
||||
function isAllowed($role, $resource, $privilege);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Security;
|
||||
|
||||
|
||||
/**
|
||||
* Represents the user of application.
|
||||
*/
|
||||
interface IIdentity
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns the ID of user.
|
||||
* @return mixed
|
||||
*/
|
||||
function getId();
|
||||
|
||||
/**
|
||||
* Returns a list of roles that the user is a member of.
|
||||
* @return array
|
||||
*/
|
||||
function getRoles();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Security;
|
||||
|
||||
|
||||
/**
|
||||
* Represents resource, an object to which access is controlled.
|
||||
*/
|
||||
interface IResource
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns a string identifier of the Resource.
|
||||
* @return string
|
||||
*/
|
||||
function getResourceId();
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Security;
|
||||
|
||||
|
||||
/**
|
||||
* Represents role, an object that may request access to an IResource.
|
||||
*/
|
||||
interface IRole
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns a string identifier of the Role.
|
||||
* @return string
|
||||
*/
|
||||
function getRoleId();
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Security;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for persistent storage for user object data.
|
||||
*/
|
||||
interface IUserStorage
|
||||
{
|
||||
/** Log-out reason {@link IUserStorage::getLogoutReason()} */
|
||||
const
|
||||
MANUAL = 0b0001,
|
||||
INACTIVITY = 0b0010;
|
||||
|
||||
/** Log-out behavior */
|
||||
const CLEAR_IDENTITY = 0b1000;
|
||||
|
||||
/** @deprecated */
|
||||
const BROWSER_CLOSED = 0b0100;
|
||||
|
||||
/**
|
||||
* Sets the authenticated status of this user.
|
||||
* @param bool
|
||||
* @return static
|
||||
*/
|
||||
function setAuthenticated($state);
|
||||
|
||||
/**
|
||||
* Is this user authenticated?
|
||||
* @return bool
|
||||
*/
|
||||
function isAuthenticated();
|
||||
|
||||
/**
|
||||
* Sets the user identity.
|
||||
* @return static
|
||||
*/
|
||||
function setIdentity(IIdentity $identity = null);
|
||||
|
||||
/**
|
||||
* Returns current user identity, if any.
|
||||
* @return IIdentity|null
|
||||
*/
|
||||
function getIdentity();
|
||||
|
||||
/**
|
||||
* Enables log out from the persistent storage after inactivity.
|
||||
* @param string|int|\DateTimeInterface number of seconds or timestamp
|
||||
* @param int flag IUserStorage::CLEAR_IDENTITY
|
||||
* @return static
|
||||
*/
|
||||
function setExpiration($time, $flags = 0);
|
||||
|
||||
/**
|
||||
* Why was user logged out?
|
||||
* @return int|null
|
||||
*/
|
||||
function getLogoutReason();
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Security;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Default implementation of IIdentity.
|
||||
*
|
||||
* @property mixed $id
|
||||
* @property array $roles
|
||||
* @property array $data
|
||||
*/
|
||||
class Identity implements IIdentity
|
||||
{
|
||||
use Nette\SmartObject {
|
||||
__get as private parentGet;
|
||||
__set as private parentSet;
|
||||
__isset as private parentIsSet;
|
||||
}
|
||||
|
||||
/** @var mixed */
|
||||
private $id;
|
||||
|
||||
/** @var array */
|
||||
private $roles;
|
||||
|
||||
/** @var array */
|
||||
private $data;
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed
|
||||
* @param mixed
|
||||
* @param iterable
|
||||
*/
|
||||
public function __construct($id, $roles = null, $data = null)
|
||||
{
|
||||
$this->setId($id);
|
||||
$this->setRoles((array) $roles);
|
||||
$this->data = $data instanceof \Traversable ? iterator_to_array($data) : (array) $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the ID of user.
|
||||
* @param mixed
|
||||
* @return static
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = is_numeric($id) && !is_float($tmp = $id * 1) ? $tmp : $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the ID of user.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets a list of roles that the user is a member of.
|
||||
* @return static
|
||||
*/
|
||||
public function setRoles(array $roles)
|
||||
{
|
||||
$this->roles = $roles;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of roles that the user is a member of.
|
||||
* @return array
|
||||
*/
|
||||
public function getRoles()
|
||||
{
|
||||
return $this->roles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a user data.
|
||||
* @return array
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets user data value.
|
||||
* @param string
|
||||
* @param mixed
|
||||
* @return void
|
||||
*/
|
||||
public function __set($key, $value)
|
||||
{
|
||||
if ($this->parentIsSet($key)) {
|
||||
$this->parentSet($key, $value);
|
||||
|
||||
} else {
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns user data value.
|
||||
* @param string
|
||||
* @return mixed
|
||||
*/
|
||||
public function &__get($key)
|
||||
{
|
||||
if ($this->parentIsSet($key)) {
|
||||
return $this->parentGet($key);
|
||||
|
||||
} else {
|
||||
return $this->data[$key];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($key)
|
||||
{
|
||||
return isset($this->data[$key]) || $this->parentIsSet($key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Security;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Passwords tools.
|
||||
*/
|
||||
class Passwords
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @deprecated */
|
||||
const BCRYPT_COST = 10;
|
||||
|
||||
|
||||
/**
|
||||
* Computes salted password hash.
|
||||
* @param string
|
||||
* @param array with cost (4-31)
|
||||
* @return string 60 chars long
|
||||
*/
|
||||
public static function hash($password, array $options = [])
|
||||
{
|
||||
$hash = @password_hash($password, PASSWORD_BCRYPT, $options); // @ is escalated to exception
|
||||
if (!$hash) {
|
||||
throw new Nette\InvalidStateException('Computed hash is invalid. ' . error_get_last()['message']);
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verifies that a password matches a hash.
|
||||
* @return bool
|
||||
*/
|
||||
public static function verify($password, $hash)
|
||||
{
|
||||
return password_verify($password, $hash);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the given hash matches the options.
|
||||
* @param string
|
||||
* @param array with cost (4-31)
|
||||
* @return bool
|
||||
*/
|
||||
public static function needsRehash($hash, array $options = [])
|
||||
{
|
||||
return password_needs_rehash($hash, PASSWORD_BCRYPT, $options);
|
||||
}
|
||||
}
|
||||
+803
@@ -0,0 +1,803 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Security;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Access control list (ACL) functionality and privileges management.
|
||||
*
|
||||
* This solution is mostly based on Zend_Acl (c) Zend Technologies USA Inc. (http://www.zend.com), new BSD license
|
||||
*
|
||||
* @copyright Copyright (c) 2005, 2007 Zend Technologies USA Inc.
|
||||
*/
|
||||
class Permission implements IAuthorizator
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var array Role storage */
|
||||
private $roles = [];
|
||||
|
||||
/** @var array Resource storage */
|
||||
private $resources = [];
|
||||
|
||||
/** @var array Access Control List rules; whitelist (deny everything to all) by default */
|
||||
private $rules = [
|
||||
'allResources' => [
|
||||
'allRoles' => [
|
||||
'allPrivileges' => [
|
||||
'type' => self::DENY,
|
||||
'assert' => null,
|
||||
],
|
||||
'byPrivilege' => [],
|
||||
],
|
||||
'byRole' => [],
|
||||
],
|
||||
'byResource' => [],
|
||||
];
|
||||
|
||||
/** @var mixed */
|
||||
private $queriedRole;
|
||||
|
||||
private $queriedResource;
|
||||
|
||||
|
||||
/********************* roles ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Adds a Role to the list. The most recently added parent
|
||||
* takes precedence over parents that were previously added.
|
||||
* @param string
|
||||
* @param string|array
|
||||
* @throws Nette\InvalidArgumentException
|
||||
* @throws Nette\InvalidStateException
|
||||
* @return static
|
||||
*/
|
||||
public function addRole($role, $parents = null)
|
||||
{
|
||||
$this->checkRole($role, false);
|
||||
if (isset($this->roles[$role])) {
|
||||
throw new Nette\InvalidStateException("Role '$role' already exists in the list.");
|
||||
}
|
||||
|
||||
$roleParents = [];
|
||||
|
||||
if ($parents !== null) {
|
||||
if (!is_array($parents)) {
|
||||
$parents = [$parents];
|
||||
}
|
||||
|
||||
foreach ($parents as $parent) {
|
||||
$this->checkRole($parent);
|
||||
$roleParents[$parent] = true;
|
||||
$this->roles[$parent]['children'][$role] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->roles[$role] = [
|
||||
'parents' => $roleParents,
|
||||
'children' => [],
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the Role exists in the list.
|
||||
* @param string
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRole($role)
|
||||
{
|
||||
$this->checkRole($role, false);
|
||||
return isset($this->roles[$role]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether Role is valid and exists in the list.
|
||||
* @param string
|
||||
* @param bool
|
||||
* @throws Nette\InvalidStateException
|
||||
* @return void
|
||||
*/
|
||||
private function checkRole($role, $throw = true)
|
||||
{
|
||||
if (!is_string($role) || $role === '') {
|
||||
throw new Nette\InvalidArgumentException('Role must be a non-empty string.');
|
||||
|
||||
} elseif ($throw && !isset($this->roles[$role])) {
|
||||
throw new Nette\InvalidStateException("Role '$role' does not exist.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all Roles.
|
||||
* @return array
|
||||
*/
|
||||
public function getRoles()
|
||||
{
|
||||
return array_keys($this->roles);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns existing Role's parents ordered by ascending priority.
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public function getRoleParents($role)
|
||||
{
|
||||
$this->checkRole($role);
|
||||
return array_keys($this->roles[$role]['parents']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if $role inherits from $inherit. If $onlyParents is true,
|
||||
* then $role must inherit directly from $inherit.
|
||||
* @param string
|
||||
* @param string
|
||||
* @param bool
|
||||
* @throws Nette\InvalidStateException
|
||||
* @return bool
|
||||
*/
|
||||
public function roleInheritsFrom($role, $inherit, $onlyParents = false)
|
||||
{
|
||||
$this->checkRole($role);
|
||||
$this->checkRole($inherit);
|
||||
|
||||
$inherits = isset($this->roles[$role]['parents'][$inherit]);
|
||||
|
||||
if ($inherits || $onlyParents) {
|
||||
return $inherits;
|
||||
}
|
||||
|
||||
foreach ($this->roles[$role]['parents'] as $parent => $foo) {
|
||||
if ($this->roleInheritsFrom($parent, $inherit)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes the Role from the list.
|
||||
*
|
||||
* @param string
|
||||
* @throws Nette\InvalidStateException
|
||||
* @return static
|
||||
*/
|
||||
public function removeRole($role)
|
||||
{
|
||||
$this->checkRole($role);
|
||||
|
||||
foreach ($this->roles[$role]['children'] as $child => $foo) {
|
||||
unset($this->roles[$child]['parents'][$role]);
|
||||
}
|
||||
|
||||
foreach ($this->roles[$role]['parents'] as $parent => $foo) {
|
||||
unset($this->roles[$parent]['children'][$role]);
|
||||
}
|
||||
|
||||
unset($this->roles[$role]);
|
||||
|
||||
foreach ($this->rules['allResources']['byRole'] as $roleCurrent => $rules) {
|
||||
if ($role === $roleCurrent) {
|
||||
unset($this->rules['allResources']['byRole'][$roleCurrent]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->rules['byResource'] as $resourceCurrent => $visitor) {
|
||||
if (isset($visitor['byRole'])) {
|
||||
foreach ($visitor['byRole'] as $roleCurrent => $rules) {
|
||||
if ($role === $roleCurrent) {
|
||||
unset($this->rules['byResource'][$resourceCurrent]['byRole'][$roleCurrent]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes all Roles from the list.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function removeAllRoles()
|
||||
{
|
||||
$this->roles = [];
|
||||
|
||||
foreach ($this->rules['allResources']['byRole'] as $roleCurrent => $rules) {
|
||||
unset($this->rules['allResources']['byRole'][$roleCurrent]);
|
||||
}
|
||||
|
||||
foreach ($this->rules['byResource'] as $resourceCurrent => $visitor) {
|
||||
foreach ($visitor['byRole'] as $roleCurrent => $rules) {
|
||||
unset($this->rules['byResource'][$resourceCurrent]['byRole'][$roleCurrent]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/********************* resources ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Adds a Resource having an identifier unique to the list.
|
||||
*
|
||||
* @param string
|
||||
* @param string
|
||||
* @throws Nette\InvalidArgumentException
|
||||
* @throws Nette\InvalidStateException
|
||||
* @return static
|
||||
*/
|
||||
public function addResource($resource, $parent = null)
|
||||
{
|
||||
$this->checkResource($resource, false);
|
||||
|
||||
if (isset($this->resources[$resource])) {
|
||||
throw new Nette\InvalidStateException("Resource '$resource' already exists in the list.");
|
||||
}
|
||||
|
||||
if ($parent !== null) {
|
||||
$this->checkResource($parent);
|
||||
$this->resources[$parent]['children'][$resource] = true;
|
||||
}
|
||||
|
||||
$this->resources[$resource] = [
|
||||
'parent' => $parent,
|
||||
'children' => [],
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the Resource exists in the list.
|
||||
* @param string
|
||||
* @return bool
|
||||
*/
|
||||
public function hasResource($resource)
|
||||
{
|
||||
$this->checkResource($resource, false);
|
||||
return isset($this->resources[$resource]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether Resource is valid and exists in the list.
|
||||
* @param string
|
||||
* @param bool
|
||||
* @throws Nette\InvalidStateException
|
||||
* @return void
|
||||
*/
|
||||
private function checkResource($resource, $throw = true)
|
||||
{
|
||||
if (!is_string($resource) || $resource === '') {
|
||||
throw new Nette\InvalidArgumentException('Resource must be a non-empty string.');
|
||||
|
||||
} elseif ($throw && !isset($this->resources[$resource])) {
|
||||
throw new Nette\InvalidStateException("Resource '$resource' does not exist.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all Resources.
|
||||
* @return array
|
||||
*/
|
||||
public function getResources()
|
||||
{
|
||||
return array_keys($this->resources);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if $resource inherits from $inherit. If $onlyParents is true,
|
||||
* then $resource must inherit directly from $inherit.
|
||||
*
|
||||
* @param string
|
||||
* @param string
|
||||
* @param bool
|
||||
* @throws Nette\InvalidStateException
|
||||
* @return bool
|
||||
*/
|
||||
public function resourceInheritsFrom($resource, $inherit, $onlyParent = false)
|
||||
{
|
||||
$this->checkResource($resource);
|
||||
$this->checkResource($inherit);
|
||||
|
||||
if ($this->resources[$resource]['parent'] === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$parent = $this->resources[$resource]['parent'];
|
||||
if ($inherit === $parent) {
|
||||
return true;
|
||||
|
||||
} elseif ($onlyParent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while ($this->resources[$parent]['parent'] !== null) {
|
||||
$parent = $this->resources[$parent]['parent'];
|
||||
if ($inherit === $parent) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes a Resource and all of its children.
|
||||
*
|
||||
* @param string
|
||||
* @throws Nette\InvalidStateException
|
||||
* @return static
|
||||
*/
|
||||
public function removeResource($resource)
|
||||
{
|
||||
$this->checkResource($resource);
|
||||
|
||||
$parent = $this->resources[$resource]['parent'];
|
||||
if ($parent !== null) {
|
||||
unset($this->resources[$parent]['children'][$resource]);
|
||||
}
|
||||
|
||||
$removed = [$resource];
|
||||
foreach ($this->resources[$resource]['children'] as $child => $foo) {
|
||||
$this->removeResource($child);
|
||||
$removed[] = $child;
|
||||
}
|
||||
|
||||
foreach ($removed as $resourceRemoved) {
|
||||
foreach ($this->rules['byResource'] as $resourceCurrent => $rules) {
|
||||
if ($resourceRemoved === $resourceCurrent) {
|
||||
unset($this->rules['byResource'][$resourceCurrent]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unset($this->resources[$resource]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes all Resources.
|
||||
* @return static
|
||||
*/
|
||||
public function removeAllResources()
|
||||
{
|
||||
foreach ($this->resources as $resource => $foo) {
|
||||
foreach ($this->rules['byResource'] as $resourceCurrent => $rules) {
|
||||
if ($resource === $resourceCurrent) {
|
||||
unset($this->rules['byResource'][$resourceCurrent]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->resources = [];
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/********************* defining rules ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Allows one or more Roles access to [certain $privileges upon] the specified Resource(s).
|
||||
* If $assertion is provided, then it must return true in order for rule to apply.
|
||||
*
|
||||
* @param string|string[]|null
|
||||
* @param string|string[]|null
|
||||
* @param string|string[]|null
|
||||
* @param callable assertion
|
||||
* @return static
|
||||
*/
|
||||
public function allow($roles = self::ALL, $resources = self::ALL, $privileges = self::ALL, $assertion = null)
|
||||
{
|
||||
$this->setRule(true, self::ALLOW, $roles, $resources, $privileges, $assertion);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Denies one or more Roles access to [certain $privileges upon] the specified Resource(s).
|
||||
* If $assertion is provided, then it must return true in order for rule to apply.
|
||||
*
|
||||
* @param string|string[]|null
|
||||
* @param string|string[]|null
|
||||
* @param string|string[]|null
|
||||
* @param callable assertion
|
||||
* @return static
|
||||
*/
|
||||
public function deny($roles = self::ALL, $resources = self::ALL, $privileges = self::ALL, $assertion = null)
|
||||
{
|
||||
$this->setRule(true, self::DENY, $roles, $resources, $privileges, $assertion);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes "allow" permissions from the list in the context of the given Roles, Resources, and privileges.
|
||||
*
|
||||
* @param string|string[]|null
|
||||
* @param string|string[]|null
|
||||
* @param string|string[]|null
|
||||
* @return static
|
||||
*/
|
||||
public function removeAllow($roles = self::ALL, $resources = self::ALL, $privileges = self::ALL)
|
||||
{
|
||||
$this->setRule(false, self::ALLOW, $roles, $resources, $privileges);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes "deny" restrictions from the list in the context of the given Roles, Resources, and privileges.
|
||||
*
|
||||
* @param string|string[]|null
|
||||
* @param string|string[]|null
|
||||
* @param string|string[]|null
|
||||
* @return static
|
||||
*/
|
||||
public function removeDeny($roles = self::ALL, $resources = self::ALL, $privileges = self::ALL)
|
||||
{
|
||||
$this->setRule(false, self::DENY, $roles, $resources, $privileges);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs operations on Access Control List rules.
|
||||
* @param bool operation add?
|
||||
* @param bool type
|
||||
* @param string|string[]|null
|
||||
* @param string|string[]|null
|
||||
* @param string|string[]|null
|
||||
* @param callable assertion
|
||||
* @throws Nette\InvalidStateException
|
||||
* @return static
|
||||
*/
|
||||
protected function setRule($toAdd, $type, $roles, $resources, $privileges, $assertion = null)
|
||||
{
|
||||
// ensure that all specified Roles exist; normalize input to array of Roles or null
|
||||
if ($roles === self::ALL) {
|
||||
$roles = [self::ALL];
|
||||
|
||||
} else {
|
||||
if (!is_array($roles)) {
|
||||
$roles = [$roles];
|
||||
}
|
||||
|
||||
foreach ($roles as $role) {
|
||||
$this->checkRole($role);
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that all specified Resources exist; normalize input to array of Resources or null
|
||||
if ($resources === self::ALL) {
|
||||
$resources = [self::ALL];
|
||||
|
||||
} else {
|
||||
if (!is_array($resources)) {
|
||||
$resources = [$resources];
|
||||
}
|
||||
|
||||
foreach ($resources as $resource) {
|
||||
$this->checkResource($resource);
|
||||
}
|
||||
}
|
||||
|
||||
// normalize privileges to array
|
||||
if ($privileges === self::ALL) {
|
||||
$privileges = [];
|
||||
|
||||
} elseif (!is_array($privileges)) {
|
||||
$privileges = [$privileges];
|
||||
}
|
||||
|
||||
if ($toAdd) { // add to the rules
|
||||
foreach ($resources as $resource) {
|
||||
foreach ($roles as $role) {
|
||||
$rules = &$this->getRules($resource, $role, true);
|
||||
if (count($privileges) === 0) {
|
||||
$rules['allPrivileges']['type'] = $type;
|
||||
$rules['allPrivileges']['assert'] = $assertion;
|
||||
if (!isset($rules['byPrivilege'])) {
|
||||
$rules['byPrivilege'] = [];
|
||||
}
|
||||
} else {
|
||||
foreach ($privileges as $privilege) {
|
||||
$rules['byPrivilege'][$privilege]['type'] = $type;
|
||||
$rules['byPrivilege'][$privilege]['assert'] = $assertion;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else { // remove from the rules
|
||||
foreach ($resources as $resource) {
|
||||
foreach ($roles as $role) {
|
||||
$rules = &$this->getRules($resource, $role);
|
||||
if ($rules === null) {
|
||||
continue;
|
||||
}
|
||||
if (count($privileges) === 0) {
|
||||
if ($resource === self::ALL && $role === self::ALL) {
|
||||
if ($type === $rules['allPrivileges']['type']) {
|
||||
$rules = [
|
||||
'allPrivileges' => [
|
||||
'type' => self::DENY,
|
||||
'assert' => null,
|
||||
],
|
||||
'byPrivilege' => [],
|
||||
];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ($type === $rules['allPrivileges']['type']) {
|
||||
unset($rules['allPrivileges']);
|
||||
}
|
||||
} else {
|
||||
foreach ($privileges as $privilege) {
|
||||
if (isset($rules['byPrivilege'][$privilege]) &&
|
||||
$type === $rules['byPrivilege'][$privilege]['type']
|
||||
) {
|
||||
unset($rules['byPrivilege'][$privilege]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/********************* querying the ACL ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if and only if the Role has access to [certain $privileges upon] the Resource.
|
||||
*
|
||||
* This method checks Role inheritance using a depth-first traversal of the Role list.
|
||||
* The highest priority parent (i.e., the parent most recently added) is checked first,
|
||||
* and its respective parents are checked similarly before the lower-priority parents of
|
||||
* the Role are checked.
|
||||
*
|
||||
* @param string|null|IRole $role
|
||||
* @param string|null|IResource $resource
|
||||
* @param string|null $privilege
|
||||
* @throws Nette\InvalidStateException
|
||||
* @return bool
|
||||
*/
|
||||
public function isAllowed($role = self::ALL, $resource = self::ALL, $privilege = self::ALL)
|
||||
{
|
||||
$this->queriedRole = $role;
|
||||
if ($role !== self::ALL) {
|
||||
if ($role instanceof IRole) {
|
||||
$role = $role->getRoleId();
|
||||
}
|
||||
$this->checkRole($role);
|
||||
}
|
||||
|
||||
$this->queriedResource = $resource;
|
||||
if ($resource !== self::ALL) {
|
||||
if ($resource instanceof IResource) {
|
||||
$resource = $resource->getResourceId();
|
||||
}
|
||||
$this->checkResource($resource);
|
||||
}
|
||||
|
||||
do {
|
||||
// depth-first search on $role if it is not 'allRoles' pseudo-parent
|
||||
if ($role !== null && ($result = $this->searchRolePrivileges($privilege === self::ALL, $role, $resource, $privilege)) !== null) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($privilege === self::ALL) {
|
||||
if ($rules = $this->getRules($resource, self::ALL)) { // look for rule on 'allRoles' psuedo-parent
|
||||
foreach ($rules['byPrivilege'] as $privilege => $rule) {
|
||||
if (($result = $this->getRuleType($resource, null, $privilege)) === self::DENY) {
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
if (($result = $this->getRuleType($resource, null, null)) !== null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (($result = $this->getRuleType($resource, null, $privilege)) !== null) { // look for rule on 'allRoles' pseudo-parent
|
||||
break;
|
||||
|
||||
} elseif (($result = $this->getRuleType($resource, null, null)) !== null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$resource = $this->resources[$resource]['parent']; // try next Resource
|
||||
} while (true);
|
||||
|
||||
$this->queriedRole = $this->queriedResource = null;
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns real currently queried Role. Use by assertion.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getQueriedRole()
|
||||
{
|
||||
return $this->queriedRole;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns real currently queried Resource. Use by assertion.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getQueriedResource()
|
||||
{
|
||||
return $this->queriedResource;
|
||||
}
|
||||
|
||||
|
||||
/********************* internals ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Performs a depth-first search of the Role DAG, starting at $role, in order to find a rule
|
||||
* allowing/denying $role access to a/all $privilege upon $resource.
|
||||
* @param bool all (true) or one?
|
||||
* @param string
|
||||
* @param string
|
||||
* @param string only for one
|
||||
* @return mixed null if no applicable rule is found, otherwise returns ALLOW or DENY
|
||||
*/
|
||||
private function searchRolePrivileges($all, $role, $resource, $privilege)
|
||||
{
|
||||
$dfs = [
|
||||
'visited' => [],
|
||||
'stack' => [$role],
|
||||
];
|
||||
|
||||
while (($role = array_pop($dfs['stack'])) !== null) {
|
||||
if (isset($dfs['visited'][$role])) {
|
||||
continue;
|
||||
}
|
||||
if ($all) {
|
||||
if ($rules = $this->getRules($resource, $role)) {
|
||||
foreach ($rules['byPrivilege'] as $privilege2 => $rule) {
|
||||
if ($this->getRuleType($resource, $role, $privilege2) === self::DENY) {
|
||||
return self::DENY;
|
||||
}
|
||||
}
|
||||
if (($type = $this->getRuleType($resource, $role, null)) !== null) {
|
||||
return $type;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (($type = $this->getRuleType($resource, $role, $privilege)) !== null) {
|
||||
return $type;
|
||||
|
||||
} elseif (($type = $this->getRuleType($resource, $role, null)) !== null) {
|
||||
return $type;
|
||||
}
|
||||
}
|
||||
|
||||
$dfs['visited'][$role] = true;
|
||||
foreach ($this->roles[$role]['parents'] as $roleParent => $foo) {
|
||||
$dfs['stack'][] = $roleParent;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the rule type associated with the specified Resource, Role, and privilege.
|
||||
* @param string|null $resource
|
||||
* @param string|null $role
|
||||
* @param string|null $privilege
|
||||
* @return bool|null null if a rule does not exist or assertion fails, otherwise returns ALLOW or DENY
|
||||
*/
|
||||
private function getRuleType($resource, $role, $privilege)
|
||||
{
|
||||
if (!$rules = $this->getRules($resource, $role)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($privilege === self::ALL) {
|
||||
if (isset($rules['allPrivileges'])) {
|
||||
$rule = $rules['allPrivileges'];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} elseif (!isset($rules['byPrivilege'][$privilege])) {
|
||||
return null;
|
||||
|
||||
} else {
|
||||
$rule = $rules['byPrivilege'][$privilege];
|
||||
}
|
||||
|
||||
if ($rule['assert'] === null || call_user_func($rule['assert'], $this, $role, $resource, $privilege)) {
|
||||
return $rule['type'];
|
||||
|
||||
} elseif ($resource !== self::ALL || $role !== self::ALL || $privilege !== self::ALL) {
|
||||
return null;
|
||||
|
||||
} elseif ($rule['type'] === self::ALLOW) {
|
||||
return self::DENY;
|
||||
|
||||
} else {
|
||||
return self::ALLOW;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the rules associated with a Resource and a Role, or null if no such rules exist.
|
||||
* If the $create parameter is true, then a rule set is first created and then returned to the caller.
|
||||
* @param string|null $resource
|
||||
* @param string|null $role
|
||||
* @param bool
|
||||
* @return array|null
|
||||
*/
|
||||
private function &getRules($resource, $role, $create = false)
|
||||
{
|
||||
$null = null;
|
||||
if ($resource === self::ALL) {
|
||||
$visitor = &$this->rules['allResources'];
|
||||
} else {
|
||||
if (!isset($this->rules['byResource'][$resource])) {
|
||||
if (!$create) {
|
||||
return $null;
|
||||
}
|
||||
$this->rules['byResource'][$resource] = [];
|
||||
}
|
||||
$visitor = &$this->rules['byResource'][$resource];
|
||||
}
|
||||
|
||||
if ($role === self::ALL) {
|
||||
if (!isset($visitor['allRoles'])) {
|
||||
if (!$create) {
|
||||
return $null;
|
||||
}
|
||||
$visitor['allRoles']['byPrivilege'] = [];
|
||||
}
|
||||
return $visitor['allRoles'];
|
||||
}
|
||||
|
||||
if (!isset($visitor['byRole'][$role])) {
|
||||
if (!$create) {
|
||||
return $null;
|
||||
}
|
||||
$visitor['byRole'][$role]['byPrivilege'] = [];
|
||||
}
|
||||
|
||||
return $visitor['byRole'][$role];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Security;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Trivial implementation of IAuthenticator.
|
||||
*/
|
||||
class SimpleAuthenticator implements IAuthenticator
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var array */
|
||||
private $userlist;
|
||||
|
||||
/** @var array */
|
||||
private $usersRoles;
|
||||
|
||||
|
||||
/**
|
||||
* @param array list of pairs username => password
|
||||
* @param array list of pairs username => role[]
|
||||
*/
|
||||
public function __construct(array $userlist, array $usersRoles = [])
|
||||
{
|
||||
$this->userlist = $userlist;
|
||||
$this->usersRoles = $usersRoles;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Performs an authentication against e.g. database.
|
||||
* and returns IIdentity on success or throws AuthenticationException
|
||||
* @return IIdentity
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
public function authenticate(array $credentials)
|
||||
{
|
||||
list($username, $password) = $credentials;
|
||||
foreach ($this->userlist as $name => $pass) {
|
||||
if (strcasecmp($name, $username) === 0) {
|
||||
if ((string) $pass === (string) $password) {
|
||||
return new Identity($name, isset($this->usersRoles[$name]) ? $this->usersRoles[$name] : null);
|
||||
} else {
|
||||
throw new AuthenticationException('Invalid password.', self::INVALID_CREDENTIAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new AuthenticationException("User '$username' not found.", self::IDENTITY_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
namespace Nette\Security;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* User authentication and authorization.
|
||||
*
|
||||
* @property-read bool $loggedIn
|
||||
* @property-read IIdentity $identity
|
||||
* @property-read mixed $id
|
||||
* @property-read array $roles
|
||||
* @property-read int $logoutReason
|
||||
* @property IAuthenticator $authenticator
|
||||
* @property IAuthorizator $authorizator
|
||||
*/
|
||||
class User
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @deprecated */
|
||||
const
|
||||
MANUAL = IUserStorage::MANUAL,
|
||||
INACTIVITY = IUserStorage::INACTIVITY;
|
||||
|
||||
/** @deprecated */
|
||||
const BROWSER_CLOSED = IUserStorage::BROWSER_CLOSED;
|
||||
|
||||
/** @var string default role for unauthenticated user */
|
||||
public $guestRole = 'guest';
|
||||
|
||||
/** @var string default role for authenticated user without own identity */
|
||||
public $authenticatedRole = 'authenticated';
|
||||
|
||||
/** @var callable[] function (User $sender); Occurs when the user is successfully logged in */
|
||||
public $onLoggedIn;
|
||||
|
||||
/** @var callable[] function (User $sender); Occurs when the user is logged out */
|
||||
public $onLoggedOut;
|
||||
|
||||
/** @var IUserStorage Session storage for current user */
|
||||
private $storage;
|
||||
|
||||
/** @var IAuthenticator|null */
|
||||
private $authenticator;
|
||||
|
||||
/** @var IAuthorizator|null */
|
||||
private $authorizator;
|
||||
|
||||
|
||||
public function __construct(IUserStorage $storage, IAuthenticator $authenticator = null, IAuthorizator $authorizator = null)
|
||||
{
|
||||
$this->storage = $storage;
|
||||
$this->authenticator = $authenticator;
|
||||
$this->authorizator = $authorizator;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return IUserStorage
|
||||
*/
|
||||
public function getStorage()
|
||||
{
|
||||
return $this->storage;
|
||||
}
|
||||
|
||||
|
||||
/********************* Authentication ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Conducts the authentication process. Parameters are optional.
|
||||
* @param string|IIdentity username or Identity
|
||||
* @param string
|
||||
* @return void
|
||||
* @throws AuthenticationException if authentication was not successful
|
||||
*/
|
||||
public function login($user, $password = null)
|
||||
{
|
||||
$this->logout(true);
|
||||
if (!$user instanceof IIdentity) {
|
||||
$user = $this->getAuthenticator()->authenticate(func_get_args());
|
||||
}
|
||||
$this->storage->setIdentity($user);
|
||||
$this->storage->setAuthenticated(true);
|
||||
$this->onLoggedIn($this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Logs out the user from the current session.
|
||||
* @param bool clear the identity from persistent storage?
|
||||
* @return void
|
||||
*/
|
||||
public function logout($clearIdentity = false)
|
||||
{
|
||||
if ($this->isLoggedIn()) {
|
||||
$this->onLoggedOut($this);
|
||||
$this->storage->setAuthenticated(false);
|
||||
}
|
||||
if ($clearIdentity) {
|
||||
$this->storage->setIdentity(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is this user authenticated?
|
||||
* @return bool
|
||||
*/
|
||||
public function isLoggedIn()
|
||||
{
|
||||
return $this->storage->isAuthenticated();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns current user identity, if any.
|
||||
* @return IIdentity|null
|
||||
*/
|
||||
public function getIdentity()
|
||||
{
|
||||
return $this->storage->getIdentity();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns current user ID, if any.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
$identity = $this->getIdentity();
|
||||
return $identity ? $identity->getId() : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets authentication handler.
|
||||
* @return static
|
||||
*/
|
||||
public function setAuthenticator(IAuthenticator $handler)
|
||||
{
|
||||
$this->authenticator = $handler;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns authentication handler.
|
||||
* @return IAuthenticator|null
|
||||
*/
|
||||
public function getAuthenticator($throw = true)
|
||||
{
|
||||
if ($throw && !$this->authenticator) {
|
||||
throw new Nette\InvalidStateException('Authenticator has not been set.');
|
||||
}
|
||||
return $this->authenticator;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enables log out after inactivity.
|
||||
* @param string|int|\DateTimeInterface number of seconds or timestamp
|
||||
* @param int|bool flag IUserStorage::CLEAR_IDENTITY
|
||||
* @param bool clear the identity from persistent storage? (deprecated)
|
||||
* @return static
|
||||
*/
|
||||
public function setExpiration($time, $flags = null, $clearIdentity = false)
|
||||
{
|
||||
$clearIdentity = $clearIdentity || $flags === IUserStorage::CLEAR_IDENTITY;
|
||||
$this->storage->setExpiration($time, $clearIdentity ? IUserStorage::CLEAR_IDENTITY : 0);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Why was user logged out?
|
||||
* @return int|null
|
||||
*/
|
||||
public function getLogoutReason()
|
||||
{
|
||||
return $this->storage->getLogoutReason();
|
||||
}
|
||||
|
||||
|
||||
/********************* Authorization ****************d*g**/
|
||||
|
||||
|
||||
/**
|
||||
* Returns a list of effective roles that a user has been granted.
|
||||
* @return array
|
||||
*/
|
||||
public function getRoles()
|
||||
{
|
||||
if (!$this->isLoggedIn()) {
|
||||
return [$this->guestRole];
|
||||
}
|
||||
|
||||
$identity = $this->getIdentity();
|
||||
return $identity && $identity->getRoles() ? $identity->getRoles() : [$this->authenticatedRole];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is a user in the specified effective role?
|
||||
* @param string
|
||||
* @return bool
|
||||
*/
|
||||
public function isInRole($role)
|
||||
{
|
||||
return in_array($role, $this->getRoles(), true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Has a user effective access to the Resource?
|
||||
* If $resource is null, then the query applies to all resources.
|
||||
* @param string resource
|
||||
* @param string privilege
|
||||
* @return bool
|
||||
*/
|
||||
public function isAllowed($resource = IAuthorizator::ALL, $privilege = IAuthorizator::ALL)
|
||||
{
|
||||
foreach ($this->getRoles() as $role) {
|
||||
if ($this->getAuthorizator()->isAllowed($role, $resource, $privilege)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets authorization handler.
|
||||
* @return static
|
||||
*/
|
||||
public function setAuthorizator(IAuthorizator $handler)
|
||||
{
|
||||
$this->authorizator = $handler;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns current authorization handler.
|
||||
* @return IAuthorizator|null
|
||||
*/
|
||||
public function getAuthorizator($throw = true)
|
||||
{
|
||||
if ($throw && !$this->authorizator) {
|
||||
throw new Nette\InvalidStateException('Authorizator has not been set.');
|
||||
}
|
||||
return $this->authorizator;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user