gplx/mediawiki/extensions/BlueSpicePermissionManager/src/PermissionManager.php

376 lines
9.2 KiB
PHP

<?php
namespace BlueSpice\PermissionManager;
use BlueSpice\ExtensionAttributeBasedRegistry;
use BlueSpice\Permission\IRole;
use BlueSpice\Permission\RoleManager;
use InvalidArgumentException;
use ManualLogEntry;
use MediaWiki\Config\Config;
use MediaWiki\Context\RequestContext;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\MediaWikiServices;
use MediaWiki\Message\Message;
use MediaWiki\SpecialPage\SpecialPage;
use MWStake\MediaWiki\Component\DynamicConfig\DynamicConfigManager;
use Psr\Log\LoggerInterface;
use Throwable;
use UnexpectedValueException;
class PermissionManager {
/** @var RoleManager */
private $roleManager;
/** @var Config */
private $config;
/** @var LoggerInterface */
private $logger;
/** @var HookContainer */
private $hookContainer;
/** @var MediaWikiServices */
private $services;
/** @var array */
protected $groups = [];
/** @var ExtensionAttributeBasedRegistry */
protected $presets;
/** @var DynamicConfigManager */
private DynamicConfigManager $dynamicConfigManager;
/**
* @param RoleManager $roleManager
* @param MediaWikiServices $services
* @param Config $config
* @param LoggerInterface $logger
* @param HookContainer $hookContainer
* @param DynamicConfigManager $dynamicConfigManager
*/
public function __construct(
RoleManager $roleManager,
MediaWikiServices $services,
Config $config,
LoggerInterface $logger,
HookContainer $hookContainer,
DynamicConfigManager $dynamicConfigManager
) {
$this->roleManager = $roleManager;
$this->config = $config;
$this->logger = $logger;
$this->hookContainer = $hookContainer;
$this->services = $services;
$this->dynamicConfigManager = $dynamicConfigManager;
$this->presets = new ExtensionAttributeBasedRegistry(
'BlueSpicePermissionManagerPermissionPresets'
);
}
/**
* Enable role system
*/
public function enableRoleSystem() {
$this->roleManager->enableRoleSystem();
}
/**
* Apply permission preset
*/
public function applyCurrentPreset() {
try {
$preset = $this->getActivePreset();
$preset->apply();
$this->services->getHookContainer()->run( 'BSPermissionManagerAfterApplyPreset', [
$preset, $this->roleManager
] );
} catch ( Throwable $exception ) {
$this->logger->critical(
'Exception while applying preset: {error}',
[
'error' => $exception->getMessage()
]
);
}
}
/**
* @return IPreset
*/
public function getActivePreset() {
$activePreset = $this->config->get( 'PermissionManagerActivePreset' );
$preset = $this->getPreset( $activePreset );
if ( $preset === null ) {
$this->logger->critical(
'Attempted to apply unrecognized preset: {preset}',
[
'preset' => $activePreset
]
);
throw new UnexpectedValueException(
"Permission preset \"$activePreset\" is not registered or not allowed"
);
}
return $preset;
}
/**
* Get names of available presets
*
* @return array
*/
public function getAvailablePresets() {
return array_intersect(
$this->presets->getAllKeys(),
$this->config->get( 'PermissionManagerAllowedPresets' )
);
}
/**
* Get preset instance
*
* @param string $name
* @return IPreset|null
*/
public function getPreset( $name ) {
if ( !in_array( $name, $this->config->get( 'PermissionManagerAllowedPresets' ) ) ) {
return null;
}
if ( !$this->presets->getValue( $name, null ) ) {
return null;
}
$callable = $this->presets->getValue( $name );
$preset = call_user_func( $callable );
if ( !$preset instanceof IPreset ) {
$this->logger->critical(
'Preset expected to be an instance of {preset_class}, {actual} given',
[
'preset_class' => IPreset::class,
'actual' => get_class( $preset )
]
);
return null;
}
return $preset;
}
/**
* Get all permissions assigned to given role
*
* @param IRole|string $role
* @param bool $includeDesc
* @return array
*/
public function getRolePermissions( $role, $includeDesc = false ) {
if ( $role instanceof IRole ) {
$role = $role->getName();
}
$role = $this->roleManager->getRole( $role );
if ( $role instanceof IRole === false ) {
return [];
}
$permissions = $role->getPermissions();
if ( !$includeDesc ) {
return $permissions;
}
$permissionsAndDescs = [];
foreach ( $permissions as $permission ) {
$permissionsAndDescs[ $permission ] =
wfMessage( "right-$permission" )->text();
}
return $permissionsAndDescs;
}
/**
* @return RoleManager
*/
public function getRoleManager() {
return $this->roleManager;
}
/**
*
* @param array $data
* @return array
*/
public function saveRoles( $data ) {
if ( !isset( $data ) || !isset( $data['groupRoles'] ) || !isset( $data['roleLockdown'] ) ) {
throw new InvalidArgumentException();
}
$groupRoles = $data['groupRoles'];
$roleLockdown = $data['roleLockdown'];
$status = $this->hookContainer->run(
'BsPermissionManager::beforeSaveRoles', [ &$groupRoles, &$roleLockdown ]
);
if ( !$status ) {
throw new \RuntimeException( 'Hook aborted' );
}
return $this->persistRoles( $groupRoles, $roleLockdown );
}
/**
*
* @return array
*/
public function getNamespaceRolesLockdown() {
return $this->config->get( 'NamespaceRolesLockdown' );
}
/**
*
* @return array
*/
public function getRoleDependencyTree() {
$roles = $this->roleManager->getRoleNames();
$tree = [];
foreach ( $roles as $roleName ) {
$role = $this->roleManager->getRole( $roleName );
if ( !$role instanceof \BlueSpice\Permission\IRole ) {
continue;
}
if ( empty( $role->getRequiredPermissions() ) ) {
continue;
}
$requiredPermissions = $role->getRequiredPermissions();
$neededRoles = [];
foreach ( $requiredPermissions as $permission ) {
$rolesWithPermission = $this->roleManager->getRolesWithPermission( $permission );
if ( in_array( $roleName, $rolesWithPermission ) ) {
continue;
}
$neededRoles[$permission] = $rolesWithPermission;
}
$tree[$roleName] = $neededRoles;
}
return $tree;
}
/**
*
* @param array $groupRoles
* @param array $roleLockdown
* @return array
*/
private function persistRoles( $groupRoles, $roleLockdown ) {
if ( $this->services->getReadOnlyMode()->isReadOnly() ) {
return [
'success' => false,
'message' => wfMessage( 'bs-readonly', $this->services->getReadOnlyMode()->getReason() )->text()
];
}
$roleMatrixDiff = new RoleMatrixDiff( $this->config, $groupRoles, $roleLockdown );
$globalDiff = $roleMatrixDiff->getGlobalDiff();
$nsDiff = $roleMatrixDiff->getNsDiff();
$status = $this->dynamicConfigManager->storeConfig(
$this->dynamicConfigManager->getConfigObject( 'bs-permissionmanager-roles' ),
[ 'groupRoles' => $groupRoles, 'roleLockdown' => $roleLockdown ]
);
if ( $status ) {
$this->doLog( $globalDiff, $nsDiff );
return [ 'success' => true ];
} else {
return [
'success' => false,
'message' => Message::newFromKey(
'bs-permissionmanager-write-config-file-error',
'bs-permissionmanager-roles'
)->text()
];
}
}
/**
*
* @param array $globalDiff
* @param array $nsDiff
*/
private function doLog( $globalDiff, $nsDiff ) {
foreach ( $globalDiff as $group => $roles ) {
$addedRoles = [];
$removedRoles = [];
foreach ( $roles as $role => $added ) {
if ( $added ) {
$addedRoles[] = $role;
} else {
$removedRoles[] = $role;
}
}
if ( !empty( $addedRoles ) ) {
$this->insertLog( 'global-add', [
'4::diffGroup' => $group,
'5::diffRoles' => implode( ',', $addedRoles ),
'6::roleCount' => count( $addedRoles )
] );
}
if ( !empty( $removedRoles ) ) {
$this->insertLog( 'global-remove', [
'4::diffGroup' => $group,
'5::diffRoles' => implode( ',', $removedRoles ),
'6::roleCount' => count( $removedRoles )
] );
}
}
foreach ( $nsDiff as $group => $namespaces ) {
foreach ( $namespaces as $ns => $roles ) {
$namespaceInfo = $this->services->getNamespaceInfo();
$nsCanonical = $namespaceInfo->getCanonicalName( $ns );
if ( $ns === NS_MAIN ) {
$nsCanonical = wfMessage( 'bs-ns_main' )->text();
}
$addedRoles = [];
$removedRoles = [];
foreach ( $roles as $role => $added ) {
if ( $added ) {
$addedRoles[] = $role;
} else {
$removedRoles[] = $role;
}
}
if ( !empty( $addedRoles ) ) {
$this->insertLog( 'ns-add', [
'4::diffGroup' => $group,
'5::diffRoles' => implode( ',', $addedRoles ),
'6::roleCount' => count( $addedRoles ),
'7::ns' => $nsCanonical
] );
}
if ( !empty( $removedRoles ) ) {
$this->insertLog( 'ns-remove', [
'4::diffGroup' => $group,
'5::diffRoles' => implode( ',', $removedRoles ),
'6::roleCount' => count( $removedRoles ),
'7::ns' => $nsCanonical
] );
}
}
}
}
/**
*
* @param string $type
* @param array $params
*/
private function insertLog( $type, $params ) {
$targetTitle = SpecialPage::getTitleFor( 'PermissionManager' );
$user = RequestContext::getMain()->getUser();
$logger = new ManualLogEntry( 'bs-permission-manager', $type );
$logger->setPerformer( $user );
$logger->setTarget( $targetTitle );
$logger->setParameters( $params );
$logger->insert( $this->services->getDBLoadBalancer()->getConnection( DB_PRIMARY ) );
}
}