Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User Extender (prepareGroups functionality) #2110

Merged
merged 8 commits into from Jul 17, 2020
1 change: 1 addition & 0 deletions src/Event/PrepareUserGroups.php
Expand Up @@ -12,6 +12,7 @@
use Flarum\User\User;

/**
* @deprecated beta 13, remove in beta 14. Use the User extender instead.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These numbers are not correct (anymore 🙈 ). Will fix this in master.

* The `PrepareUserGroups` event.
*/
class PrepareUserGroups
Expand Down
28 changes: 27 additions & 1 deletion src/Extend/User.php
Expand Up @@ -15,9 +15,10 @@
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
Expand All @@ -29,10 +30,35 @@ public function displayNameDriver(string $identifier, $driver)
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);
});
}
}
22 changes: 22 additions & 0 deletions src/User/User.php
Expand Up @@ -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:.
Expand Down Expand Up @@ -660,8 +666,13 @@ public function permissions()
$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);
}

Expand Down Expand Up @@ -751,6 +762,17 @@ public static function addPreference($key, callable $transformer = null, $defaul
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.
Expand Down
12 changes: 12 additions & 0 deletions src/User/UserServiceProvider.php
Expand Up @@ -31,6 +31,10 @@ public function register()
{
$this->registerAvatarsFilesystem();
$this->registerDisplayNameDrivers();

$this->app->singleton('flarum.user.group_processors', function () {
return [];
});
}

protected function registerDisplayNameDrivers()
Expand Down Expand Up @@ -72,6 +76,14 @@ protected function registerAvatarsFilesystem()
*/
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'));
Expand Down
32 changes: 31 additions & 1 deletion tests/integration/extenders/UserTest.php
Expand Up @@ -24,7 +24,9 @@ protected function prepDb()
$this->prepareDatabase([
'users' => [
$this->adminUser(),
], 'settings' => [
$this->normalUser(),
],
'settings' => [
['key' => 'display_name_driver', 'value' => 'custom'],
],
]);
Expand Down Expand Up @@ -58,6 +60,34 @@ public function can_use_custom_display_name_driver()

$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
Expand Down