diff options
Diffstat (limited to 'app/Model')
-rw-r--r-- | app/Model/Acl.php | 182 | ||||
-rw-r--r-- | app/Model/Authentication.php | 9 | ||||
-rw-r--r-- | app/Model/Base.php | 1 | ||||
-rw-r--r-- | app/Model/Notification.php | 2 | ||||
-rw-r--r-- | app/Model/Project.php | 4 | ||||
-rw-r--r-- | app/Model/ProjectPermission.php | 82 | ||||
-rw-r--r-- | app/Model/SubTask.php | 1 | ||||
-rw-r--r-- | app/Model/TaskExport.php | 1 | ||||
-rw-r--r-- | app/Model/TaskPermission.php | 4 | ||||
-rw-r--r-- | app/Model/TaskPosition.php | 1 | ||||
-rw-r--r-- | app/Model/User.php | 43 | ||||
-rw-r--r-- | app/Model/UserSession.php | 89 |
12 files changed, 244 insertions, 175 deletions
diff --git a/app/Model/Acl.php b/app/Model/Acl.php index d294197a..0d26edc4 100644 --- a/app/Model/Acl.php +++ b/app/Model/Acl.php @@ -3,7 +3,7 @@ namespace Model; /** - * Acl model + * Access List * * @package model * @author Frederic Guillot @@ -16,162 +16,188 @@ class Acl extends Base * @access private * @var array */ - private $public_actions = array( + private $public_acl = array( 'user' => array('login', 'check', 'google', 'github'), 'task' => array('readonly'), 'board' => array('readonly'), 'project' => array('feed'), - 'webhook' => array('task', 'github', 'gitlab'), + 'webhook' => '*', ); /** - * Controllers and actions allowed for regular users + * Controllers and actions for project members * * @access private * @var array */ - private $user_actions = array( - 'app' => array('index', 'preview', 'status'), - 'project' => array('index', 'show', 'exporttasks', 'exportdaily', 'share', 'edit', 'update', 'users', 'remove', 'duplicate', 'disable', 'enable', 'activity', 'search', 'tasks', 'create', 'save', 'revoke', 'setowner', 'allow'), - 'board' => array('index', 'show', 'save', 'check', 'changeassignee', 'updateassignee', 'changecategory', 'updatecategory', 'movecolumn', 'edit', 'update', 'add', 'confirm', 'remove', 'subtasks', 'togglesubtask', 'attachments', 'comments', 'description'), - 'user' => array('edit', 'forbidden', 'logout', 'show', 'external', 'unlinkgoogle', 'unlinkgithub', 'sessions', 'removesession', 'last', 'notifications', 'password'), - 'comment' => array('create', 'save', 'confirm', 'remove', 'update', 'edit', 'forbidden'), - 'file' => array('create', 'save', 'download', 'confirm', 'remove', 'open', 'image'), - 'subtask' => array('create', 'save', 'edit', 'update', 'confirm', 'remove', 'togglestatus'), - 'task' => array('show', 'create', 'save', 'edit', 'update', 'close', 'open', 'duplicate', 'remove', 'description', 'move', 'copy', 'time'), - 'category' => array('index', 'save', 'edit', 'update', 'confirm', 'remove'), - 'action' => array('index', 'event', 'params', 'create', 'confirm', 'remove'), - 'analytic' => array('tasks', 'users', 'cfd'), - 'swimlane' => array('index', 'save', 'change', 'edit', 'update', 'confirm', 'remove', 'disable', 'enable', 'moveup', 'movedown'), + private $member_acl = array( + 'board' => '*', + 'comment' => '*', + 'file' => '*', + 'project' => array('show', 'tasks', 'search', 'activity'), + 'subtask' => '*', + 'task' => '*', ); /** - * Return true if the specified controller/action is allowed according to the given acl + * Controllers and actions for project managers * - * @access public - * @param array $acl Acl list - * @param string $controller Controller name - * @param string $action Action name - * @return bool + * @access private + * @var array */ - public function isAllowedAction(array $acl, $controller, $action) - { - if (isset($acl[$controller])) { - return in_array($action, $acl[$controller]); - } + private $manager_acl = array( + 'action' => '*', + 'analytic' => '*', + 'board' => array('movecolumn', 'edit', 'update', 'add', 'remove'), + 'category' => '*', + 'project' => array('edit', 'update', 'exporttasks', 'exportdailyprojectsummary', 'share', 'integration', 'users', 'alloweverybody', 'allow', 'setowner', 'revoke', 'duplicate', 'disable', 'enable'), + 'swimlane' => '*', + 'task' => array('remove'), + ); - return false; - } + /** + * Controllers and actions for admins + * + * @access private + * @var array + */ + private $admin_acl = array( + 'user' => array('index', 'create', 'save', 'remove'), + 'config' => '*', + 'project' => array('remove'), + ); /** - * Return true if the given action is public + * Return true if the specified controller/action match the given acl * * @access public + * @param array $acl Acl list * @param string $controller Controller name * @param string $action Action name * @return bool */ - public function isPublicAction($controller, $action) + public function matchAcl(array $acl, $controller, $action) { - return $this->isAllowedAction($this->public_actions, $controller, $action); + $action = strtolower($action); + return isset($acl[$controller]) && $this->hasAction($action, $acl[$controller]); } /** - * Return true if the given action is allowed for a regular user + * Return true if the specified action is inside the list of actions * * @access public - * @param string $controller Controller name * @param string $action Action name + * @param mixed $action Actions list * @return bool */ - public function isUserAction($controller, $action) + public function hasAction($action, $actions) { - return $this->isAllowedAction($this->user_actions, $controller, $action); + if (is_array($actions)) { + return in_array($action, $actions); + } + + return $actions === '*'; } /** - * Return true if the logged user is admin + * Return true if the given action is public * * @access public + * @param string $controller Controller name + * @param string $action Action name * @return bool */ - public function isAdminUser() + public function isPublicAction($controller, $action) { - return isset($this->session['user']['is_admin']) && $this->session['user']['is_admin'] === true; + return $this->matchAcl($this->public_acl, $controller, $action); } /** - * Return true if the logged user is not admin + * Return true if the given action is for admins * * @access public + * @param string $controller Controller name + * @param string $action Action name * @return bool */ - public function isRegularUser() + public function isAdminAction($controller, $action) { - return isset($this->session['user']['is_admin']) && $this->session['user']['is_admin'] === false; + return $this->matchAcl($this->admin_acl, $controller, $action); } /** - * Get the connected user id + * Return true if the given action is for project managers * * @access public - * @return integer + * @param string $controller Controller name + * @param string $action Action name + * @return bool */ - public function getUserId() + public function isManagerAction($controller, $action) { - return isset($this->session['user']['id']) ? (int) $this->session['user']['id'] : 0; + return $this->matchAcl($this->manager_acl, $controller, $action); } /** - * Check if the given user_id is the connected user + * Return true if the given action is for project members * - * @param integer $user_id User id - * @return boolean + * @access public + * @param string $controller Controller name + * @param string $action Action name + * @return bool */ - public function isCurrentUser($user_id) + public function isMemberAction($controller, $action) { - return $this->acl->getUserId() == $user_id; + return $this->matchAcl($this->member_acl, $controller, $action); } /** - * Check is the user is connected + * Return true if the visitor is allowed to access to the given page + * We suppose the user already authenticated * * @access public + * @param string $controller Controller name + * @param string $action Action name + * @param integer $project_id Project id * @return bool */ - public function isLogged() + public function isAllowed($controller, $action, $project_id = 0) { - return ! empty($this->session['user']); + // If you are admin you have access to everything + if ($this->userSession->isAdmin()) { + return true; + } + + // If you access to an admin action, your are not allowed + if ($this->isAdminAction($controller, $action)) { + return false; + } + + // Check project manager permissions + if ($this->isManagerAction($controller, $action)) { + return $this->isManagerActionAllowed($project_id); + } + + // Check project member permissions + if ($this->isMemberAction($controller, $action)) { + return $this->isMemberActionAllowed($project_id); + } + + // Other applications actions are allowed + return true; } - /** - * Check is the user was authenticated with the RememberMe or set the value - * - * @access public - * @param bool $value Set true if the user use the RememberMe - * @return bool - */ - public function isRememberMe($value = null) + public function isManagerActionAllowed($project_id) { - if ($value !== null) { - $this->session['is_remember_me'] = $value; + if ($this->userSession->isAdmin()) { + return true; } - return empty($this->session['is_remember_me']) ? false : $this->session['is_remember_me']; + return $project_id > 0 && $this->projectPermission->isManager($project_id, $this->userSession->getId()); } - /** - * Check if an action is allowed for the logged user - * - * @access public - * @param string $controller Controller name - * @param string $action Action name - * @return bool - */ - public function isPageAccessAllowed($controller, $action) + public function isMemberActionAllowed($project_id) { - return $this->isPublicAction($controller, $action) || - $this->isAdminUser() || - ($this->isRegularUser() && $this->isUserAction($controller, $action)); + return $project_id > 0 && $this->projectPermission->isMember($project_id, $this->userSession->getId()); } } diff --git a/app/Model/Authentication.php b/app/Model/Authentication.php index 87493c88..f917bff4 100644 --- a/app/Model/Authentication.php +++ b/app/Model/Authentication.php @@ -41,13 +41,8 @@ class Authentication extends Base */ public function isAuthenticated($controller, $action) { - // If the action is public we don't need to do any checks - if ($this->acl->isPublicAction($controller, $action)) { - return true; - } - // If the user is already logged it's ok - if ($this->acl->isLogged()) { + if ($this->userSession->isLogged()) { // We update each time the RememberMe cookie tokens if ($this->backend('rememberMe')->hasCookie()) { @@ -117,7 +112,7 @@ class Authentication extends Base if (! empty($values['remember_me'])) { $credentials = $this->backend('rememberMe') - ->create($this->acl->getUserId(), Request::getIpAddress(), Request::getUserAgent()); + ->create($this->userSession->getId(), Request::getIpAddress(), Request::getUserAgent()); $this->backend('rememberMe')->writeCookie($credentials['token'], $credentials['sequence'], $credentials['expiration']); } diff --git a/app/Model/Base.php b/app/Model/Base.php index db670969..3f847c2e 100644 --- a/app/Model/Base.php +++ b/app/Model/Base.php @@ -39,6 +39,7 @@ use Pimple\Container; * @property \Model\TaskValidator $taskValidator * @property \Model\TimeTracking $timeTracking * @property \Model\User $user + * @property \Model\UserSession $userSession * @property \Model\Webhook $webhook */ abstract class Base diff --git a/app/Model/Notification.php b/app/Model/Notification.php index 99db78ad..8c13aada 100644 --- a/app/Model/Notification.php +++ b/app/Model/Notification.php @@ -66,7 +66,7 @@ class Notification extends Base { // Exclude the connected user if (Session::isOpen()) { - $exclude_users[] = $this->acl->getUserId(); + $exclude_users[] = $this->userSession->getId(); } $users = $this->getUsersWithNotification($project_id, $exclude_users); diff --git a/app/Model/Project.php b/app/Model/Project.php index 278fe6ab..de9408ec 100644 --- a/app/Model/Project.php +++ b/app/Model/Project.php @@ -109,7 +109,7 @@ class Project extends Base foreach ($projects as $key => $project) { - if (! $this->projectPermission->isUserAllowed($project['id'], $this->acl->getUserId())) { + if (! $this->projectPermission->isUserAllowed($project['id'], $this->userSession->getId())) { unset($projects[$key]); } } @@ -295,7 +295,7 @@ class Project extends Base } if ($add_user && $user_id) { - $this->projectPermission->allowUser($project_id, $user_id); + $this->projectPermission->addManager($project_id, $user_id); } $this->category->createDefaultCategories($project_id); diff --git a/app/Model/ProjectPermission.php b/app/Model/ProjectPermission.php index bcb11eca..a53f9195 100644 --- a/app/Model/ProjectPermission.php +++ b/app/Model/ProjectPermission.php @@ -92,7 +92,7 @@ class ProjectPermission extends Base * @param integer $project_id Project id * @return array */ - public function getOwners($project_id) + public function getManagers($project_id) { $users = $this->db ->table(self::TABLE) @@ -118,13 +118,13 @@ class ProjectPermission extends Base $users = array( 'allowed' => array(), 'not_allowed' => array(), - 'owners' => array(), + 'managers' => array(), ); $all_users = $this->user->getList(); $users['allowed'] = $this->getMembers($project_id); - $users['owners'] = $this->getOwners($project_id); + $users['managers'] = $this->getManagers($project_id); foreach ($all_users as $user_id => $username) { @@ -137,14 +137,14 @@ class ProjectPermission extends Base } /** - * Allow a specific user for a given project + * Add a new project member * * @access public * @param integer $project_id Project id * @param integer $user_id User id * @return bool */ - public function allowUser($project_id, $user_id) + public function addMember($project_id, $user_id) { return $this->db ->table(self::TABLE) @@ -152,38 +152,53 @@ class ProjectPermission extends Base } /** - * Make the specific user owner of the given project + * Remove a member * * @access public * @param integer $project_id Project id * @param integer $user_id User id - * @param integer $is_owner Is user owner of the project * @return bool */ - public function setOwner($project_id, $user_id, $is_owner = 1) + public function revokeMember($project_id, $user_id) { return $this->db ->table(self::TABLE) ->eq('project_id', $project_id) ->eq('user_id', $user_id) - ->update(array('is_owner' => $is_owner)); + ->remove(); + } + + /** + * Add a project manager + * + * @access public + * @param integer $project_id Project id + * @param integer $user_id User id + * @return bool + */ + public function addManager($project_id, $user_id) + { + return $this->db + ->table(self::TABLE) + ->save(array('project_id' => $project_id, 'user_id' => $user_id, 'is_owner' => 1)); } /** - * Revoke a specific user for a given project + * Change the role of a member * * @access public * @param integer $project_id Project id * @param integer $user_id User id + * @param integer $is_owner Is user owner of the project * @return bool */ - public function revokeUser($project_id, $user_id) + public function changeRole($project_id, $user_id, $is_owner) { return $this->db ->table(self::TABLE) ->eq('project_id', $project_id) ->eq('user_id', $user_id) - ->remove(); + ->update(array('is_owner' => $is_owner)); } /** @@ -200,29 +215,29 @@ class ProjectPermission extends Base return true; } - return (bool) $this->db + return $this->db ->table(self::TABLE) ->eq('project_id', $project_id) ->eq('user_id', $user_id) - ->count(); + ->count() === 1; } /** - * Check if a specific user is owner of a given project + * Check if a specific user is manager of a given project * * @access public * @param integer $project_id Project id * @param integer $user_id User id * @return bool */ - public function isOwner($project_id, $user_id) + public function isManager($project_id, $user_id) { - return (bool) $this->db + return $this->db ->table(self::TABLE) ->eq('project_id', $project_id) ->eq('user_id', $user_id) ->eq('is_owner', 1) - ->count(); + ->count() === 1; } /** @@ -247,36 +262,11 @@ class ProjectPermission extends Base */ public function isEverybodyAllowed($project_id) { - return (bool) $this->db + return $this->db ->table(Project::TABLE) ->eq('id', $project_id) ->eq('is_everybody_allowed', 1) - ->count(); - } - - /** - * Check if a specific user is allowed to manage a project - * - * @access public - * @param integer $project_id Project id - * @param integer $user_id User id - * @return bool - */ - public function adminAllowed($project_id, $user_id) - { - if ($this->user->isAdmin($user_id)) { - return true; - } - - if ($this->isUserAllowed($project_id, $user_id) && $this->project->isPrivate($project_id)) { - return true; - } - - if ($this->isOwner($project_id, $user_id)) { - return true; - } - - return false; + ->count() === 1; } /** @@ -336,7 +326,7 @@ class ProjectPermission extends Base $users = $this->getMembers($project_from); foreach ($users as $user_id => $name) { - if (! $this->allowUser($project_to, $user_id)) { + if (! $this->addMember($project_to, $user_id)) { // TODO: Duplicate managers return false; } } diff --git a/app/Model/SubTask.php b/app/Model/SubTask.php index 12558429..adc5aa73 100644 --- a/app/Model/SubTask.php +++ b/app/Model/SubTask.php @@ -227,6 +227,7 @@ class SubTask extends Base $subtasks = $db->table(SubTask::TABLE) ->columns('title', 'time_estimated') ->eq('task_id', $src_task_id) + ->asc('id') // Explicit sorting for postgresql ->findAll(); foreach ($subtasks as &$subtask) { diff --git a/app/Model/TaskExport.php b/app/Model/TaskExport.php index 13a1eddd..1592deb1 100644 --- a/app/Model/TaskExport.php +++ b/app/Model/TaskExport.php @@ -73,6 +73,7 @@ class TaskExport extends Base LEFT JOIN columns ON columns.id = tasks.column_id LEFT JOIN projects ON projects.id = tasks.project_id WHERE tasks.date_creation >= ? AND tasks.date_creation <= ? AND tasks.project_id = ? + ORDER BY tasks.id ASC '; if (! is_numeric($from)) { diff --git a/app/Model/TaskPermission.php b/app/Model/TaskPermission.php index 2ab154f4..53740a9a 100644 --- a/app/Model/TaskPermission.php +++ b/app/Model/TaskPermission.php @@ -20,10 +20,10 @@ class TaskPermission extends Base */ public function canRemoveTask(array $task) { - if ($this->acl->isAdminUser()) { + if ($this->userSession->isAdmin()) { return true; } - else if (isset($task['creator_id']) && $task['creator_id'] == $this->acl->getUserId()) { + else if (isset($task['creator_id']) && $task['creator_id'] == $this->userSession->getId()) { return true; } diff --git a/app/Model/TaskPosition.php b/app/Model/TaskPosition.php index a1e4152c..2c271de7 100644 --- a/app/Model/TaskPosition.php +++ b/app/Model/TaskPosition.php @@ -72,6 +72,7 @@ class TaskPosition extends Base ->eq('column_id', $board_column_id) ->neq('id', $task_id) ->asc('position') + ->asc('id') // Fix Postgresql unit test ->findAllByColumn('id'); } diff --git a/app/Model/User.php b/app/Model/User.php index 3754b918..78d44b47 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -48,46 +48,11 @@ class User extends Base */ public function isAdmin($user_id) { - $result = $this->db + return $this->db ->table(User::TABLE) ->eq('id', $user_id) ->eq('is_admin', 1) - ->count(); - - return $result > 0; - } - - /** - * Get the default project from the session - * - * @access public - * @return integer - */ - public function getFavoriteProjectId() - { - return isset($this->session['user']['default_project_id']) ? $this->session['user']['default_project_id'] : 0; - } - - /** - * Get the last seen project from the session - * - * @access public - * @return integer - */ - public function getLastSeenProjectId() - { - return empty($this->session['last_show_project_id']) ? 0 : $this->session['last_show_project_id']; - } - - /** - * Set the last seen project from the session - * - * @access public - * @@param integer $project_id Project id - */ - public function storeLastSeenProjectId($project_id) - { - $this->session['last_show_project_id'] = (int) $project_id; + ->count() === 1; } /** @@ -287,7 +252,7 @@ class User extends Base $result = $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values); // If the user is connected refresh his session - if (Session::isOpen() && $this->acl->getUserId() == $values['id']) { + if (Session::isOpen() && $this->userSession->getId() == $values['id']) { $this->updateSession(); } @@ -337,7 +302,7 @@ class User extends Base public function updateSession(array $user = array()) { if (empty($user)) { - $user = $this->getById($this->acl->getUserId()); + $user = $this->getById($this->userSession->getId()); } if (isset($user['password'])) { diff --git a/app/Model/UserSession.php b/app/Model/UserSession.php new file mode 100644 index 00000000..c27b3743 --- /dev/null +++ b/app/Model/UserSession.php @@ -0,0 +1,89 @@ +<?php + +namespace Model; + +/** + * User Session + * + * @package model + * @author Frederic Guillot + */ +class UserSession extends Base +{ + /** + * Return true if the logged user is admin + * + * @access public + * @return bool + */ + public function isAdmin() + { + return isset($this->session['user']['is_admin']) && $this->session['user']['is_admin'] === true; + } + + /** + * Get the connected user id + * + * @access public + * @return integer + */ + public function getId() + { + return isset($this->session['user']['id']) ? (int) $this->session['user']['id'] : 0; + } + + /** + * Check if the given user_id is the connected user + * + * @param integer $user_id User id + * @return boolean + */ + public function isCurrentUser($user_id) + { + return $this->getId() == $user_id; + } + + /** + * Check is the user is connected + * + * @access public + * @return bool + */ + public function isLogged() + { + return ! empty($this->session['user']); + } + + /** + * Get the last seen project from the session + * + * @access public + * @return integer + */ + public function getLastSeenProjectId() + { + return empty($this->session['last_show_project_id']) ? 0 : $this->session['last_show_project_id']; + } + + /** + * Get the default project from the session + * + * @access public + * @return integer + */ + public function getFavoriteProjectId() + { + return isset($this->session['user']['default_project_id']) ? $this->session['user']['default_project_id'] : 0; + } + + /** + * Set the last seen project from the session + * + * @access public + * @param integer $project_id Project id + */ + public function storeLastSeenProjectId($project_id) + { + $this->session['last_show_project_id'] = (int) $project_id; + } +} |