From 24cf74623987e7e3df56c237ca1732faacb00b0a Mon Sep 17 00:00:00 2001 From: Damien <damien.burel@maarch.org> Date: Wed, 28 Nov 2018 18:05:36 +0100 Subject: [PATCH] FEAT #8886 User groups and privileges --- sql/data_fr.sql | 15 +++++ sql/structure.sql | 31 +++++++++++ .../controllers/DocumentController.php | 16 ++++-- src/app/user/controllers/UserController.php | 41 +++++++++++++- src/app/user/models/UserGroupModel.php | 55 +++++++++++++++++++ 5 files changed, 150 insertions(+), 8 deletions(-) create mode 100755 src/app/user/models/UserGroupModel.php diff --git a/sql/data_fr.sql b/sql/data_fr.sql index 788222f20b..0bb231951f 100755 --- a/sql/data_fr.sql +++ b/sql/data_fr.sql @@ -6,6 +6,21 @@ ALTER SEQUENCE users_id_seq RESTART WITH 1; INSERT INTO users (email, password, firstname, lastname, mode) VALUES ('jjane@maarch.com', '$2y$10$C.QSslBKD3yNMfRPuZfcaubFwPKiCkqqOUyAdOr5FSGKPaePwuEjG', 'Jenny', 'JANE', 'standard'); INSERT INTO users (email, password, firstname, lastname, mode) VALUES ('ccornillac@maarch.com', '$2y$10$C.QSslBKD3yNMfRPuZfcaubFwPKiCkqqOUyAdOr5FSGKPaePwuEjG', 'Clovis', 'CORNILLAC', 'rest'); +------------ +--GROUPS +------------ +TRUNCATE TABLE groups; +ALTER SEQUENCE groups_id_seq RESTART WITH 2; +INSERT INTO groups (id, label) VALUES (1, 'Administrateur'); + +TRUNCATE TABLE groups_privileges; +ALTER SEQUENCE groups_privileges_id_seq RESTART WITH 1; +INSERT INTO groups_privileges (group_id, privilege) VALUES (1, 'manage_rest_users'); + +TRUNCATE TABLE users_groups; +ALTER SEQUENCE users_groups_id_seq RESTART WITH 1; +INSERT INTO users_groups (group_id, user_id) VALUES (1, 1); + ------------ --DOCSERVERS ------------ diff --git a/sql/structure.sql b/sql/structure.sql index 6987b5b29e..6679c16f16 100755 --- a/sql/structure.sql +++ b/sql/structure.sql @@ -89,6 +89,37 @@ CREATE TABLE users ) WITH (OIDS=FALSE); +DROP TABLE IF EXISTS groups; +CREATE TABLE groups +( + id serial NOT NULL, + label character varying(128) NOT NULL, + CONSTRAINT groups_pkey PRIMARY KEY (id) +) +WITH (OIDS=FALSE); + +DROP TABLE IF EXISTS groups_privileges; +CREATE TABLE groups_privileges +( + id serial NOT NULL, + group_id INTEGER NOT NULL, + privilege character varying(128) NOT NULL, + CONSTRAINT groups_privileges_pkey PRIMARY KEY (id), + CONSTRAINT groups_privileges_unique_key UNIQUE (group_id, privilege) +) +WITH (OIDS=FALSE); + +DROP TABLE IF EXISTS users_groups; +CREATE TABLE users_groups +( + id serial NOT NULL, + group_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + CONSTRAINT users_groups_pkey PRIMARY KEY (id), + CONSTRAINT users_groups_unique_key UNIQUE (group_id, user_id) +) +WITH (OIDS=FALSE); + DROP TABLE IF EXISTS docservers; CREATE TABLE docservers ( diff --git a/src/app/document/controllers/DocumentController.php b/src/app/document/controllers/DocumentController.php index a17ad735d8..92fb1f80f0 100755 --- a/src/app/document/controllers/DocumentController.php +++ b/src/app/document/controllers/DocumentController.php @@ -318,10 +318,13 @@ class DocumentController public function getHandwrittenDocumentById(Request $request, Response $response, array $args) { - if (!DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id']])) { - return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']); + $user = UserModel::getById(['select' => ['mode'], 'id' => $GLOBALS['id']]); + if ($user['mode'] != 'rest') { + if (!DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id']])) { + return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']); + } } - + $adr = AdrModel::getDocumentsAdr([ 'select' => ['path', 'filename', 'fingerprint'], 'where' => ['main_document_id = ?', 'type = ?'], @@ -351,8 +354,11 @@ class DocumentController public function getStatusById(Request $request, Response $response, array $args) { - if (!DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id']])) { - return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']); + $user = UserModel::getById(['select' => ['mode'], 'id' => $GLOBALS['id']]); + if ($user['mode'] != 'rest') { + if (!DocumentController::hasRightById(['id' => $args['id'], 'userId' => $GLOBALS['id']])) { + return $response->withStatus(403)->withJson(['errors' => 'Document out of perimeter']); + } } $document = DocumentModel::getById(['select' => ['status', 'mode'], 'id' => $args['id']]); diff --git a/src/app/user/controllers/UserController.php b/src/app/user/controllers/UserController.php index 9484873384..560e5370ad 100755 --- a/src/app/user/controllers/UserController.php +++ b/src/app/user/controllers/UserController.php @@ -22,12 +22,19 @@ use Slim\Http\Request; use Slim\Http\Response; use SrcCore\controllers\PasswordController; use SrcCore\models\AuthenticationModel; +use SrcCore\models\ValidatorModel; +use User\models\UserGroupModel; use User\models\UserModel; class UserController { public function get(Request $request, Response $response) { + $user = UserModel::getById(['select' => ['mode'], 'id' => $GLOBALS['id']]); + if ($user['mode'] != 'rest') { + return $response->withStatus(403)->withJson(['errors' => 'Route forbidden']); + } + $users = UserModel::get([ 'select' => ['id', 'firstname', 'lastname'], 'where' => ['mode = ?'], @@ -50,11 +57,18 @@ class UserController $user['picture'] = 'data:image/png;base64,' . $user['picture']; } + $user['canManageRestUsers'] = UserController::hasPrivilege(['userId' => $args['id'], 'privilege' => 'manage_rest_users']); + return $response->withJson(['user' => $user]); } public function create(Request $request, Response $response) { + $user = UserModel::getById(['select' => ['mode'], 'id' => $GLOBALS['id']]); + if ($user['mode'] != 'rest') { + return $response->withStatus(403)->withJson(['errors' => 'Route forbidden']); + } + $data = $request->getParams(); if (!Validator::stringType()->notEmpty()->validate($data['firstname'])) { @@ -69,7 +83,7 @@ class UserController $existingUser = UserModel::getByEmail(['email' => $data['email'], 'select' => ['id']]); if (!empty($existingUser)) { - return $response->withStatus(400)->withJson(['errors' => 'User already exist']); + return $response->withStatus(400)->withJson(['errors' => 'User already exists']); } $logingModes = ['standard', 'rest']; @@ -146,8 +160,12 @@ class UserController public function updatePassword(Request $request, Response $response, array $args) { + $user = UserModel::getById(['select' => ['email', 'mode'], 'id' => $args['id']]); + if ($GLOBALS['id'] != $args['id']) { - return $response->withStatus(403)->withJson(['errors' => 'User out of perimeter']); + if ($user['mode'] != 'rest' || !UserController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_rest_users'])) { + return $response->withStatus(403)->withJson(['errors' => 'User out of perimeter']); + } } $data = $request->getParams(); @@ -158,7 +176,6 @@ class UserController return $response->withStatus(400)->withJson(['errors' => 'Bad Request']); } - $user = UserModel::getById(['select' => ['email'], 'id' => $args['id']]); if ($data['newPassword'] != $data['passwordConfirmation']) { return $response->withStatus(400)->withJson(['errors' => 'New password does not match password confirmation']); } elseif (!AuthenticationModel::authentication(['email' => $user['email'], 'password' => $data['currentPassword']])) { @@ -284,4 +301,22 @@ class UserController return $response->withJson(['success' => 'success']); } + + public function hasPrivilege(array $args) + { + ValidatorModel::notEmpty($args, ['userId', 'privilege']); + ValidatorModel::intVal($args, ['userId']); + ValidatorModel::stringType($args, ['privilege']); + + $groups = UserGroupModel::get(['select' => ['group_id'], 'where' => ['user_id = ?'], 'data' => [$args['userId']]]); + + foreach ($groups as $group) { + $privilege = UserGroupModel::getPrivileges(['select' => [1], 'where' => ['group_id = ?', 'privilege = ?'], 'data' => [$group['group_id'], $args['privilege']]]); + if (!empty($privilege)) { + return true; + } + } + + return false; + } } diff --git a/src/app/user/models/UserGroupModel.php b/src/app/user/models/UserGroupModel.php new file mode 100755 index 0000000000..8750ccc956 --- /dev/null +++ b/src/app/user/models/UserGroupModel.php @@ -0,0 +1,55 @@ +<?php + +/** +* Copyright Maarch since 2008 under licence GPLv3. +* See LICENCE.txt file at the root folder for more details. +* This file is part of Maarch software. +* +*/ + +/** +* @brief User Group Model +* @author dev@maarch.org +*/ + +namespace User\models; + +use SrcCore\models\DatabaseModel; +use SrcCore\models\ValidatorModel; + +class UserGroupModel +{ + public static function get(array $aArgs) + { + ValidatorModel::arrayType($aArgs, ['select', 'where', 'data', 'orderBy']); + ValidatorModel::intType($aArgs, ['limit']); + + $usersGroups = DatabaseModel::select([ + 'select' => empty($aArgs['select']) ? ['*'] : $aArgs['select'], + 'table' => ['users_groups'], + 'where' => empty($aArgs['where']) ? [] : $aArgs['where'], + 'data' => empty($aArgs['data']) ? [] : $aArgs['data'], + 'orderBy' => empty($aArgs['orderBy']) ? [] : $aArgs['orderBy'], + 'limit' => empty($aArgs['limit']) ? 0 : $aArgs['limit'] + ]); + + return $usersGroups; + } + + public static function getPrivileges(array $aArgs) + { + ValidatorModel::arrayType($aArgs, ['select', 'where', 'data', 'orderBy']); + ValidatorModel::intType($aArgs, ['limit']); + + $groupsPrivileges = DatabaseModel::select([ + 'select' => empty($aArgs['select']) ? ['*'] : $aArgs['select'], + 'table' => ['groups_privileges'], + 'where' => empty($aArgs['where']) ? [] : $aArgs['where'], + 'data' => empty($aArgs['data']) ? [] : $aArgs['data'], + 'orderBy' => empty($aArgs['orderBy']) ? [] : $aArgs['orderBy'], + 'limit' => empty($aArgs['limit']) ? 0 : $aArgs['limit'] + ]); + + return $groupsPrivileges; + } +} -- GitLab