Introduce Authority interface
This introduces the Authority interface and an implementation of basic functionality. This serves as a basis for spike exploring the concept. Bug: T261963 Change-Id: Idd4e8bd934f191296eada443450bc69500102937
This commit is contained in:
parent
927a423d5b
commit
0ac16e53aa
9 changed files with 1324 additions and 0 deletions
155
includes/Permissions/Authority.php
Normal file
155
includes/Permissions/Authority.php
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace MediaWiki\Permissions;
|
||||||
|
|
||||||
|
use MediaWiki\Page\PageIdentity;
|
||||||
|
use MediaWiki\User\UserIdentity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @unstable
|
||||||
|
* @since 1.36
|
||||||
|
*/
|
||||||
|
interface Authority {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the actor associated with this authority.
|
||||||
|
*
|
||||||
|
* Actions performed under this authority should generally be attributed
|
||||||
|
* to the actor returned by this method.
|
||||||
|
*
|
||||||
|
* @return UserIdentity
|
||||||
|
*/
|
||||||
|
public function getActor(): UserIdentity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this authority has the given permission in general.
|
||||||
|
* For some permissions, exceptions may exist, both positive and negative, on a per-target basis.
|
||||||
|
*
|
||||||
|
* @param string $permission
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isAllowed( string $permission ): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this authority has any of the given permissions in general.
|
||||||
|
*
|
||||||
|
* Implementations must ensure that this method returns true if isAllowed would return true
|
||||||
|
* for any of the given permissions. Calling isAllowedAny() with one parameter must be
|
||||||
|
* equivalent to calling isAllowed(). Calling isAllowedAny() with no parameter is not allowed.
|
||||||
|
*
|
||||||
|
* @see isAllowed
|
||||||
|
*
|
||||||
|
* @param string ...$permissions Permissions to test. At least one must be given.
|
||||||
|
* @return bool True if user is allowed to perform *any* of the given actions
|
||||||
|
*/
|
||||||
|
public function isAllowedAny( ...$permissions ): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this authority has any of the given permissions in general.
|
||||||
|
*
|
||||||
|
* Implementations must ensure that this method returns false if isAllowed would return false
|
||||||
|
* for any of the given permissions. Calling isAllowedAll() with one parameter must be
|
||||||
|
* equivalent to calling isAllowed(). Calling isAllowedAny() with no parameter is not allowed.
|
||||||
|
*
|
||||||
|
* @see isAllowed
|
||||||
|
*
|
||||||
|
* @param string ...$permissions Permissions to test. At least one must be given.
|
||||||
|
* @return bool True if the user is allowed to perform *all* of the given actions
|
||||||
|
*/
|
||||||
|
public function isAllowedAll( ...$permissions ): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this authority can probably perform the given action on the given target page.
|
||||||
|
* This method offers a fast, lightweight check, and may produce false positives.
|
||||||
|
* It is intended for determining which UI elements should be offered to the user.
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status aggregator for failures
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function probablyCan(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether this authority can perform the given action on the given target page.
|
||||||
|
* This method performs a thorough check, but does not protect against race conditions.
|
||||||
|
* It is intended to be used when a user is intending to perform an action, but has not
|
||||||
|
* yet committed to it. For example, when a user goes to the edit page of an article, this
|
||||||
|
* method may be used to determine whether the user should be presented with a warning and
|
||||||
|
* a read-only view instead.
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status aggregator for failures
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function definitelyCan(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorize read access. This should be used immediately before performing read access on
|
||||||
|
* restricted information.
|
||||||
|
*
|
||||||
|
* Calling this method may have non-trivial side-effects, such as incrementing a rate limit
|
||||||
|
* counter.
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status aggregator for failures
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorizeRead(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorize write access. This should be used immediately before updating
|
||||||
|
* persisted information.
|
||||||
|
*
|
||||||
|
* Calling this method may have non-trivial side-effects, such as incrementing a rate limit
|
||||||
|
* counter.
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status aggregator for failures
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorizeWrite(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool;
|
||||||
|
|
||||||
|
}
|
||||||
43
includes/Permissions/PermissionStatus.php
Normal file
43
includes/Permissions/PermissionStatus.php
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace MediaWiki\Permissions;
|
||||||
|
|
||||||
|
use StatusValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A StatusValue for permission errors.
|
||||||
|
*
|
||||||
|
* @todo Add compat code for PermissionManager::getPermissionErrors
|
||||||
|
* and additional info about user blocks.
|
||||||
|
*
|
||||||
|
* @unstable
|
||||||
|
* @since 1.36
|
||||||
|
*/
|
||||||
|
class PermissionStatus extends StatusValue {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function newEmpty() {
|
||||||
|
return new static();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
194
includes/Permissions/SimpleAuthority.php
Normal file
194
includes/Permissions/SimpleAuthority.php
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace MediaWiki\Permissions;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use MediaWiki\Page\PageIdentity;
|
||||||
|
use MediaWiki\User\UserIdentity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an authority that has a specific set of permissions
|
||||||
|
* which are specified explicitly. This is useful for testing, but
|
||||||
|
* may also be used to represent a fixed set of permissions to be
|
||||||
|
* used in some context, e.g. in an asynchronous job.
|
||||||
|
*
|
||||||
|
* @unstable
|
||||||
|
* @since 1.36
|
||||||
|
*/
|
||||||
|
class SimpleAuthority implements Authority {
|
||||||
|
|
||||||
|
/** @var UserIdentity */
|
||||||
|
private $actor;
|
||||||
|
|
||||||
|
/** @var array permissions (stored in the keys, values are ignured) */
|
||||||
|
private $permissions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param UserIdentity $actor
|
||||||
|
* @param string[] $permissions A list of permissions to grant to the actor
|
||||||
|
*/
|
||||||
|
public function __construct( UserIdentity $actor, array $permissions ) {
|
||||||
|
$this->actor = $actor;
|
||||||
|
$this->permissions = array_flip( $permissions );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user identity associated with this authority.
|
||||||
|
*
|
||||||
|
* @return UserIdentity
|
||||||
|
*/
|
||||||
|
public function getActor(): UserIdentity {
|
||||||
|
return $this->actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string $permission
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isAllowed( string $permission ): bool {
|
||||||
|
return isset( $this->permissions[ $permission ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string ...$permissions
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isAllowedAny( ...$permissions ): bool {
|
||||||
|
if ( !$permissions ) {
|
||||||
|
throw new InvalidArgumentException( 'At least one permission must be specified' );
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ( $permissions as $perm ) {
|
||||||
|
if ( $this->isAllowed( $perm ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string ...$permissions
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isAllowedAll( ...$permissions ): bool {
|
||||||
|
if ( !$permissions ) {
|
||||||
|
throw new InvalidArgumentException( 'At least one permission must be specified' );
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ( $permissions as $perm ) {
|
||||||
|
if ( !$this->isAllowed( $perm ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkPermission( string $permission, ?PermissionStatus $status ): bool {
|
||||||
|
$ok = $this->isAllowed( $permission );
|
||||||
|
|
||||||
|
if ( !$ok && $status ) {
|
||||||
|
// TODO: use a message that at includes the permission name
|
||||||
|
$status->fatal( 'permissionserrors' );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function probablyCan(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool {
|
||||||
|
return $this->checkPermission( $action, $status );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function definitelyCan(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool {
|
||||||
|
return $this->checkPermission( $action, $status );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorizeRead(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool {
|
||||||
|
return $this->checkPermission( $action, $status );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorizeWrite(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool {
|
||||||
|
return $this->checkPermission( $action, $status );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
158
includes/Permissions/UltimateAuthority.php
Normal file
158
includes/Permissions/UltimateAuthority.php
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace MediaWiki\Permissions;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use MediaWiki\Page\PageIdentity;
|
||||||
|
use MediaWiki\User\UserIdentity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an authority that has all permissions.
|
||||||
|
* This is intended for use in maintenance scripts and tests.
|
||||||
|
*
|
||||||
|
* @unstable
|
||||||
|
* @since 1.36
|
||||||
|
*/
|
||||||
|
class UltimateAuthority implements Authority {
|
||||||
|
|
||||||
|
/** @var UserIdentity */
|
||||||
|
private $actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param UserIdentity $actor
|
||||||
|
*/
|
||||||
|
public function __construct( UserIdentity $actor ) {
|
||||||
|
$this->actor = $actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user identity associated with this authority.
|
||||||
|
*
|
||||||
|
* @return UserIdentity
|
||||||
|
*/
|
||||||
|
public function getActor(): UserIdentity {
|
||||||
|
return $this->actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isAllowed( string $permission ): bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isAllowedAny( ...$permissions ): bool {
|
||||||
|
if ( !$permissions ) {
|
||||||
|
throw new InvalidArgumentException( 'At least one permission must be specified' );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isAllowedAll( ...$permissions ): bool {
|
||||||
|
if ( !$permissions ) {
|
||||||
|
throw new InvalidArgumentException( 'At least one permission must be specified' );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function probablyCan(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function definitelyCan(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorizeRead(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorizeWrite(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
269
includes/Permissions/WebRequestAuthority.php
Normal file
269
includes/Permissions/WebRequestAuthority.php
Normal file
|
|
@ -0,0 +1,269 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace MediaWiki\Permissions;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use MediaWiki\Linker\LinkTarget;
|
||||||
|
use MediaWiki\Page\PageIdentity;
|
||||||
|
use MediaWiki\User\UserIdentity;
|
||||||
|
use TitleValue;
|
||||||
|
use WebRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the authority of a given web request. For anonymous visitors, this will typically
|
||||||
|
* allow only basic permissions. For logged in users, permissions are generally based on group
|
||||||
|
* membership, but may be adjusted based on things like IP range blocks, OAuth grants, or
|
||||||
|
* rate limits.
|
||||||
|
*
|
||||||
|
* @unstable
|
||||||
|
* @since 1.36
|
||||||
|
*/
|
||||||
|
class WebRequestAuthority implements Authority {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var PermissionManager
|
||||||
|
*/
|
||||||
|
private $permissionManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var UserIdentity
|
||||||
|
*/
|
||||||
|
private $actor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param WebRequest $request
|
||||||
|
* @param PermissionManager $permissionManager
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
WebRequest $request,
|
||||||
|
PermissionManager $permissionManager
|
||||||
|
) {
|
||||||
|
$this->permissionManager = $permissionManager;
|
||||||
|
|
||||||
|
$this->actor = $request->getSession()->getUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @return UserIdentity
|
||||||
|
*/
|
||||||
|
public function getActor(): UserIdentity {
|
||||||
|
return $this->actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string $permission
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isAllowed( string $permission ): bool {
|
||||||
|
return $this->permissionManager->userHasRight( $this->actor, $permission );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string ...$permissions
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isAllowedAny( ...$permissions ): bool {
|
||||||
|
if ( !$permissions ) {
|
||||||
|
throw new InvalidArgumentException( 'At least one permission must be specified' );
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ( $permissions as $perm ) {
|
||||||
|
if ( $this->isAllowed( $perm ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string ...$permissions
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isAllowedAll( ...$permissions ): bool {
|
||||||
|
if ( !$permissions ) {
|
||||||
|
throw new InvalidArgumentException( 'At least one permission must be specified' );
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ( $permissions as $perm ) {
|
||||||
|
if ( !$this->isAllowed( $perm ) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function probablyCan(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool {
|
||||||
|
return $this->internalCan(
|
||||||
|
PermissionManager::RIGOR_QUICK,
|
||||||
|
$action,
|
||||||
|
$target,
|
||||||
|
$status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function definitelyCan(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool {
|
||||||
|
// Note that we do not use RIGOR_SECURE to avoid hitting the master
|
||||||
|
// database for read operations. RIGOR_FULL performs the same checks,
|
||||||
|
// but is subject to replication lag.
|
||||||
|
return $this->internalCan(
|
||||||
|
PermissionManager::RIGOR_FULL,
|
||||||
|
$action,
|
||||||
|
$target,
|
||||||
|
$status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorizeRead(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool {
|
||||||
|
// Any side-effects can be added here.
|
||||||
|
|
||||||
|
// Note that we do not use RIGOR_SECURE to avoid hitting the master
|
||||||
|
// database for read operations. RIGOR_FULL performs the same checks,
|
||||||
|
// but is subject to replication lag.
|
||||||
|
return $this->internalCan(
|
||||||
|
PermissionManager::RIGOR_FULL,
|
||||||
|
$action,
|
||||||
|
$target,
|
||||||
|
$status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function authorizeWrite(
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool {
|
||||||
|
// Any side-effects can be added here.
|
||||||
|
|
||||||
|
// Note that we need to use RIGOR_SECURE here to ensure that we do not
|
||||||
|
// miss a user block or page protection due to replication lag.
|
||||||
|
return $this->internalCan(
|
||||||
|
PermissionManager::RIGOR_SECURE,
|
||||||
|
$action,
|
||||||
|
$target,
|
||||||
|
$status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $rigor
|
||||||
|
* @param string $action
|
||||||
|
* @param PageIdentity $target
|
||||||
|
* @param PermissionStatus|null $status
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function internalCan(
|
||||||
|
string $rigor,
|
||||||
|
string $action,
|
||||||
|
PageIdentity $target,
|
||||||
|
PermissionStatus $status = null
|
||||||
|
): bool {
|
||||||
|
if ( !( $target instanceof LinkTarget ) ) {
|
||||||
|
// FIXME: PermissionManager should accept PageIdentity!
|
||||||
|
$target = TitleValue::newFromPage( $target );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $status ) {
|
||||||
|
$errors = $this->permissionManager->getPermissionErrors(
|
||||||
|
$action,
|
||||||
|
$this->actor,
|
||||||
|
$target,
|
||||||
|
$rigor
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ( $errors as $err ) {
|
||||||
|
$status->fatal( ...$err );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $status->isOK();
|
||||||
|
} else {
|
||||||
|
// allow PermissionManager to short-circuit
|
||||||
|
return $this->permissionManager->userCan(
|
||||||
|
$action,
|
||||||
|
$this->actor,
|
||||||
|
$target,
|
||||||
|
$rigor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace MediaWiki\Tests\Unit\Permissions;
|
||||||
|
|
||||||
|
use MediaWiki\Permissions\PermissionStatus;
|
||||||
|
use MediaWikiUnitTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \MediaWiki\Permissions\PermissionStatus
|
||||||
|
*/
|
||||||
|
class PermissionStatusTest extends MediaWikiUnitTestCase {
|
||||||
|
|
||||||
|
public function testNewEmpty() {
|
||||||
|
$status = PermissionStatus::newEmpty();
|
||||||
|
|
||||||
|
$this->assertTrue( $status->isOK() );
|
||||||
|
$this->assertTrue( $status->isGood() );
|
||||||
|
$this->assertEmpty( $status->getErrors() );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
142
tests/phpunit/unit/includes/Permissions/SimpleAuthorityTest.php
Normal file
142
tests/phpunit/unit/includes/Permissions/SimpleAuthorityTest.php
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace MediaWiki\Tests\Unit\Permissions;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use MediaWiki\Page\PageIdentity;
|
||||||
|
use MediaWiki\Page\PageIdentityValue;
|
||||||
|
use MediaWiki\Permissions\PermissionStatus;
|
||||||
|
use MediaWiki\Permissions\SimpleAuthority;
|
||||||
|
use MediaWiki\User\UserIdentityValue;
|
||||||
|
use MediaWikiUnitTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \MediaWiki\Permissions\SimpleAuthority
|
||||||
|
*/
|
||||||
|
class SimpleAuthorityTest extends MediaWikiUnitTestCase {
|
||||||
|
|
||||||
|
public function testGetAuthor() {
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new SimpleAuthority( $actor, [] );
|
||||||
|
|
||||||
|
$this->assertSame( $actor, $authority->getActor() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPermissions() {
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new SimpleAuthority( $actor, [ 'foo', 'bar' ] );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->isAllowed( 'foo' ) );
|
||||||
|
$this->assertTrue( $authority->isAllowed( 'bar' ) );
|
||||||
|
$this->assertFalse( $authority->isAllowed( 'quux' ) );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->isAllowedAll( 'foo', 'bar' ) );
|
||||||
|
$this->assertTrue( $authority->isAllowedAny( 'bar', 'quux' ) );
|
||||||
|
|
||||||
|
$this->assertFalse( $authority->isAllowedAll( 'foo', 'quux' ) );
|
||||||
|
$this->assertFalse( $authority->isAllowedAny( 'xyzzy', 'quux' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testProbablyCan() {
|
||||||
|
$target = new PageIdentityValue( 321, NS_MAIN, __METHOD__, PageIdentity::LOCAL );
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new SimpleAuthority( $actor, [ 'foo', 'bar' ] );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->probablyCan( 'foo', $target ) );
|
||||||
|
$this->assertTrue( $authority->probablyCan( 'bar', $target ) );
|
||||||
|
$this->assertFalse( $authority->probablyCan( 'quux', $target ) );
|
||||||
|
|
||||||
|
$status = new PermissionStatus();
|
||||||
|
$authority->probablyCan( 'foo', $target, $status );
|
||||||
|
$this->assertTrue( $status->isOK() );
|
||||||
|
|
||||||
|
$authority->probablyCan( 'quux', $target, $status );
|
||||||
|
$this->assertFalse( $status->isOK() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitlyCan() {
|
||||||
|
$target = new PageIdentityValue( 321, NS_MAIN, __METHOD__, PageIdentity::LOCAL );
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new SimpleAuthority( $actor, [ 'foo', 'bar' ] );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->definitelyCan( 'foo', $target ) );
|
||||||
|
$this->assertTrue( $authority->definitelyCan( 'bar', $target ) );
|
||||||
|
$this->assertFalse( $authority->definitelyCan( 'quux', $target ) );
|
||||||
|
|
||||||
|
$status = new PermissionStatus();
|
||||||
|
$authority->definitelyCan( 'foo', $target, $status );
|
||||||
|
$this->assertTrue( $status->isOK() );
|
||||||
|
|
||||||
|
$authority->definitelyCan( 'quux', $target, $status );
|
||||||
|
$this->assertFalse( $status->isOK() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAuthorizeRead() {
|
||||||
|
$target = new PageIdentityValue( 321, NS_MAIN, __METHOD__, PageIdentity::LOCAL );
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new SimpleAuthority( $actor, [ 'foo', 'bar' ] );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->authorizeRead( 'foo', $target ) );
|
||||||
|
$this->assertTrue( $authority->authorizeRead( 'bar', $target ) );
|
||||||
|
$this->assertFalse( $authority->authorizeRead( 'quux', $target ) );
|
||||||
|
|
||||||
|
$status = new PermissionStatus();
|
||||||
|
$authority->authorizeRead( 'foo', $target, $status );
|
||||||
|
$this->assertTrue( $status->isOK() );
|
||||||
|
|
||||||
|
$authority->authorizeRead( 'quux', $target, $status );
|
||||||
|
$this->assertFalse( $status->isOK() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAuthorizeWrite() {
|
||||||
|
$target = new PageIdentityValue( 321, NS_MAIN, __METHOD__, PageIdentity::LOCAL );
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new SimpleAuthority( $actor, [ 'foo', 'bar' ] );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->authorizeWrite( 'foo', $target ) );
|
||||||
|
$this->assertTrue( $authority->authorizeWrite( 'bar', $target ) );
|
||||||
|
$this->assertFalse( $authority->authorizeWrite( 'quux', $target ) );
|
||||||
|
|
||||||
|
$status = new PermissionStatus();
|
||||||
|
$authority->authorizeWrite( 'foo', $target, $status );
|
||||||
|
$this->assertTrue( $status->isOK() );
|
||||||
|
|
||||||
|
$authority->authorizeWrite( 'quux', $target, $status );
|
||||||
|
$this->assertFalse( $status->isOK() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsAllowedAnyThrowsOnEmptySet() {
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new SimpleAuthority( $actor, [ 'foo', 'bar' ] );
|
||||||
|
|
||||||
|
$this->expectException( InvalidArgumentException::class );
|
||||||
|
$authority->isAllowedAny();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsAllowedAllThrowsOnEmptySet() {
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new SimpleAuthority( $actor, [ 'foo', 'bar' ] );
|
||||||
|
|
||||||
|
$this->expectException( InvalidArgumentException::class );
|
||||||
|
$authority->isAllowedAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace MediaWiki\Tests\Unit\Permissions;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use MediaWiki\Page\PageIdentity;
|
||||||
|
use MediaWiki\Page\PageIdentityValue;
|
||||||
|
use MediaWiki\Permissions\PermissionStatus;
|
||||||
|
use MediaWiki\Permissions\UltimateAuthority;
|
||||||
|
use MediaWiki\User\UserIdentityValue;
|
||||||
|
use MediaWikiUnitTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \MediaWiki\Permissions\UltimateAuthority
|
||||||
|
*/
|
||||||
|
class UltimateAuthorityTest extends MediaWikiUnitTestCase {
|
||||||
|
|
||||||
|
public function testGetAuthor() {
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new UltimateAuthority( $actor );
|
||||||
|
|
||||||
|
$this->assertSame( $actor, $authority->getActor() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPermissions() {
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new UltimateAuthority( $actor );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->isAllowed( 'foo' ) );
|
||||||
|
$this->assertTrue( $authority->isAllowed( 'bar' ) );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->isAllowedAll( 'foo', 'bar' ) );
|
||||||
|
$this->assertTrue( $authority->isAllowedAny( 'bar', 'quux' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testProbablyCan() {
|
||||||
|
$target = new PageIdentityValue( 321, NS_MAIN, __METHOD__, PageIdentity::LOCAL );
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new UltimateAuthority( $actor );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->probablyCan( 'foo', $target ) );
|
||||||
|
$this->assertTrue( $authority->probablyCan( 'bar', $target ) );
|
||||||
|
|
||||||
|
$status = new PermissionStatus();
|
||||||
|
$authority->probablyCan( 'foo', $target, $status );
|
||||||
|
$this->assertTrue( $status->isOK() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitlyCan() {
|
||||||
|
$target = new PageIdentityValue( 321, NS_MAIN, __METHOD__, PageIdentity::LOCAL );
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new UltimateAuthority( $actor );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->definitelyCan( 'foo', $target ) );
|
||||||
|
$this->assertTrue( $authority->definitelyCan( 'bar', $target ) );
|
||||||
|
|
||||||
|
$status = new PermissionStatus();
|
||||||
|
$authority->definitelyCan( 'foo', $target, $status );
|
||||||
|
$this->assertTrue( $status->isOK() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAuthorizeRead() {
|
||||||
|
$target = new PageIdentityValue( 321, NS_MAIN, __METHOD__, PageIdentity::LOCAL );
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new UltimateAuthority( $actor );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->authorizeRead( 'foo', $target ) );
|
||||||
|
$this->assertTrue( $authority->authorizeRead( 'bar', $target ) );
|
||||||
|
|
||||||
|
$status = new PermissionStatus();
|
||||||
|
$authority->authorizeRead( 'foo', $target, $status );
|
||||||
|
$this->assertTrue( $status->isOK() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAuthorizeWrite() {
|
||||||
|
$target = new PageIdentityValue( 321, NS_MAIN, __METHOD__, PageIdentity::LOCAL );
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new UltimateAuthority( $actor );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->authorizeWrite( 'foo', $target ) );
|
||||||
|
$this->assertTrue( $authority->authorizeWrite( 'bar', $target ) );
|
||||||
|
|
||||||
|
$status = new PermissionStatus();
|
||||||
|
$authority->authorizeWrite( 'foo', $target, $status );
|
||||||
|
$this->assertTrue( $status->isOK() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsAllowedAnyThrowsOnEmptySet() {
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new UltimateAuthority( $actor );
|
||||||
|
|
||||||
|
$this->expectException( InvalidArgumentException::class );
|
||||||
|
$authority->isAllowedAny();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsAllowedAllThrowsOnEmptySet() {
|
||||||
|
$actor = new UserIdentityValue( 12, 'Test', 17 );
|
||||||
|
$authority = new UltimateAuthority( $actor );
|
||||||
|
|
||||||
|
$this->expectException( InvalidArgumentException::class );
|
||||||
|
$authority->isAllowedAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
* http://www.gnu.org/copyleft/gpl.html
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace MediaWiki\Tests\Unit\Permissions;
|
||||||
|
|
||||||
|
use FauxRequest;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use MediaWiki\Page\PageIdentity;
|
||||||
|
use MediaWiki\Page\PageIdentityValue;
|
||||||
|
use MediaWiki\Permissions\Authority;
|
||||||
|
use MediaWiki\Permissions\PermissionManager;
|
||||||
|
use MediaWiki\Permissions\PermissionStatus;
|
||||||
|
use MediaWiki\Permissions\WebRequestAuthority;
|
||||||
|
use MediaWiki\Session\Session;
|
||||||
|
use MediaWiki\User\UserIdentity;
|
||||||
|
use MediaWikiUnitTestCase;
|
||||||
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
use User;
|
||||||
|
use WebRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \MediaWiki\Permissions\WebRequestAuthority
|
||||||
|
*/
|
||||||
|
class WebRequestAuthorityTest extends MediaWikiUnitTestCase {
|
||||||
|
|
||||||
|
private function newPermissionsManager( array $permissions ): PermissionManager {
|
||||||
|
/** @var PermissionManager|MockObject $manager */
|
||||||
|
$manager = $this->createNoOpMock(
|
||||||
|
PermissionManager::class,
|
||||||
|
[ 'userHasRight', 'userCan', 'getPermissionErrors' ]
|
||||||
|
);
|
||||||
|
|
||||||
|
$manager->method( 'userHasRight' )->willReturnCallback(
|
||||||
|
function ( $user, $permission ) use ( $permissions ) {
|
||||||
|
return in_array( $permission, $permissions );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$manager->method( 'userCan' )->willReturnCallback(
|
||||||
|
function ( $permission, $user ) use ( $manager ) {
|
||||||
|
return $manager->userHasRight( $user, $permission );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$manager->method( 'getPermissionErrors' )->willReturnCallback(
|
||||||
|
function ( $permission, $user, $target ) use ( $manager ) {
|
||||||
|
$errors = [];
|
||||||
|
if ( !$manager->userCan( $permission, $user, $target ) ) {
|
||||||
|
$errors[] = [ 'permissionserrors' ];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $errors;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newUser(): User {
|
||||||
|
return $this->createNoOpMock( User::class );
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newAuthority( UserIdentity $actor, array $permissions ): Authority {
|
||||||
|
$session = $this->createNoOpMock( Session::class, [ 'getUser' ] );
|
||||||
|
$session->method( 'getUser' )->willReturn( $actor );
|
||||||
|
|
||||||
|
/** @var WebRequest|MockObject $request */
|
||||||
|
$request = $this->createNoOpMock( FauxRequest::class, [ 'getSession' ] );
|
||||||
|
$request->method( 'getSession' )->willReturn( $session );
|
||||||
|
|
||||||
|
$permissionManager = $this->newPermissionsManager( $permissions );
|
||||||
|
|
||||||
|
return new WebRequestAuthority(
|
||||||
|
$request,
|
||||||
|
$permissionManager
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetAuthor() {
|
||||||
|
$actor = $this->newUser();
|
||||||
|
$authority = $this->newAuthority( $actor, [] );
|
||||||
|
|
||||||
|
$this->assertSame( $actor, $authority->getActor() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPermissions() {
|
||||||
|
$actor = $this->newUser();
|
||||||
|
$authority = $this->newAuthority( $actor, [ 'foo', 'bar' ] );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->isAllowed( 'foo' ) );
|
||||||
|
$this->assertTrue( $authority->isAllowed( 'bar' ) );
|
||||||
|
$this->assertFalse( $authority->isAllowed( 'quux' ) );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->isAllowedAll( 'foo', 'bar' ) );
|
||||||
|
$this->assertTrue( $authority->isAllowedAny( 'bar', 'quux' ) );
|
||||||
|
|
||||||
|
$this->assertFalse( $authority->isAllowedAll( 'foo', 'quux' ) );
|
||||||
|
$this->assertFalse( $authority->isAllowedAny( 'xyzzy', 'quux' ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testProbablyCan() {
|
||||||
|
$target = new PageIdentityValue( 321, NS_MAIN, __METHOD__, PageIdentity::LOCAL );
|
||||||
|
$actor = $this->newUser();
|
||||||
|
$authority = $this->newAuthority( $actor, [ 'foo', 'bar' ] );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->probablyCan( 'foo', $target ) );
|
||||||
|
$this->assertTrue( $authority->probablyCan( 'bar', $target ) );
|
||||||
|
$this->assertFalse( $authority->probablyCan( 'quux', $target ) );
|
||||||
|
|
||||||
|
$status = new PermissionStatus();
|
||||||
|
$authority->probablyCan( 'foo', $target, $status );
|
||||||
|
$this->assertTrue( $status->isOK() );
|
||||||
|
|
||||||
|
$authority->probablyCan( 'quux', $target, $status );
|
||||||
|
$this->assertFalse( $status->isOK() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefinitlyCan() {
|
||||||
|
$target = new PageIdentityValue( 321, NS_MAIN, __METHOD__, PageIdentity::LOCAL );
|
||||||
|
$actor = $this->newUser();
|
||||||
|
$authority = $this->newAuthority( $actor, [ 'foo', 'bar' ] );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->definitelyCan( 'foo', $target ) );
|
||||||
|
$this->assertTrue( $authority->definitelyCan( 'bar', $target ) );
|
||||||
|
$this->assertFalse( $authority->definitelyCan( 'quux', $target ) );
|
||||||
|
|
||||||
|
$status = new PermissionStatus();
|
||||||
|
$authority->definitelyCan( 'foo', $target, $status );
|
||||||
|
$this->assertTrue( $status->isOK() );
|
||||||
|
|
||||||
|
$authority->definitelyCan( 'quux', $target, $status );
|
||||||
|
$this->assertFalse( $status->isOK() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAuthorizeRead() {
|
||||||
|
$target = new PageIdentityValue( 321, NS_MAIN, __METHOD__, PageIdentity::LOCAL );
|
||||||
|
$actor = $this->newUser();
|
||||||
|
$authority = $this->newAuthority( $actor, [ 'foo', 'bar' ] );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->authorizeRead( 'foo', $target ) );
|
||||||
|
$this->assertTrue( $authority->authorizeRead( 'bar', $target ) );
|
||||||
|
$this->assertFalse( $authority->authorizeRead( 'quux', $target ) );
|
||||||
|
|
||||||
|
$status = new PermissionStatus();
|
||||||
|
$authority->authorizeRead( 'foo', $target, $status );
|
||||||
|
$this->assertTrue( $status->isOK() );
|
||||||
|
|
||||||
|
$authority->authorizeRead( 'quux', $target, $status );
|
||||||
|
$this->assertFalse( $status->isOK() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAuthorizeWrite() {
|
||||||
|
$target = new PageIdentityValue( 321, NS_MAIN, __METHOD__, PageIdentity::LOCAL );
|
||||||
|
$actor = $this->newUser();
|
||||||
|
$authority = $this->newAuthority( $actor, [ 'foo', 'bar' ] );
|
||||||
|
|
||||||
|
$this->assertTrue( $authority->authorizeWrite( 'foo', $target ) );
|
||||||
|
$this->assertTrue( $authority->authorizeWrite( 'bar', $target ) );
|
||||||
|
$this->assertFalse( $authority->authorizeWrite( 'quux', $target ) );
|
||||||
|
|
||||||
|
$status = new PermissionStatus();
|
||||||
|
$authority->authorizeWrite( 'foo', $target, $status );
|
||||||
|
$this->assertTrue( $status->isOK() );
|
||||||
|
|
||||||
|
$authority->authorizeWrite( 'quux', $target, $status );
|
||||||
|
$this->assertFalse( $status->isOK() );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsAllowedAnyThrowsOnEmptySet() {
|
||||||
|
$actor = $this->newUser();
|
||||||
|
$authority = $this->newAuthority( $actor, [ 'foo', 'bar' ] );
|
||||||
|
|
||||||
|
$this->expectException( InvalidArgumentException::class );
|
||||||
|
$authority->isAllowedAny();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsAllowedAllThrowsOnEmptySet() {
|
||||||
|
$actor = $this->newUser();
|
||||||
|
$authority = $this->newAuthority( $actor, [ 'foo', 'bar' ] );
|
||||||
|
|
||||||
|
$this->expectException( InvalidArgumentException::class );
|
||||||
|
$authority->isAllowedAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue