Auth: Added canAny

This commit is contained in:
Igor Scheller 2024-04-07 14:18:38 +02:00 committed by xuwhite
parent d18e203560
commit a214e0ff8f
8 changed files with 80 additions and 31 deletions

View File

@ -677,11 +677,7 @@ function User_view(
url('/shifts-json-export', ['key' => $user_source->api_key]), url('/shifts-json-export', ['key' => $user_source->api_key]),
icon('braces') . __('JSON Export') icon('braces') . __('JSON Export')
) : '', ) : '',
( $auth->canAny(['shifts_json_export', 'ical', 'atom']) ? button(
$auth->can('shifts_json_export')
|| $auth->can('ical')
|| $auth->can('atom')
) ? button(
url('/user-myshifts', ['reset' => 1]), url('/user-myshifts', ['reset' => 1]),
icon('arrow-repeat') . __('Reset API key') icon('arrow-repeat') . __('Reset API key')
) : '', ) : '',

View File

@ -38,8 +38,7 @@ class UserSettingsController extends BaseController
if ( if (
!( !(
$this->auth->can('user.ifsg.edit') $this->auth->canAny(['user.ifsg.edit', 'user.drive.edit'])
|| $this->auth->can('user.drive.edit')
|| $this->isDriverLicenseSupporter() || $this->isDriverLicenseSupporter()
|| $this->isIfsgSupporter() || $this->isIfsgSupporter()
) )
@ -158,9 +157,8 @@ class UserSettingsController extends BaseController
'title' => 'settings.certificates', 'title' => 'settings.certificates',
'icon' => 'card-checklist', 'icon' => 'card-checklist',
'permission' => ( 'permission' => (
$this->auth->can('user.ifsg.edit') $this->auth->canAny(['user.ifsg.edit', 'user.drive.edit'])
|| $this->isIfsgSupporter() || $this->isIfsgSupporter()
|| $this->auth->can('user.drive.edit')
|| $this->isDriverLicenseSupporter() || $this->isDriverLicenseSupporter()
) ? null : '_', ) ? null : '_',
]; ];

View File

@ -102,8 +102,7 @@ class NewsController extends BaseController
$comment = $this->comment->findOrFail($commentId); $comment = $this->comment->findOrFail($commentId);
if ( if (
$comment->user->id != $this->auth->user()->id $comment->user->id != $this->auth->user()->id
&& !$this->auth->can('admin_news') && !$this->auth->canAny(['admin_news', 'comment.delete'])
&& !$this->auth->can('comment.delete')
) { ) {
throw new HttpForbidden(); throw new HttpForbidden();
} }

View File

@ -97,24 +97,7 @@ class Authenticator
$abilities = (array) $abilities; $abilities = (array) $abilities;
if (empty($this->permissions)) { if (empty($this->permissions)) {
$user = $this->user(); $this->loadPermissions();
if ($user) {
$this->permissions = $user->privileges->pluck('name')->toArray();
if ($user->last_login_at < Carbon::now()->subMinutes(5) && !$this->isApiRequest()) {
$user->last_login_at = Carbon::now();
$user->save(['touch' => false]);
}
} elseif ($this->session->get('user_id')) {
$this->session->remove('user_id');
}
if (empty($this->permissions)) {
/** @var Group $group */
$group = Group::find($this->guestRole);
$this->permissions = $group->privileges->pluck('name')->toArray();
}
} }
foreach ($abilities as $ability) { foreach ($abilities as $ability) {
@ -126,6 +109,22 @@ class Authenticator
return true; return true;
} }
/**
* @param string[]|string $abilities
*/
public function canAny(array|string $abilities): bool
{
$abilities = (array) $abilities;
foreach ($abilities as $ability) {
if ($this->can($ability)) {
return true;
}
}
return false;
}
public function authenticate(string $login, string $password): ?User public function authenticate(string $login, string $password): ?User
{ {
/** @var User $user */ /** @var User $user */
@ -248,4 +247,26 @@ class Authenticator
{ {
$this->guestRole = $guestRole; $this->guestRole = $guestRole;
} }
protected function loadPermissions(): void
{
$user = $this->user();
if ($user) {
$this->permissions = $user->privileges->pluck('name')->toArray();
if ($user->last_login_at < Carbon::now()->subMinutes(5) && !$this->isApiRequest()) {
$user->last_login_at = Carbon::now();
$user->save(['touch' => false]);
}
} elseif ($this->session->get('user_id')) {
$this->session->remove('user_id');
}
if (empty($this->permissions)) {
/** @var Group $group */
$group = Group::find($this->guestRole);
$this->permissions = $group->privileges->pluck('name')->toArray();
}
}
} }

View File

@ -23,6 +23,7 @@ class Authentication extends TwigExtension
new TwigFunction('is_user', [$this, 'isAuthenticated']), new TwigFunction('is_user', [$this, 'isAuthenticated']),
new TwigFunction('is_guest', [$this, 'isGuest']), new TwigFunction('is_guest', [$this, 'isGuest']),
new TwigFunction('has_permission_to', [$this->auth, 'can']), new TwigFunction('has_permission_to', [$this->auth, 'can']),
new TwigFunction('has_permission_to_any', [$this->auth, 'canAny']),
]; ];
} }

View File

@ -64,7 +64,7 @@ class UserSettingsControllerTest extends ControllerTest
public function testCertificateByPermission(): void public function testCertificateByPermission(): void
{ {
config(['ifsg_enabled' => true]); config(['ifsg_enabled' => true]);
$this->setExpects($this->auth, 'can', ['user.ifsg.edit'], true, $this->atLeastOnce()); $this->setExpects($this->auth, 'canAny', [['user.ifsg.edit', 'user.drive.edit']], true, $this->atLeastOnce());
$this->response->expects($this->once()) $this->response->expects($this->once())
->method('withView') ->method('withView')

View File

@ -227,6 +227,7 @@ class AuthenticatorTest extends ServiceProviderTest
/** /**
* @covers \Engelsystem\Helpers\Authenticator::can * @covers \Engelsystem\Helpers\Authenticator::can
* @covers \Engelsystem\Helpers\Authenticator::loadPermissions
* @covers \Engelsystem\Helpers\Authenticator::isApiRequest * @covers \Engelsystem\Helpers\Authenticator::isApiRequest
*/ */
public function testCan(): void public function testCan(): void
@ -252,11 +253,15 @@ class AuthenticatorTest extends ServiceProviderTest
$this->assertTrue($auth->can('bar')); $this->assertTrue($auth->can('bar'));
// Permissions cached // Permissions cached
$this->assertTrue($auth->can('bar')); $this->assertTrue($auth->can(['bar']));
// Can not
$this->assertFalse($auth->can(['nope']));
} }
/** /**
* @covers \Engelsystem\Helpers\Authenticator::can * @covers \Engelsystem\Helpers\Authenticator::can
* @covers \Engelsystem\Helpers\Authenticator::loadPermissions
*/ */
public function testCanUnauthorized(): void public function testCanUnauthorized(): void
{ {
@ -275,6 +280,34 @@ class AuthenticatorTest extends ServiceProviderTest
$this->assertNull($session->get('user_id')); $this->assertNull($session->get('user_id'));
} }
/**
* @covers \Engelsystem\Helpers\Authenticator::canAny
*/
public function testCanAny(): void
{
$this->initDatabase();
$request = new Request();
$this->app->instance('request', $request);
$session = new Session(new MockArraySessionStorage());
/** @var User $user */
$user = User::factory()->create();
/** @var Group $group */
$group = Group::factory()->create();
/** @var Privilege $privilege */
$privilege = Privilege::factory()->create(['name' => 'bar']);
$user->groups()->attach($group);
$group->privileges()->attach($privilege);
$auth = new Authenticator($request, $session, new User());
$session->set('user_id', $user->id);
$this->assertTrue($auth->canAny('bar'));
$this->assertTrue($auth->canAny(['foo', 'bar', 'baz']));
$this->assertFalse($auth->canAny(['lorem', 'ipsum']));
}
/** /**
* @covers \Engelsystem\Helpers\Authenticator::authenticate * @covers \Engelsystem\Helpers\Authenticator::authenticate
*/ */

View File

@ -26,6 +26,7 @@ class AuthenticationTest extends ExtensionTest
$this->assertExtensionExists('is_user', [$extension, 'isAuthenticated'], $functions); $this->assertExtensionExists('is_user', [$extension, 'isAuthenticated'], $functions);
$this->assertExtensionExists('is_guest', [$extension, 'isGuest'], $functions); $this->assertExtensionExists('is_guest', [$extension, 'isGuest'], $functions);
$this->assertExtensionExists('has_permission_to', [$auth, 'can'], $functions); $this->assertExtensionExists('has_permission_to', [$auth, 'can'], $functions);
$this->assertExtensionExists('has_permission_to_any', [$auth, 'canAny'], $functions);
} }
/** /**