mirror of
https://github.com/grocy/grocy.git
synced 2026-04-07 05:16:15 +02:00
Add UI & API for Permissions, protect "User"-(Api)-Controller with new permissions.
This commit is contained in:
parent
f6c76b6e20
commit
359baa794a
|
|
@ -5,6 +5,10 @@ namespace Grocy\Controllers\Users;
|
||||||
abstract class User
|
abstract class User
|
||||||
{
|
{
|
||||||
const PERMISSION_ADMIN = 'ADMIN';
|
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;
|
public abstract function hasPermission(string $permission): bool;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace Grocy\Controllers;
|
namespace Grocy\Controllers;
|
||||||
|
|
||||||
|
use Grocy\Controllers\Users\User;
|
||||||
|
|
||||||
class UsersApiController extends BaseApiController
|
class UsersApiController extends BaseApiController
|
||||||
{
|
{
|
||||||
public function __construct(\DI\Container $container)
|
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)
|
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());
|
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)
|
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();
|
$requestBody = $request->getParsedBody();
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|
@ -43,7 +47,8 @@ class UsersApiController extends BaseApiController
|
||||||
|
|
||||||
public function DeleteUser(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
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']);
|
$this->getUsersService()->DeleteUser($args['userId']);
|
||||||
return $this->EmptyApiResponse($response);
|
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)
|
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
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -108,4 +118,66 @@ class UsersApiController extends BaseApiController
|
||||||
return $this->GenericErrorResponse($response, $ex->getMessage());
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,14 @@
|
||||||
|
|
||||||
namespace Grocy\Controllers;
|
namespace Grocy\Controllers;
|
||||||
|
|
||||||
|
use Grocy\Controllers\Users\User;
|
||||||
|
|
||||||
class UsersController extends BaseController
|
class UsersController extends BaseController
|
||||||
{
|
{
|
||||||
public function UsersList(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args)
|
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')
|
'users' => $this->getDatabase()->users()->orderBy('username')
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -15,16 +18,30 @@ class UsersController extends BaseController
|
||||||
{
|
{
|
||||||
if ($args['userId'] == 'new')
|
if ($args['userId'] == 'new')
|
||||||
{
|
{
|
||||||
return $this->renderPage($response, 'userform', [
|
User::checkPermission($request, User::PERMISSION_CREATE_USER);
|
||||||
|
return $this->renderPage($response, 'userform', [
|
||||||
'mode' => 'create'
|
'mode' => 'create'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
else
|
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']),
|
'user' => $this->getDatabase()->users($args['userId']),
|
||||||
'mode' => 'edit'
|
'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']),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,3 +59,14 @@ SELECT ph.id AS id,
|
||||||
ph.parent AS parent
|
ph.parent AS parent
|
||||||
FROM users u,
|
FROM users u,
|
||||||
permission_hierarchy ph;
|
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'));
|
||||||
41
public/viewjs/permissions.js
Normal file
41
public/viewjs/permissions.js
Normal file
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
11
routes.php
11
routes.php
|
|
@ -34,7 +34,10 @@ $app->group('', function(RouteCollectorProxy $group)
|
||||||
$group->get('/users', '\Grocy\Controllers\UsersController:UsersList');
|
$group->get('/users', '\Grocy\Controllers\UsersController:UsersList');
|
||||||
$group->get('/user/{userId}', '\Grocy\Controllers\UsersController:UserEditForm');
|
$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)
|
if (GROCY_FEATURE_FLAG_STOCK)
|
||||||
{
|
{
|
||||||
$group->get('/stockoverview', '\Grocy\Controllers\StockController:Overview');
|
$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->put('/users/{userId}', '\Grocy\Controllers\UsersApiController:EditUser');
|
||||||
$group->delete('/users/{userId}', '\Grocy\Controllers\UsersApiController:DeleteUser');
|
$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', '\Grocy\Controllers\UsersApiController:GetUserSettings');
|
||||||
$group->get('/user/settings/{settingKey}', '\Grocy\Controllers\UsersApiController:GetUserSetting');
|
$group->get('/user/settings/{settingKey}', '\Grocy\Controllers\UsersApiController:GetUserSetting');
|
||||||
$group->put('/user/settings/{settingKey}', '\Grocy\Controllers\UsersApiController:SetUserSetting');
|
$group->put('/user/settings/{settingKey}', '\Grocy\Controllers\UsersApiController:SetUserSetting');
|
||||||
|
|
|
||||||
15
views/components/permission_select.blade.php
Normal file
15
views/components/permission_select.blade.php
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="{{ $perm->permission_name }}" class="permission-cb" data-perm-id="{{ $perm->permission_id }}" @if($perm->has_permission) checked @endif>
|
||||||
|
{{ $__t($perm->permission_name) }}
|
||||||
|
</label>
|
||||||
|
<div id="permission-sub-{{ $perm->permission_name }}">
|
||||||
|
<ul>
|
||||||
|
@foreach($perm->uihelper_permissionList(array('user_id' => $user->id))->via('parent') as $p)
|
||||||
|
<li>
|
||||||
|
@include('components.permission_select', array(
|
||||||
|
'perm' => $p
|
||||||
|
))
|
||||||
|
</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
33
views/permissions.blade.php
Normal file
33
views/permissions.blade.php
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
@extends('layout.default')
|
||||||
|
|
||||||
|
@section('title', $__t('Permissions of %s', GetUserDisplayName($user)))
|
||||||
|
@section('activeNav', '')
|
||||||
|
@section('viewJsName', 'permissions')
|
||||||
|
@push('pageScripts')
|
||||||
|
<script>
|
||||||
|
var edited_user_id = {{ $user->id }};
|
||||||
|
</script>
|
||||||
|
@endpush
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<h2 class="title">@yield('title')</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="row">
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
@foreach($permissions as $perm)
|
||||||
|
<li>
|
||||||
|
@include('components.permission_select', array(
|
||||||
|
'permission' => $perm
|
||||||
|
))
|
||||||
|
</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
<button id="permission-save" class="btn btn-success" type="submit">{{ $__t('Save') }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
@ -47,6 +47,9 @@
|
||||||
<a class="btn btn-info btn-sm" href="{{ $U('/user/') }}{{ $user->id }}">
|
<a class="btn btn-info btn-sm" href="{{ $U('/user/') }}{{ $user->id }}">
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
</a>
|
</a>
|
||||||
|
<a class="btn btn-info btn-sm" href="{{ $U('/user/permissions/') }}{{ $user->id }}">
|
||||||
|
<i class="fas fa-lock"></i>
|
||||||
|
</a>
|
||||||
<a class="btn btn-danger btn-sm user-delete-button @if($user->id == GROCY_USER_ID) disabled @endif" href="#" data-user-id="{{ $user->id }}" data-user-username="{{ $user->username }}">
|
<a class="btn btn-danger btn-sm user-delete-button @if($user->id == GROCY_USER_ID) disabled @endif" href="#" data-user-id="{{ $user->id }}" data-user-username="{{ $user->username }}">
|
||||||
<i class="fas fa-trash"></i>
|
<i class="fas fa-trash"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user