User Extender (prepareGroups functionality) (#2110)

This commit is contained in:
Alexander Skvortsov 2020-07-17 06:18:35 -04:00 committed by GitHub
parent b89ccaf83c
commit c10ebea00c
5 changed files with 93 additions and 2 deletions

View File

@ -12,6 +12,7 @@ namespace Flarum\Event;
use Flarum\User\User;
/**
* @deprecated beta 13, remove in beta 14. Use the User extender instead.
* The `PrepareUserGroups` event.
*/
class PrepareUserGroups

View File

@ -15,9 +15,10 @@ use Illuminate\Contracts\Container\Container;
class User implements ExtenderInterface
{
private $displayNameDrivers = [];
private $groupProcessors = [];
/**
* Add a mail driver.
* Add a display name driver.
*
* @param string $identifier Identifier for display name driver. E.g. 'username' for UserNameDriver
* @param string $driver ::class attribute of driver class, which must implement Flarum\User\DisplayName\DriverInterface
@ -29,10 +30,35 @@ class User implements ExtenderInterface
return $this;
}
/**
* Dynamically process a user's list of groups when calculating permissions.
* This can be used to give a user permissions for groups they aren't actually in, based on context.
* It will not change the group badges displayed for the user.
*
* @param callable $callable
*
* The callable can be a closure or invokable class, and should accept:
* - \Flarum\User\User $user: the user in question.
* - array $groupIds: an array of ids for the groups the user belongs to.
*
* The callable should return:
* - array $groupIds: an array of ids for the groups the user belongs to.
*/
public function permissionGroups(callable $callable)
{
$this->groupProcessors[] = $callable;
return $this;
}
public function extend(Container $container, Extension $extension = null)
{
$container->extend('flarum.user.display_name.supported_drivers', function ($existingDrivers) {
return array_merge($existingDrivers, $this->displayNameDrivers);
});
$container->extend('flarum.user.group_processors', function ($existingRelations) {
return array_merge($existingRelations, $this->groupProcessors);
});
}
}

View File

@ -83,6 +83,12 @@ class User extends AbstractModel
*/
protected $session;
/**
* An array of callables, through each of which the user's list of groups is passed
* before being returned.
*/
protected static $groupProcessors = [];
/**
* An array of registered user preferences. Each preference is defined with
* a key, and its value is an array containing the following keys:.
@ -660,8 +666,13 @@ class User extends AbstractModel
$groupIds = array_merge($groupIds, [Group::MEMBER_ID], $this->groups->pluck('id')->all());
}
// Deprecated, remove in beta 14.
event(new PrepareUserGroups($this, $groupIds));
foreach (static::$groupProcessors as $processor) {
$groupIds = $processor($this, $groupIds);
}
return Permission::whereIn('group_id', $groupIds);
}
@ -751,6 +762,17 @@ class User extends AbstractModel
static::$preferences[$key] = compact('transformer', 'default');
}
/**
* Register a callback that processes a user's list of groups.
*
* @param callable $callback
* @return array $groupIds
*/
public static function addGroupProcessor($callback)
{
static::$groupProcessors[] = $callback;
}
/**
* Get the key for a preference which flags whether or not the user will
* receive a notification for $type via $method.

View File

@ -31,6 +31,10 @@ class UserServiceProvider extends AbstractServiceProvider
{
$this->registerAvatarsFilesystem();
$this->registerDisplayNameDrivers();
$this->app->singleton('flarum.user.group_processors', function () {
return [];
});
}
protected function registerDisplayNameDrivers()
@ -72,6 +76,14 @@ class UserServiceProvider extends AbstractServiceProvider
*/
public function boot()
{
foreach ($this->app->make('flarum.user.group_processors') as $callback) {
if (is_string($callback)) {
$callback = $this->app->make($callback);
}
User::addGroupProcessor($callback);
}
User::setHasher($this->app->make('hash'));
User::setGate($this->app->make(Gate::class));
User::setDisplayNameDriver($this->app->make('flarum.user.display_name.driver'));

View File

@ -24,7 +24,9 @@ class UserTest extends TestCase
$this->prepareDatabase([
'users' => [
$this->adminUser(),
], 'settings' => [
$this->normalUser(),
],
'settings' => [
['key' => 'display_name_driver', 'value' => 'custom'],
],
]);
@ -58,6 +60,34 @@ class UserTest extends TestCase
$this->assertEquals('admin@machine.local$$$suffix', $user->displayName);
}
/**
* @test
*/
public function user_has_permissions_for_expected_groups_if_no_processors_added()
{
$this->prepDb();
$user = User::find(2);
$this->assertContains('viewUserList', $user->getPermissions());
}
/**
* @test
*/
public function processor_can_restrict_user_groups()
{
$this->extend((new Extend\User)->permissionGroups(function (User $user, array $groupIds) {
return array_filter($groupIds, function ($id) {
return $id != 3;
});
}));
$this->prepDb();
$user = User::find(2);
$this->assertNotContains('viewUserList', $user->getPermissions());
}
}
class CustomDisplayNameDriver implements DriverInterface