diff --git a/controllers/Users/User.php b/controllers/Users/User.php index e5484033..0f5ab5fa 100644 --- a/controllers/Users/User.php +++ b/controllers/Users/User.php @@ -5,6 +5,10 @@ namespace Grocy\Controllers\Users; abstract class User { const PERMISSION_ADMIN = 'ADMIN'; + const PERMISSION_CREATE_USER = 'CREATE_USER'; + const PERMISSION_EDIT_USER = 'EDIT_USER'; + const PERMISSION_READ_USER = 'READ_USER'; + const PERMISSION_EDIT_SELF = 'EDIT_SELF'; public abstract function hasPermission(string $permission): bool; diff --git a/controllers/UsersApiController.php b/controllers/UsersApiController.php index c7f21cf0..20f4a00b 100644 --- a/controllers/UsersApiController.php +++ b/controllers/UsersApiController.php @@ -2,6 +2,8 @@ namespace Grocy\Controllers; +use Grocy\Controllers\Users\User; + class UsersApiController extends BaseApiController { public function __construct(\DI\Container $container) @@ -11,7 +13,8 @@ class UsersApiController extends BaseApiController public function GetUsers(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { - try + User::checkPermission($request, User::PERMISSION_READ_USER); + try { return $this->ApiResponse($response, $this->getUsersService()->GetUsersAsDto()); } @@ -23,6 +26,7 @@ class UsersApiController extends BaseApiController public function CreateUser(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { + User::checkPermission($request, User::PERMISSION_CREATE_USER); $requestBody = $request->getParsedBody(); try @@ -43,7 +47,8 @@ class UsersApiController extends BaseApiController public function DeleteUser(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { - try + User::checkPermission($request, User::PERMISSION_EDIT_USER); + try { $this->getUsersService()->DeleteUser($args['userId']); return $this->EmptyApiResponse($response); @@ -56,7 +61,12 @@ class UsersApiController extends BaseApiController public function EditUser(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { - $requestBody = $request->getParsedBody(); + if ($args['userId'] == GROCY_USER_ID) { + User::checkPermission($request, User::PERMISSION_EDIT_SELF); + } else { + User::checkPermission($request, User::PERMISSION_EDIT_USER); + } + $requestBody = $request->getParsedBody(); try { @@ -108,4 +118,66 @@ class UsersApiController extends BaseApiController return $this->GenericErrorResponse($response, $ex->getMessage()); } } + + public function AddPermission(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) + { + try { + User::checkPermission($request, User::PERMISSION_ADMIN); + $requestBody = $request->getParsedBody(); + + $this->getDatabase()->user_permissions()->createRow(array( + 'user_id' => $args['userId'], + 'permission_id' => $requestBody['permission_id'], + ))->save(); + return $this->EmptyApiResponse($response); + } catch (\Slim\Exception\HttpSpecializedException $ex) { + return $this->GenericErrorResponse($response, $ex->getMessage(), $ex->getCode()); + } catch (\Exception $ex) { + return $this->GenericErrorResponse($response, $ex->getMessage()); + } + } + + public function ListPermissions(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) + { + try { + User::checkPermission($request, User::PERMISSION_ADMIN); + + return $this->ApiResponse($response, + $this->getDatabase()->user_permissions()->where($args['userId']) + ); + } catch (\Slim\Exception\HttpSpecializedException $ex) { + return $this->GenericErrorResponse($response, $ex->getMessage(), $ex->getCode()); + } catch (\Exception $ex) { + return $this->GenericErrorResponse($response, $ex->getMessage()); + } + } + + public function SetPermissions(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) + { + try { + User::checkPermission($request, User::PERMISSION_ADMIN); + $requestBody = $request->getParsedBody(); + $db = $this->getDatabase(); + $db->user_permissions() + ->where('user_id', $args['userId']) + ->delete(); + + $perms = []; + + foreach ($requestBody['permissions'] as $perm_id) { + $perms[] = array( + 'user_id' => $args['userId'], + 'permission_id' => $perm_id + ); + } + + $db->insert('user_permissions', $perms, 'batch'); + + return $this->EmptyApiResponse($response); + } catch (\Slim\Exception\HttpSpecializedException $ex) { + return $this->GenericErrorResponse($response, $ex->getMessage(), $ex->getCode()); + } catch (\Exception $ex) { + return $this->GenericErrorResponse($response, $ex->getMessage()); + } + } } diff --git a/controllers/UsersController.php b/controllers/UsersController.php index 01894a66..c8cfb34b 100644 --- a/controllers/UsersController.php +++ b/controllers/UsersController.php @@ -2,11 +2,14 @@ namespace Grocy\Controllers; +use Grocy\Controllers\Users\User; + class UsersController extends BaseController { public function UsersList(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { - return $this->renderPage($response, 'users', [ + User::checkPermission($request, User::PERMISSION_READ_USER); + return $this->renderPage($response, 'users', [ 'users' => $this->getDatabase()->users()->orderBy('username') ]); } @@ -15,16 +18,30 @@ class UsersController extends BaseController { if ($args['userId'] == 'new') { - return $this->renderPage($response, 'userform', [ + User::checkPermission($request, User::PERMISSION_CREATE_USER); + return $this->renderPage($response, 'userform', [ 'mode' => 'create' ]); } else { - return $this->renderPage($response, 'userform', [ + if($args['userId'] == GROCY_USER_ID) + User::checkPermission($request, User::PERMISSION_EDIT_SELF); + else User::checkPermission($request, User::PERMISSION_EDIT_USER); + return $this->renderPage($response, 'userform', [ 'user' => $this->getDatabase()->users($args['userId']), 'mode' => 'edit' ]); } } + + public function PermissionList(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) + { + User::checkPermission($request, User::PERMISSION_READ_USER); + return $this->renderPage($response, 'permissions', [ + 'user' => $this->getDatabase()->users($args['userId']), + 'permissions' => $this->getDatabase()->uihelper_permission() + ->where('parent IS NULL')->where('user_id', $args['userId']), + ]); + } } diff --git a/migrations/0111.sql b/migrations/0111.sql index e3a08b47..c621e6be 100644 --- a/migrations/0111.sql +++ b/migrations/0111.sql @@ -58,4 +58,15 @@ SELECT ph.id AS id, ) AS has_permission, ph.parent AS parent FROM users u, - permission_hierarchy ph; \ No newline at end of file + permission_hierarchy ph; + + +INSERT INTO permission_hierarchy(name, parent) +VALUES ('CREATE_USER', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')); + +INSERT INTO permission_hierarchy(name, parent) +VALUES ('EDIT_USER', last_insert_rowid()); + +INSERT INTO permission_hierarchy(name, parent) +VALUES ('READ_USER', last_insert_rowid()), + ('EDIT_SELF', (SELECT id FROM permission_hierarchy WHERE name = 'ADMIN')); \ No newline at end of file diff --git a/public/viewjs/permissions.js b/public/viewjs/permissions.js new file mode 100644 index 00000000..12b571eb --- /dev/null +++ b/public/viewjs/permissions.js @@ -0,0 +1,41 @@ +$('input.permission-cb').click( + function () { + check_hierachy(this.checked, this.name); + } +); + +function check_hierachy(checked, name) { + var disabled = checked; + $('#permission-sub-' + name).find('input.permission-cb') + .prop('checked', disabled) + .attr('disabled', disabled); +} + +$('#permission-save').click( + function () { + var permission_list = $('input.permission-cb') + .filter(function () { + return $(this).prop('checked') && !$(this).attr('disabled'); + }).map(function () { + return $(this).data('perm-id'); + }).toArray(); + Grocy.Api.Put('users/' + edited_user_id + '/permissions', { + 'permissions': permission_list, + }, function (result) { + toastr.success(__t("Permissions saved!")); + }, function (xhr) { + toastr.error(__t(JSON.parse(xhr.response).error_message)); + } + ); + } +); +if (edited_user_id == Grocy.UserId) { + $('input.permission-cb[name=ADMIN]').click(function () { + if (!this.checked) { + if (!confirm(__t('Are you sure you want to stop being an ADMIN?'))) { + this.checked = true; + check_hierachy(this.checked, this.name); + } + } + }) +} diff --git a/routes.php b/routes.php index 11117c72..7528b9af 100644 --- a/routes.php +++ b/routes.php @@ -34,7 +34,10 @@ $app->group('', function(RouteCollectorProxy $group) $group->get('/users', '\Grocy\Controllers\UsersController:UsersList'); $group->get('/user/{userId}', '\Grocy\Controllers\UsersController:UserEditForm'); - // Stock routes + $group->get('/user/permissions/{userId}', '\Grocy\Controllers\UsersController:PermissionList'); + + + // Stock routes if (GROCY_FEATURE_FLAG_STOCK) { $group->get('/stockoverview', '\Grocy\Controllers\StockController:Overview'); @@ -169,7 +172,11 @@ $app->group('/api', function(RouteCollectorProxy $group) $group->put('/users/{userId}', '\Grocy\Controllers\UsersApiController:EditUser'); $group->delete('/users/{userId}', '\Grocy\Controllers\UsersApiController:DeleteUser'); - // User + $group->get('/users/{userId}/permissions', '\Grocy\Controllers\UsersApiController:ListPermissions'); + $group->post('/users/{userId}/permissions', '\Grocy\Controllers\UsersApiController:AddPermission'); + $group->put('/users/{userId}/permissions', '\Grocy\Controllers\UsersApiController:SetPermissions'); + + // User $group->get('/user/settings', '\Grocy\Controllers\UsersApiController:GetUserSettings'); $group->get('/user/settings/{settingKey}', '\Grocy\Controllers\UsersApiController:GetUserSetting'); $group->put('/user/settings/{settingKey}', '\Grocy\Controllers\UsersApiController:SetUserSetting'); diff --git a/views/components/permission_select.blade.php b/views/components/permission_select.blade.php new file mode 100644 index 00000000..c30005ac --- /dev/null +++ b/views/components/permission_select.blade.php @@ -0,0 +1,15 @@ + +
+ +
\ No newline at end of file diff --git a/views/permissions.blade.php b/views/permissions.blade.php new file mode 100644 index 00000000..cceac372 --- /dev/null +++ b/views/permissions.blade.php @@ -0,0 +1,33 @@ +@extends('layout.default') + +@section('title', $__t('Permissions of %s', GetUserDisplayName($user))) +@section('activeNav', '') +@section('viewJsName', 'permissions') +@push('pageScripts') + +@endpush + +@section('content') +
+
+

@yield('title')

+
+
+
+
+
+ + +
+
+@endsection \ No newline at end of file diff --git a/views/users.blade.php b/views/users.blade.php index e2688ef6..6b991122 100644 --- a/views/users.blade.php +++ b/views/users.blade.php @@ -47,6 +47,9 @@ + + +