summaryrefslogtreecommitdiff
path: root/app/Model
diff options
context:
space:
mode:
Diffstat (limited to 'app/Model')
-rw-r--r--app/Model/Acl.php182
-rw-r--r--app/Model/Authentication.php9
-rw-r--r--app/Model/Base.php1
-rw-r--r--app/Model/Notification.php2
-rw-r--r--app/Model/Project.php4
-rw-r--r--app/Model/ProjectPermission.php82
-rw-r--r--app/Model/SubTask.php1
-rw-r--r--app/Model/TaskExport.php1
-rw-r--r--app/Model/TaskPermission.php4
-rw-r--r--app/Model/TaskPosition.php1
-rw-r--r--app/Model/User.php43
-rw-r--r--app/Model/UserSession.php89
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;
+ }
+}