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

242 lines
6.7 KiB
PHP

<?php
namespace BlueSpice\PermissionManager;
use BlueSpice\PermissionManager\Logging\GroupManagerSpecialLogLogger;
use Exception;
use InvalidArgumentException;
use MediaWiki\Config\Config;
use MediaWiki\HookContainer\HookContainer;
use MediaWiki\Message\Message;
use MediaWiki\Permissions\Authority;
use MediaWiki\User\User;
use MWStake\MediaWiki\Component\DynamicConfig\DynamicConfigManager;
use Psr\Log\LoggerInterface;
use Wikimedia\Rdbms\ILoadBalancer;
class GroupManager {
/** @var DynamicConfigManager */
private DynamicConfigManager $configManager;
/** @var ILoadBalancer */
private ILoadBalancer $lb;
/** @var Config */
private Config $config;
/** @var HookContainer */
private HookContainer $hookContainer;
/** @var LoggerInterface */
private LoggerInterface $logger;
/** @var GroupManagerSpecialLogLogger */
private GroupManagerSpecialLogLogger $spLogger;
/**
* @param DynamicConfigManager $configManager
* @param ILoadBalancer $lb
* @param Config $config Main config (wg)
* @param HookContainer $hookContainer
* @param LoggerInterface $logger
* @param GroupManagerSpecialLogLogger $spLogger
*/
public function __construct(
DynamicConfigManager $configManager, ILoadBalancer $lb, Config $config,
HookContainer $hookContainer, LoggerInterface $logger, GroupManagerSpecialLogLogger $spLogger
) {
$this->configManager = $configManager;
$this->lb = $lb;
$this->config = $config;
$this->hookContainer = $hookContainer;
$this->logger = $logger;
$this->spLogger = $spLogger;
}
/**
* @param string $name
* @param Authority $actor
* @param bool $ignoreExists
* @return void
* @throws Exception
*/
public function addGroup( string $name, Authority $actor, bool $ignoreExists = false ) {
$this->assertActorCan( 'add', $actor );
$this->assertValidName( $name, $ignoreExists );
$current = $this->config->get( 'AdditionalGroups' ) ?? [];
$current[$name] = true;
$this->store( $current );
$this->hookContainer->run( 'BSPermissionManagerGroupAdded', [ $name, $actor ] );
$this->log( 'create', $actor, [ 'group' => $name ] );
}
/**
* @param string $oldName
* @param string $newName
* @param Authority $actor
* @return void
* @throws Exception
*/
public function editGroup( string $oldName, string $newName, Authority $actor ) {
$this->assertActorCan( 'edit', $actor );
$this->assertValidName( $newName );
$this->assertGroupExists( $oldName );
$current = $this->config->get( 'AdditionalGroups' ) ?? [];
unset( $current[$oldName] );
$current[$newName] = true;
$this->renameGroup( $oldName, $newName );
$this->store( $current );
$this->hookContainer->run( 'BSPermissionManagerGroupEdited', [ $oldName, $newName, $actor ] );
$this->log( 'modify', $actor, [ 'group' => $oldName, 'newGroup' => $newName ] );
}
/**
* @param string $name
* @param Authority $actor
* @return void
* @throws Exception
*/
public function removeGroup( string $name, Authority $actor ) {
$this->assertActorCan( 'delete', $actor );
$this->assertGroupExists( $name );
$current = $this->config->get( 'AdditionalGroups' ) ?? [];
unset( $current[$name] );
$this->store( $current );
$this->unassignUsers( $name );
$this->hookContainer->run( 'BSPermissionManagerGroupDeleted', [ $name, $actor ] );
$this->log( 'remove', $actor, [ 'group' => $name ] );
}
/**
* @param string $name
* @param bool $ignoreExists
* @return void
*/
private function assertValidName( string $name, bool $ignoreExists = false ) {
$invalidChars = [];
$name = trim( $name );
if ( substr_count( $name, '\'' ) > 0 ) {
$invalidChars[] = '\'';
}
if ( substr_count( $name, '"' ) > 0 ) {
$invalidChars[] = '"';
}
if ( !empty( $invalidChars ) ) {
throw new InvalidArgumentException(
Message::newFromKey( 'bs-permissionmanager-groupmanager-invalid-name' )
->numParams( count( $invalidChars ) )
->params( implode( ',', $invalidChars ) )
->text()
);
} elseif ( preg_match( "/^[0-9]+$/", $name ) ) {
throw new InvalidArgumentException(
Message::newFromKey( 'bs-permissionmanager-groupmanager-invalid-name-numeric' )->text()
);
} elseif ( strlen( $name ) > 255 ) {
throw new InvalidArgumentException(
Message::newFromKey( 'bs-permissionmanager-groupmanager-invalid-name-length' )->text()
);
}
if ( !$ignoreExists && $this->checkGroupExists( $name ) ) {
throw new InvalidArgumentException(
Message::newFromKey( 'bs-permissionmanager-group-already-exists' )->text()
);
}
}
/**
* @param array $value
* @return void
* @throws Exception
*/
private function store( array $value ) {
$config = $this->configManager->getConfigObject( 'bs-groupmanager-groups' );
if ( !$config ) {
throw new Exception( 'Config object not found' );
}
$this->configManager->storeConfig( $config, $value );
}
/**
* @param string $oldName
* @param string $newName
* @return void
* @throws Exception
*/
private function renameGroup( string $oldName, string $newName ) {
$db = $this->lb->getConnection( DB_PRIMARY );
$res = $db->update(
'user_groups',
[ 'ug_group' => $newName ],
[ 'ug_group' => $oldName ],
__METHOD__
);
if ( !$res ) {
throw new Exception( $db->lastError() );
}
}
/**
* @param string $name
* @return void
*/
private function assertGroupExists( string $name ) {
if ( !$this->checkGroupExists( $name ) ) {
throw new InvalidArgumentException(
Message::newFromKey( 'bs-permissionmanager-group-not-found' )->text()
);
}
}
/**
* @param string $action
* @param Authority $actor
* @return void
*/
private function assertActorCan( string $action, Authority $actor ) {
if ( $actor instanceof User && $actor->isSystemUser() ) {
return;
}
if ( !$actor->isAllowed( 'wikiadmin' ) ) {
throw new InvalidArgumentException(
Message::newFromKey( 'bs-permissionmanager-action-not-allowed' )->text()
);
}
}
/**
* @param string $name
* @return bool
*/
private function checkGroupExists( string $name ): bool {
$groupPermissions = $this->config->get( 'GroupPermissions' ) ?? [];
$existingGroups = array_keys( $groupPermissions );
return in_array( $name, $existingGroups );
}
/**
* @param string $type
* @param Authority $actor
* @param array $params
* @return void
*/
private function log( string $type, Authority $actor, array $params ) {
// Special:Log logging
$this->spLogger->log( $type, $actor, $params );
// Structured logging
$this->logger->info( 'New group created', array_merge( [
'actor' => $actor->getUser()->getName(),
], $params ) );
}
/**
* @param string $name
* @return void
*/
private function unassignUsers( string $name ) {
$db = $this->lb->getConnection( DB_PRIMARY );
$db->delete(
'user_groups',
[ 'ug_group' => $name ],
__METHOD__
);
}
}