Rewrite of the authentication and authorization system
This commit is contained in:
@@ -1,289 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Model;
|
||||
|
||||
/**
|
||||
* Access List
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Acl extends Base
|
||||
{
|
||||
/**
|
||||
* Controllers and actions allowed from outside
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $public_acl = array(
|
||||
'auth' => array('login', 'check', 'captcha'),
|
||||
'task' => array('readonly'),
|
||||
'board' => array('readonly'),
|
||||
'webhook' => '*',
|
||||
'ical' => '*',
|
||||
'feed' => '*',
|
||||
'oauth' => array('google', 'github', 'gitlab'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Controllers and actions for project members
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $project_member_acl = array(
|
||||
'board' => '*',
|
||||
'comment' => '*',
|
||||
'file' => '*',
|
||||
'project' => array('show'),
|
||||
'listing' => '*',
|
||||
'activity' => '*',
|
||||
'subtask' => '*',
|
||||
'task' => '*',
|
||||
'taskduplication' => '*',
|
||||
'taskcreation' => '*',
|
||||
'taskmodification' => '*',
|
||||
'taskstatus' => '*',
|
||||
'tasklink' => '*',
|
||||
'timer' => '*',
|
||||
'customfilter' => '*',
|
||||
'calendar' => array('show', 'project'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Controllers and actions for project managers
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $project_manager_acl = array(
|
||||
'action' => '*',
|
||||
'analytic' => '*',
|
||||
'category' => '*',
|
||||
'column' => '*',
|
||||
'export' => '*',
|
||||
'taskimport' => '*',
|
||||
'project' => array('edit', 'update', 'share', 'integrations', 'notifications', 'users', 'alloweverybody', 'allow', 'setowner', 'revoke', 'duplicate', 'disable', 'enable'),
|
||||
'swimlane' => '*',
|
||||
'gantt' => array('project', 'savetaskdate', 'task', 'savetask'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Controllers and actions for project admins
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $project_admin_acl = array(
|
||||
'project' => array('remove'),
|
||||
'projectuser' => '*',
|
||||
'gantt' => array('projects', 'saveprojectdate'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Controllers and actions for admins
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $admin_acl = array(
|
||||
'user' => array('index', 'create', 'save', 'remove', 'authentication'),
|
||||
'userimport' => '*',
|
||||
'config' => '*',
|
||||
'link' => '*',
|
||||
'currency' => '*',
|
||||
'twofactor' => array('disable'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Extend ACL rules
|
||||
*
|
||||
* @access public
|
||||
* @param string $acl_name
|
||||
* @param aray $rules
|
||||
*/
|
||||
public function extend($acl_name, array $rules)
|
||||
{
|
||||
$this->$acl_name = array_merge($this->$acl_name, $rules);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 matchAcl(array $acl, $controller, $action)
|
||||
{
|
||||
$controller = strtolower($controller);
|
||||
$action = strtolower($action);
|
||||
return isset($acl[$controller]) && $this->hasAction($action, $acl[$controller]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the specified action is inside the list of actions
|
||||
*
|
||||
* @access public
|
||||
* @param string $action Action name
|
||||
* @param mixed $action Actions list
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAction($action, $actions)
|
||||
{
|
||||
if (is_array($actions)) {
|
||||
return in_array($action, $actions);
|
||||
}
|
||||
|
||||
return $actions === '*';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given action is public
|
||||
*
|
||||
* @access public
|
||||
* @param string $controller Controller name
|
||||
* @param string $action Action name
|
||||
* @return bool
|
||||
*/
|
||||
public function isPublicAction($controller, $action)
|
||||
{
|
||||
return $this->matchAcl($this->public_acl, $controller, $action);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 isAdminAction($controller, $action)
|
||||
{
|
||||
return $this->matchAcl($this->admin_acl, $controller, $action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given action is for project managers
|
||||
*
|
||||
* @access public
|
||||
* @param string $controller Controller name
|
||||
* @param string $action Action name
|
||||
* @return bool
|
||||
*/
|
||||
public function isProjectManagerAction($controller, $action)
|
||||
{
|
||||
return $this->matchAcl($this->project_manager_acl, $controller, $action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given action is for application managers
|
||||
*
|
||||
* @access public
|
||||
* @param string $controller Controller name
|
||||
* @param string $action Action name
|
||||
* @return bool
|
||||
*/
|
||||
public function isProjectAdminAction($controller, $action)
|
||||
{
|
||||
return $this->matchAcl($this->project_admin_acl, $controller, $action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given action is for project members
|
||||
*
|
||||
* @access public
|
||||
* @param string $controller Controller name
|
||||
* @param string $action Action name
|
||||
* @return bool
|
||||
*/
|
||||
public function isProjectMemberAction($controller, $action)
|
||||
{
|
||||
return $this->matchAcl($this->project_member_acl, $controller, $action);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 isAllowed($controller, $action, $project_id = 0)
|
||||
{
|
||||
// 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 admin permissions
|
||||
if ($this->isProjectAdminAction($controller, $action)) {
|
||||
return $this->handleProjectAdminPermissions($project_id);
|
||||
}
|
||||
|
||||
// Check project manager permissions
|
||||
if ($this->isProjectManagerAction($controller, $action)) {
|
||||
return $this->handleProjectManagerPermissions($project_id);
|
||||
}
|
||||
|
||||
// Check project member permissions
|
||||
if ($this->isProjectMemberAction($controller, $action)) {
|
||||
return $project_id > 0 && $this->projectPermission->isMember($project_id, $this->userSession->getId());
|
||||
}
|
||||
|
||||
// Other applications actions are allowed
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle permission for project manager
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function handleProjectManagerPermissions($project_id)
|
||||
{
|
||||
if ($project_id > 0) {
|
||||
if ($this->userSession->isProjectAdmin()) {
|
||||
return $this->projectPermission->isMember($project_id, $this->userSession->getId());
|
||||
}
|
||||
|
||||
return $this->projectPermission->isManager($project_id, $this->userSession->getId());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle permission for project admins
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function handleProjectAdminPermissions($project_id)
|
||||
{
|
||||
if (! $this->userSession->isProjectAdmin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($project_id > 0) {
|
||||
return $this->projectPermission->isMember($project_id, $this->userSession->getId());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace Kanboard\Model;
|
||||
|
||||
use Kanboard\Core\Http\Request;
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
use Gregwar\Captcha\CaptchaBuilder;
|
||||
@@ -15,113 +14,6 @@ use Gregwar\Captcha\CaptchaBuilder;
|
||||
*/
|
||||
class Authentication extends Base
|
||||
{
|
||||
/**
|
||||
* Load automatically an authentication backend
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Backend class name
|
||||
* @return mixed
|
||||
*/
|
||||
public function backend($name)
|
||||
{
|
||||
if (! isset($this->container[$name])) {
|
||||
$class = '\Kanboard\Auth\\'.ucfirst($name);
|
||||
$this->container[$name] = new $class($this->container);
|
||||
}
|
||||
|
||||
return $this->container[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current user is authenticated
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isAuthenticated()
|
||||
{
|
||||
// If the user is already logged it's ok
|
||||
if ($this->userSession->isLogged()) {
|
||||
|
||||
// Check if the user session match an existing user
|
||||
$userNotFound = ! $this->user->exists($this->userSession->getId());
|
||||
$reverseProxyWrongUser = REVERSE_PROXY_AUTH && $this->backend('reverseProxy')->getUsername() !== $this->userSession->getUsername();
|
||||
|
||||
if ($userNotFound || $reverseProxyWrongUser) {
|
||||
$this->backend('rememberMe')->destroy($this->userSession->getId());
|
||||
$this->sessionManager->close();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// We try first with the RememberMe cookie
|
||||
if (REMEMBER_ME_AUTH && $this->backend('rememberMe')->authenticate()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Then with the ReverseProxy authentication
|
||||
if (REVERSE_PROXY_AUTH && $this->backend('reverseProxy')->authenticate()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate a user by different methods
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Username
|
||||
* @param string $password Password
|
||||
* @return boolean
|
||||
*/
|
||||
public function authenticate($username, $password)
|
||||
{
|
||||
if ($this->user->isLocked($username)) {
|
||||
$this->container['logger']->error('Account locked: '.$username);
|
||||
return false;
|
||||
} elseif ($this->backend('database')->authenticate($username, $password)) {
|
||||
$this->user->resetFailedLogin($username);
|
||||
return true;
|
||||
} elseif (LDAP_AUTH && $this->backend('ldap')->authenticate($username, $password)) {
|
||||
$this->user->resetFailedLogin($username);
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->handleFailedLogin($username);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the captcha must be shown
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasCaptcha($username)
|
||||
{
|
||||
return $this->user->getFailedLogin($username) >= BRUTEFORCE_CAPTCHA;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle failed login
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
*/
|
||||
public function handleFailedLogin($username)
|
||||
{
|
||||
$this->user->incrementFailedLogin($username);
|
||||
|
||||
if ($this->user->getFailedLogin($username) >= BRUTEFORCE_LOCKDOWN) {
|
||||
$this->container['logger']->critical('Locking account: '.$username);
|
||||
$this->user->lock($username, BRUTEFORCE_LOCKDOWN_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate user login form
|
||||
*
|
||||
@@ -131,14 +23,14 @@ class Authentication extends Base
|
||||
*/
|
||||
public function validateForm(array $values)
|
||||
{
|
||||
list($result, $errors) = $this->validateFormCredentials($values);
|
||||
$result = false;
|
||||
$errors = array();
|
||||
|
||||
if ($result) {
|
||||
if ($this->validateFormCaptcha($values) && $this->authenticate($values['username'], $values['password'])) {
|
||||
$this->createRememberMeSession($values);
|
||||
} else {
|
||||
$result = false;
|
||||
$errors['login'] = t('Bad username or password');
|
||||
foreach (array('validateFields', 'validateLocking', 'validateCaptcha', 'validateCredentials') as $method) {
|
||||
list($result, $errors) = $this->$method($values);
|
||||
|
||||
if (! $result) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,11 +40,11 @@ class Authentication extends Base
|
||||
/**
|
||||
* Validate credentials syntax
|
||||
*
|
||||
* @access public
|
||||
* @access private
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateFormCredentials(array $values)
|
||||
private function validateFields(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('username', t('The username is required')),
|
||||
@@ -167,40 +59,72 @@ class Authentication extends Base
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate captcha
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return boolean
|
||||
*/
|
||||
public function validateFormCaptcha(array $values)
|
||||
{
|
||||
if ($this->hasCaptcha($values['username'])) {
|
||||
if (! isset($this->sessionStorage->captcha)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$builder = new CaptchaBuilder;
|
||||
$builder->setPhrase($this->sessionStorage->captcha);
|
||||
return $builder->testPhrase(isset($values['captcha']) ? $values['captcha'] : '');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create remember me session if necessary
|
||||
* Validate user locking
|
||||
*
|
||||
* @access private
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
private function createRememberMeSession(array $values)
|
||||
private function validateLocking(array $values)
|
||||
{
|
||||
if (REMEMBER_ME_AUTH && ! empty($values['remember_me'])) {
|
||||
$credentials = $this->backend('rememberMe')
|
||||
->create($this->userSession->getId(), Request::getIpAddress(), Request::getUserAgent());
|
||||
$result = true;
|
||||
$errors = array();
|
||||
|
||||
$this->backend('rememberMe')->writeCookie($credentials['token'], $credentials['sequence'], $credentials['expiration']);
|
||||
if ($this->userLocking->isLocked($values['username'])) {
|
||||
$result = false;
|
||||
$errors['login'] = t('Your account is locked for %d minutes', BRUTEFORCE_LOCKDOWN_DURATION);
|
||||
$this->logger->error('Account locked: '.$values['username']);
|
||||
}
|
||||
|
||||
return array($result, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate password syntax
|
||||
*
|
||||
* @access private
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
private function validateCredentials(array $values)
|
||||
{
|
||||
$result = true;
|
||||
$errors = array();
|
||||
|
||||
if (! $this->authenticationManager->passwordAuthentication($values['username'], $values['password'])) {
|
||||
$result = false;
|
||||
$errors['login'] = t('Bad username or password');
|
||||
}
|
||||
|
||||
return array($result, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate captcha
|
||||
*
|
||||
* @access private
|
||||
* @param array $values Form values
|
||||
* @return boolean
|
||||
*/
|
||||
private function validateCaptcha(array $values)
|
||||
{
|
||||
$result = true;
|
||||
$errors = array();
|
||||
|
||||
if ($this->userLocking->hasCaptcha($values['username'])) {
|
||||
if (! isset($this->sessionStorage->captcha)) {
|
||||
$result = false;
|
||||
} else {
|
||||
$builder = new CaptchaBuilder;
|
||||
$builder->setPhrase($this->sessionStorage->captcha);
|
||||
$result = $builder->testPhrase(isset($values['captcha']) ? $values['captcha'] : '');
|
||||
|
||||
if (! $result) {
|
||||
$errors['login'] = t('Invalid captcha');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array($result, $errors);;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,18 @@ class Group extends Base
|
||||
return $this->getQuery()->eq('id', $group_id)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific group by external id
|
||||
*
|
||||
* @access public
|
||||
* @param integer $external_id
|
||||
* @return array
|
||||
*/
|
||||
public function getByExternalId($external_id)
|
||||
{
|
||||
return $this->getQuery()->eq('external_id', $external_id)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all groups
|
||||
*
|
||||
@@ -54,6 +66,18 @@ class Group extends Base
|
||||
return $this->getQuery()->asc('name')->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search groups by name
|
||||
*
|
||||
* @access public
|
||||
* @param string $input
|
||||
* @return array
|
||||
*/
|
||||
public function search($input)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->ilike('name', '%'.$input.'%')->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a group
|
||||
*
|
||||
|
||||
@@ -65,8 +65,8 @@ class GroupMember extends Base
|
||||
* Add user to a group
|
||||
*
|
||||
* @access public
|
||||
* @param integer $group_id
|
||||
* @param integer $user_id
|
||||
* @param integer $group_id
|
||||
* @param integer $user_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function addUser($group_id, $user_id)
|
||||
@@ -81,8 +81,8 @@ class GroupMember extends Base
|
||||
* Remove user from a group
|
||||
*
|
||||
* @access public
|
||||
* @param integer $group_id
|
||||
* @param integer $user_id
|
||||
* @param integer $group_id
|
||||
* @param integer $user_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function removeUser($group_id, $user_id)
|
||||
@@ -92,4 +92,20 @@ class GroupMember extends Base
|
||||
->eq('user_id', $user_id)
|
||||
->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a user is member
|
||||
*
|
||||
* @access public
|
||||
* @param integer $group_id
|
||||
* @param integer $user_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function isMember($group_id, $user_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)
|
||||
->eq('group_id', $group_id)
|
||||
->eq('user_id', $user_id)
|
||||
->exists();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Kanboard\Model;
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
use Kanboard\Core\Security\Token;
|
||||
use Kanboard\Core\Security\Role;
|
||||
|
||||
/**
|
||||
* Project model
|
||||
@@ -287,7 +288,7 @@ class Project extends Base
|
||||
{
|
||||
foreach ($projects as &$project) {
|
||||
$this->getColumnStats($project);
|
||||
$project = array_merge($project, $this->projectPermission->getProjectUsers($project['id']));
|
||||
$project = array_merge($project, $this->projectUserRole->getAllUsersGroupedByRole($project['id']));
|
||||
}
|
||||
|
||||
return $projects;
|
||||
@@ -365,7 +366,7 @@ class Project extends Base
|
||||
}
|
||||
|
||||
if ($add_user && $user_id) {
|
||||
$this->projectPermission->addManager($project_id, $user_id);
|
||||
$this->projectUserRole->addUser($project_id, $user_id, Role::PROJECT_MANAGER);
|
||||
}
|
||||
|
||||
$this->category->createDefaultCategories($project_id);
|
||||
|
||||
@@ -56,7 +56,7 @@ class ProjectAnalytic extends Base
|
||||
$metrics = array();
|
||||
$total = 0;
|
||||
$tasks = $this->taskFinder->getAll($project_id);
|
||||
$users = $this->projectPermission->getMemberList($project_id);
|
||||
$users = $this->projectUserRole->getAssignableUsersList($project_id);
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
$user = isset($users[$task['owner_id']]) ? $users[$task['owner_id']] : $users[0];
|
||||
|
||||
187
app/Model/ProjectGroupRole.php
Normal file
187
app/Model/ProjectGroupRole.php
Normal file
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Model;
|
||||
|
||||
use Kanboard\Core\Security\Role;
|
||||
|
||||
/**
|
||||
* Project Group Role
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ProjectGroupRole extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'project_has_groups';
|
||||
|
||||
/**
|
||||
* Get the list of project visible by the given user according to groups
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
* @param array $status
|
||||
* @return array
|
||||
*/
|
||||
public function getProjectsByUser($user_id, $status = array(Project::ACTIVE, Project::INACTIVE))
|
||||
{
|
||||
return $this->db
|
||||
->hashtable(Project::TABLE)
|
||||
->join(self::TABLE, 'project_id', 'id')
|
||||
->join(GroupMember::TABLE, 'group_id', 'group_id', self::TABLE)
|
||||
->eq(GroupMember::TABLE.'.user_id', $user_id)
|
||||
->in(Project::TABLE.'.is_active', $status)
|
||||
->getAll(Project::TABLE.'.id', Project::TABLE.'.name');
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given project get the role of the specified user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param integer $user_id
|
||||
* @return string
|
||||
*/
|
||||
public function getUserRole($project_id, $user_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)
|
||||
->join(GroupMember::TABLE, 'group_id', 'group_id', self::TABLE)
|
||||
->eq(GroupMember::TABLE.'.user_id', $user_id)
|
||||
->eq(self::TABLE.'.project_id', $project_id)
|
||||
->findOneColumn('role');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all groups associated directly to the project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @return array
|
||||
*/
|
||||
public function getGroups($project_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)
|
||||
->columns(Group::TABLE.'.id', Group::TABLE.'.name', self::TABLE.'.role')
|
||||
->join(Group::TABLE, 'id', 'group_id')
|
||||
->eq('project_id', $project_id)
|
||||
->asc('name')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* From groups get all users associated to the project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @return array
|
||||
*/
|
||||
public function getUsers($project_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)
|
||||
->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name', self::TABLE.'.role')
|
||||
->join(GroupMember::TABLE, 'group_id', 'group_id', self::TABLE)
|
||||
->join(User::TABLE, 'id', 'user_id', GroupMember::TABLE)
|
||||
->eq(self::TABLE.'.project_id', $project_id)
|
||||
->asc(User::TABLE.'.username')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* From groups get all users assignable to tasks
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @return array
|
||||
*/
|
||||
public function getAssignableUsers($project_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)
|
||||
->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name')
|
||||
->join(GroupMember::TABLE, 'group_id', 'group_id', self::TABLE)
|
||||
->join(User::TABLE, 'id', 'user_id', GroupMember::TABLE)
|
||||
->eq(self::TABLE.'.project_id', $project_id)
|
||||
->in(self::TABLE.'.role', array(Role::PROJECT_MANAGER, Role::PROJECT_MEMBER))
|
||||
->asc(User::TABLE.'.username')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a group to the project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param integer $group_id
|
||||
* @param string $role
|
||||
* @return boolean
|
||||
*/
|
||||
public function addGroup($project_id, $group_id, $role)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->insert(array(
|
||||
'group_id' => $group_id,
|
||||
'project_id' => $project_id,
|
||||
'role' => $role,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a group from the project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param integer $group_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function removeGroup($project_id, $group_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('group_id', $group_id)->eq('project_id', $project_id)->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a group role for the project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param integer $group_id
|
||||
* @param string $role
|
||||
* @return boolean
|
||||
*/
|
||||
public function changeGroupRole($project_id, $group_id, $role)
|
||||
{
|
||||
return $this->db->table(self::TABLE)
|
||||
->eq('group_id', $group_id)
|
||||
->eq('project_id', $project_id)
|
||||
->update(array(
|
||||
'role' => $role,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy group access from a project to another one
|
||||
*
|
||||
* @param integer $project_src_id Project Template
|
||||
* @return integer $project_dst_id Project that receives the copy
|
||||
* @return boolean
|
||||
*/
|
||||
public function duplicate($project_src_id, $project_dst_id)
|
||||
{
|
||||
$rows = $this->db->table(self::TABLE)->eq('project_id', $project_src_id)->findAll();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$result = $this->db->table(self::TABLE)->save(array(
|
||||
'project_id' => $project_dst_id,
|
||||
'group_id' => $row['group_id'],
|
||||
'role' => $row['role'],
|
||||
));
|
||||
|
||||
if (! $result) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2,129 +2,25 @@
|
||||
|
||||
namespace Kanboard\Model;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
use Kanboard\Core\Security\Role;
|
||||
|
||||
/**
|
||||
* Project permission model
|
||||
* Project Permission
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ProjectPermission extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name for permissions
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'project_has_users';
|
||||
|
||||
/**
|
||||
* Get a list of people that can be assigned for tasks
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param bool $prepend_unassigned Prepend the 'Unassigned' value
|
||||
* @param bool $prepend_everybody Prepend the 'Everbody' value
|
||||
* @param bool $allow_single_user If there is only one user return only this user
|
||||
* @return array
|
||||
*/
|
||||
public function getMemberList($project_id, $prepend_unassigned = true, $prepend_everybody = false, $allow_single_user = false)
|
||||
{
|
||||
$allowed_users = $this->getMembers($project_id);
|
||||
|
||||
if ($allow_single_user && count($allowed_users) === 1) {
|
||||
return $allowed_users;
|
||||
}
|
||||
|
||||
if ($prepend_unassigned) {
|
||||
$allowed_users = array(t('Unassigned')) + $allowed_users;
|
||||
}
|
||||
|
||||
if ($prepend_everybody) {
|
||||
$allowed_users = array(User::EVERYBODY_ID => t('Everybody')) + $allowed_users;
|
||||
}
|
||||
|
||||
return $allowed_users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of members and managers with a single SQL query
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getProjectUsers($project_id)
|
||||
{
|
||||
$result = array(
|
||||
'managers' => array(),
|
||||
'members' => array(),
|
||||
);
|
||||
|
||||
$users = $this->db
|
||||
->table(self::TABLE)
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->eq('project_id', $project_id)
|
||||
->asc('username')
|
||||
->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name', self::TABLE.'.is_owner')
|
||||
->findAll();
|
||||
|
||||
foreach ($users as $user) {
|
||||
$key = $user['is_owner'] == 1 ? 'managers' : 'members';
|
||||
$result[$key][$user['id']] = $user['name'] ?: $user['username'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of allowed people for a project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getMembers($project_id)
|
||||
{
|
||||
if ($this->isEverybodyAllowed($project_id)) {
|
||||
return $this->user->getList();
|
||||
}
|
||||
|
||||
return $this->getAssociatedUsers($project_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of owners for a project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getManagers($project_id)
|
||||
{
|
||||
$users = $this->db
|
||||
->table(self::TABLE)
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->eq('project_id', $project_id)
|
||||
->eq('is_owner', 1)
|
||||
->asc('username')
|
||||
->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name')
|
||||
->findAll();
|
||||
|
||||
return $this->user->prepareList($users);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query for project users overview
|
||||
*
|
||||
* @access public
|
||||
* @param array $project_ids
|
||||
* @param integer $is_owner
|
||||
* @param string $role
|
||||
* @return \PicoDb\Table
|
||||
*/
|
||||
public function getQueryByRole(array $project_ids, $is_owner = 0)
|
||||
public function getQueryByRole(array $project_ids, $role)
|
||||
{
|
||||
if (empty($project_ids)) {
|
||||
$project_ids = array(-1);
|
||||
@@ -135,7 +31,7 @@ class ProjectPermission extends Base
|
||||
->table(self::TABLE)
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->join(Project::TABLE, 'id', 'project_id')
|
||||
->eq(self::TABLE.'.is_owner', $is_owner)
|
||||
->eq(self::TABLE.'.role', $role)
|
||||
->eq(Project::TABLE.'.is_private', 0)
|
||||
->in(Project::TABLE.'.id', $project_ids)
|
||||
->columns(
|
||||
@@ -147,172 +43,6 @@ class ProjectPermission extends Base
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of people associated to the project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getAssociatedUsers($project_id)
|
||||
{
|
||||
$users = $this->db
|
||||
->table(self::TABLE)
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->eq('project_id', $project_id)
|
||||
->asc('username')
|
||||
->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name')
|
||||
->findAll();
|
||||
|
||||
return $this->user->prepareList($users);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get allowed and not allowed users for a project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getAllUsers($project_id)
|
||||
{
|
||||
$users = array(
|
||||
'allowed' => array(),
|
||||
'not_allowed' => array(),
|
||||
'managers' => array(),
|
||||
);
|
||||
|
||||
$all_users = $this->user->getList();
|
||||
|
||||
$users['allowed'] = $this->getMembers($project_id);
|
||||
$users['managers'] = $this->getManagers($project_id);
|
||||
|
||||
foreach ($all_users as $user_id => $username) {
|
||||
if (! isset($users['allowed'][$user_id])) {
|
||||
$users['not_allowed'][$user_id] = $username;
|
||||
}
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new project member
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $user_id User id
|
||||
* @return bool
|
||||
*/
|
||||
public function addMember($project_id, $user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->save(array('project_id' => $project_id, 'user_id' => $user_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a member
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $user_id User id
|
||||
* @return bool
|
||||
*/
|
||||
public function revokeMember($project_id, $user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('user_id', $user_id)
|
||||
->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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 changeRole($project_id, $user_id, $is_owner)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('user_id', $user_id)
|
||||
->update(array('is_owner' => (int) $is_owner));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a specific user is member of a project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $user_id User id
|
||||
* @return bool
|
||||
*/
|
||||
public function isMember($project_id, $user_id)
|
||||
{
|
||||
if ($this->isEverybodyAllowed($project_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('user_id', $user_id)
|
||||
->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 isManager($project_id, $user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('is_owner', 1)
|
||||
->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a specific user is allowed to access to a given project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $user_id User id
|
||||
* @return bool
|
||||
*/
|
||||
public function isUserAllowed($project_id, $user_id)
|
||||
{
|
||||
return $project_id === 0 || $this->user->isAdmin($user_id) || $this->isMember($project_id, $user_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if everybody is allowed for the project
|
||||
*
|
||||
@@ -330,172 +60,59 @@ class ProjectPermission extends Base
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of allowed active projects for a given user
|
||||
* Return true if the user is allowed to access a project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowedProjects($user_id)
|
||||
{
|
||||
if ($this->user->isAdmin($user_id)) {
|
||||
return $this->project->getListByStatus(Project::ACTIVE);
|
||||
}
|
||||
|
||||
return $this->getActiveMemberProjects($user_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of projects where the user is member
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getMemberProjects($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->hashtable(Project::TABLE)
|
||||
->beginOr()
|
||||
->eq(self::TABLE.'.user_id', $user_id)
|
||||
->eq(Project::TABLE.'.is_everybody_allowed', 1)
|
||||
->closeOr()
|
||||
->join(self::TABLE, 'project_id', 'id')
|
||||
->getAll('projects.id', 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of project ids where the user is member
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getMemberProjectIds($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(Project::TABLE)
|
||||
->beginOr()
|
||||
->eq(self::TABLE.'.user_id', $user_id)
|
||||
->eq(Project::TABLE.'.is_everybody_allowed', 1)
|
||||
->closeOr()
|
||||
->join(self::TABLE, 'project_id', 'id')
|
||||
->findAllByColumn('projects.id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of active project ids where the user is member
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getActiveMemberProjectIds($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(Project::TABLE)
|
||||
->beginOr()
|
||||
->eq(self::TABLE.'.user_id', $user_id)
|
||||
->eq(Project::TABLE.'.is_everybody_allowed', 1)
|
||||
->closeOr()
|
||||
->eq(Project::TABLE.'.is_active', Project::ACTIVE)
|
||||
->join(self::TABLE, 'project_id', 'id')
|
||||
->findAllByColumn('projects.id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of active projects where the user is member
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getActiveMemberProjects($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->hashtable(Project::TABLE)
|
||||
->beginOr()
|
||||
->eq(self::TABLE.'.user_id', $user_id)
|
||||
->eq(Project::TABLE.'.is_everybody_allowed', 1)
|
||||
->closeOr()
|
||||
->eq(Project::TABLE.'.is_active', Project::ACTIVE)
|
||||
->join(self::TABLE, 'project_id', 'id')
|
||||
->getAll('projects.id', 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy user access from a project to another one
|
||||
*
|
||||
* @param integer $project_src Project Template
|
||||
* @return integer $project_dst Project that receives the copy
|
||||
* @param integer $project_id
|
||||
* @param integer $user_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function duplicate($project_src, $project_dst)
|
||||
public function isUserAllowed($project_id, $user_id)
|
||||
{
|
||||
$rows = $this->db
|
||||
->table(self::TABLE)
|
||||
->columns('project_id', 'user_id', 'is_owner')
|
||||
->eq('project_id', $project_src)
|
||||
->findAll();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$result = $this->db
|
||||
->table(self::TABLE)
|
||||
->save(array(
|
||||
'project_id' => $project_dst,
|
||||
'user_id' => $row['user_id'],
|
||||
'is_owner' => (int) $row['is_owner'], // (int) for postgres
|
||||
));
|
||||
|
||||
if (! $result) {
|
||||
return false;
|
||||
}
|
||||
if ($this->userSession->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate allow user
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateUserModification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('project_id', t('The project id is required')),
|
||||
new Validators\Integer('project_id', t('This value must be an integer')),
|
||||
new Validators\Required('user_id', t('The user id is required')),
|
||||
new Validators\Integer('user_id', t('This value must be an integer')),
|
||||
new Validators\Integer('is_owner', t('This value must be an integer')),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
return in_array(
|
||||
$this->projectUserRole->getUserRole($project_id, $user_id),
|
||||
array(Role::PROJECT_MANAGER, Role::PROJECT_MEMBER, Role::PROJECT_VIEWER)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate allow everybody
|
||||
* Return true if the user is assignable
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
* @param integer $project_id
|
||||
* @param integer $user_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function validateProjectModification(array $values)
|
||||
public function isMember($project_id, $user_id)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('id', t('The project id is required')),
|
||||
new Validators\Integer('id', t('This value must be an integer')),
|
||||
new Validators\Integer('is_everybody_allowed', t('This value must be an integer')),
|
||||
));
|
||||
return in_array($this->projectUserRole->getUSerRole($project_id, $user_id), array(Role::PROJECT_MEMBER, Role::PROJECT_MANAGER));
|
||||
}
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
/**
|
||||
* Get active project ids by user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
* @return array
|
||||
*/
|
||||
public function getActiveProjectIds($user_id)
|
||||
{
|
||||
return array_keys($this->projectUserRole->getProjectsByUser($user_id, array(Project::ACTIVE)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy permissions to another project
|
||||
*
|
||||
* @param integer $project_src_id Project Template
|
||||
* @param integer $project_dst_id Project that receives the copy
|
||||
* @return boolean
|
||||
*/
|
||||
public function duplicate($project_src_id, $project_dst_id)
|
||||
{
|
||||
return $this->projectUserRole->duplicate($project_src_id, $project_dst_id) &&
|
||||
$this->projectGroupRole->duplicate($project_src_id, $project_dst_id);
|
||||
}
|
||||
}
|
||||
|
||||
263
app/Model/ProjectUserRole.php
Normal file
263
app/Model/ProjectUserRole.php
Normal file
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Model;
|
||||
|
||||
use Kanboard\Core\Security\Role;
|
||||
|
||||
/**
|
||||
* Project User Role
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ProjectUserRole extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'project_has_users';
|
||||
|
||||
/**
|
||||
* Get the list of project visible by the given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id
|
||||
* @param array $status
|
||||
* @return array
|
||||
*/
|
||||
public function getProjectsByUser($user_id, $status = array(Project::ACTIVE, Project::INACTIVE))
|
||||
{
|
||||
$userProjects = $this->db
|
||||
->hashtable(Project::TABLE)
|
||||
->beginOr()
|
||||
->eq(self::TABLE.'.user_id', $user_id)
|
||||
->eq(Project::TABLE.'.is_everybody_allowed', 1)
|
||||
->closeOr()
|
||||
->in(Project::TABLE.'.is_active', $status)
|
||||
->join(self::TABLE, 'project_id', 'id')
|
||||
->getAll(Project::TABLE.'.id', Project::TABLE.'.name');
|
||||
|
||||
$groupProjects = $this->projectGroupRole->getProjectsByUser($user_id, $status);
|
||||
$groups = $userProjects + $groupProjects;
|
||||
|
||||
asort($groups);
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given project get the role of the specified user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param integer $user_id
|
||||
* @return string
|
||||
*/
|
||||
public function getUserRole($project_id, $user_id)
|
||||
{
|
||||
if ($this->projectPermission->isEverybodyAllowed($project_id)) {
|
||||
return Role::PROJECT_MEMBER;
|
||||
}
|
||||
|
||||
$role = $this->db->table(self::TABLE)->eq('user_id', $user_id)->eq('project_id', $project_id)->findOneColumn('role');
|
||||
|
||||
if (empty($role)) {
|
||||
$role = $this->projectGroupRole->getUserRole($project_id, $user_id);
|
||||
}
|
||||
|
||||
return $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all users associated directly to the project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @return array
|
||||
*/
|
||||
public function getUsers($project_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)
|
||||
->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name', self::TABLE.'.role')
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->eq('project_id', $project_id)
|
||||
->asc(User::TABLE.'.username')
|
||||
->asc(User::TABLE.'.name')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all users (fetch users from groups)
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @return array
|
||||
*/
|
||||
public function getAllUsers($project_id)
|
||||
{
|
||||
$userMembers = $this->getUsers($project_id);
|
||||
$groupMembers = $this->projectGroupRole->getUsers($project_id);
|
||||
$members = array_merge($userMembers, $groupMembers);
|
||||
|
||||
return $this->user->prepareList($members);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get users grouped by role
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getAllUsersGroupedByRole($project_id)
|
||||
{
|
||||
$users = array();
|
||||
|
||||
$userMembers = $this->getUsers($project_id);
|
||||
$groupMembers = $this->projectGroupRole->getUsers($project_id);
|
||||
$members = array_merge($userMembers, $groupMembers);
|
||||
|
||||
foreach ($members as $user) {
|
||||
if (! isset($users[$user['role']])) {
|
||||
$users[$user['role']] = array();
|
||||
}
|
||||
|
||||
$users[$user['role']][$user['id']] = $user['name'] ?: $user['username'];
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of users that can be assigned to a task (only Manager and Member)
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @return array
|
||||
*/
|
||||
public function getAssignableUsers($project_id)
|
||||
{
|
||||
if ($this->projectPermission->isEverybodyAllowed($project_id)) {
|
||||
return $this->user->getList();
|
||||
}
|
||||
|
||||
$userMembers = $this->db->table(self::TABLE)
|
||||
->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name')
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->eq('project_id', $project_id)
|
||||
->in(self::TABLE.'.role', array(Role::PROJECT_MANAGER, Role::PROJECT_MEMBER))
|
||||
->findAll();
|
||||
|
||||
$groupMembers = $this->projectGroupRole->getAssignableUsers($project_id);
|
||||
$members = array_merge($userMembers, $groupMembers);
|
||||
|
||||
return $this->user->prepareList($members);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of users that can be assigned to a task (only Manager and Member)
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param bool $unassigned Prepend the 'Unassigned' value
|
||||
* @param bool $everybody Prepend the 'Everbody' value
|
||||
* @param bool $singleUser If there is only one user return only this user
|
||||
* @return array
|
||||
*/
|
||||
public function getAssignableUsersList($project_id, $unassigned = true, $everybody = false, $singleUser = false)
|
||||
{
|
||||
$users = $this->getAssignableUsers($project_id);
|
||||
|
||||
if ($singleUser && count($users) === 1) {
|
||||
return $users;
|
||||
}
|
||||
|
||||
if ($unassigned) {
|
||||
$users = array(t('Unassigned')) + $users;
|
||||
}
|
||||
|
||||
if ($everybody) {
|
||||
$users = array(User::EVERYBODY_ID => t('Everybody')) + $users;
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a user to the project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param integer $user_id
|
||||
* @param string $role
|
||||
* @return boolean
|
||||
*/
|
||||
public function addUser($project_id, $user_id, $role)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->insert(array(
|
||||
'user_id' => $user_id,
|
||||
'project_id' => $project_id,
|
||||
'role' => $role,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a user from the project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param integer $user_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function removeUser($project_id, $user_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('user_id', $user_id)->eq('project_id', $project_id)->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a user role for the project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param integer $user_id
|
||||
* @param string $role
|
||||
* @return boolean
|
||||
*/
|
||||
public function changeUserRole($project_id, $user_id, $role)
|
||||
{
|
||||
return $this->db->table(self::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('project_id', $project_id)
|
||||
->update(array(
|
||||
'role' => $role,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy user access from a project to another one
|
||||
*
|
||||
* @param integer $project_src_id Project Template
|
||||
* @return integer $project_dst_id Project that receives the copy
|
||||
* @return boolean
|
||||
*/
|
||||
public function duplicate($project_src_id, $project_dst_id)
|
||||
{
|
||||
$rows = $this->db->table(self::TABLE)->eq('project_id', $project_src_id)->findAll();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$result = $this->db->table(self::TABLE)->save(array(
|
||||
'project_id' => $project_dst_id,
|
||||
'user_id' => $row['user_id'],
|
||||
'role' => $row['role'],
|
||||
));
|
||||
|
||||
if (! $result) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
151
app/Model/RememberMeSession.php
Normal file
151
app/Model/RememberMeSession.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Model;
|
||||
|
||||
use Kanboard\Core\Security\Token;
|
||||
|
||||
/**
|
||||
* Remember Me Model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class RememberMeSession extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'remember_me';
|
||||
|
||||
/**
|
||||
* Expiration (60 days)
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const EXPIRATION = 5184000;
|
||||
|
||||
/**
|
||||
* Get a remember me record
|
||||
*
|
||||
* @access public
|
||||
* @param $token
|
||||
* @param $sequence
|
||||
* @return mixed
|
||||
*/
|
||||
public function find($token, $sequence)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('token', $token)
|
||||
->eq('sequence', $sequence)
|
||||
->gt('expiration', time())
|
||||
->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all sessions for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getAll($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->desc('date_creation')
|
||||
->columns('id', 'ip', 'user_agent', 'date_creation', 'expiration')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RememberMe session
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param string $ip IP Address
|
||||
* @param string $user_agent User Agent
|
||||
* @return array
|
||||
*/
|
||||
public function create($user_id, $ip, $user_agent)
|
||||
{
|
||||
$token = hash('sha256', $user_id.$user_agent.$ip.Token::getToken());
|
||||
$sequence = Token::getToken();
|
||||
$expiration = time() + self::EXPIRATION;
|
||||
|
||||
$this->cleanup($user_id);
|
||||
|
||||
$this
|
||||
->db
|
||||
->table(self::TABLE)
|
||||
->insert(array(
|
||||
'user_id' => $user_id,
|
||||
'ip' => $ip,
|
||||
'user_agent' => $user_agent,
|
||||
'token' => $token,
|
||||
'sequence' => $sequence,
|
||||
'expiration' => $expiration,
|
||||
'date_creation' => time(),
|
||||
));
|
||||
|
||||
return array(
|
||||
'token' => $token,
|
||||
'sequence' => $sequence,
|
||||
'expiration' => $expiration,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a session record
|
||||
*
|
||||
* @access public
|
||||
* @param integer $session_id Session id
|
||||
* @return mixed
|
||||
*/
|
||||
public function remove($session_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $session_id)
|
||||
->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove old sessions for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return bool
|
||||
*/
|
||||
public function cleanup($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->lt('expiration', time())
|
||||
->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new sequence token and update the database
|
||||
*
|
||||
* @access public
|
||||
* @param string $token Session token
|
||||
* @return string
|
||||
*/
|
||||
public function updateSequence($token)
|
||||
{
|
||||
$sequence = Token::getToken();
|
||||
|
||||
$this
|
||||
->db
|
||||
->table(self::TABLE)
|
||||
->eq('token', $token)
|
||||
->update(array('sequence' => $sequence));
|
||||
|
||||
return $sequence;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Kanboard\Model;
|
||||
|
||||
use Kanboard\Core\Security\Role;
|
||||
|
||||
/**
|
||||
* Task permission model
|
||||
*
|
||||
@@ -20,7 +22,7 @@ class TaskPermission extends Base
|
||||
*/
|
||||
public function canRemoveTask(array $task)
|
||||
{
|
||||
if ($this->userSession->isAdmin() || $this->projectPermission->isManager($task['project_id'], $this->userSession->getId())) {
|
||||
if ($this->userSession->isAdmin() || $this->projectUserRole->getUserRole($task['project_id'], $this->userSession->getId()) === Role::PROJECT_MANAGER) {
|
||||
return true;
|
||||
} elseif (isset($task['creator_id']) && $task['creator_id'] == $this->userSession->getId()) {
|
||||
return true;
|
||||
|
||||
@@ -7,6 +7,7 @@ use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
use Kanboard\Core\Session\SessionManager;
|
||||
use Kanboard\Core\Security\Token;
|
||||
use Kanboard\Core\Security\Role;
|
||||
|
||||
/**
|
||||
* User model
|
||||
@@ -57,8 +58,7 @@ class User extends Base
|
||||
'username',
|
||||
'name',
|
||||
'email',
|
||||
'is_admin',
|
||||
'is_project_admin',
|
||||
'role',
|
||||
'is_ldap_user',
|
||||
'notifications_enabled',
|
||||
'google_id',
|
||||
@@ -91,7 +91,7 @@ class User extends Base
|
||||
$this->db
|
||||
->table(User::TABLE)
|
||||
->eq('id', $user_id)
|
||||
->eq('is_admin', 1)
|
||||
->eq('role', Role::APP_ADMIN)
|
||||
->exists();
|
||||
}
|
||||
|
||||
@@ -111,48 +111,17 @@ class User extends Base
|
||||
* Get a specific user by the Google id
|
||||
*
|
||||
* @access public
|
||||
* @param string $google_id Google unique id
|
||||
* @param string $column
|
||||
* @param string $id
|
||||
* @return array|boolean
|
||||
*/
|
||||
public function getByGoogleId($google_id)
|
||||
public function getByExternalId($column, $id)
|
||||
{
|
||||
if (empty($google_id)) {
|
||||
if (empty($id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->db->table(self::TABLE)->eq('google_id', $google_id)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific user by the Github id
|
||||
*
|
||||
* @access public
|
||||
* @param string $github_id Github user id
|
||||
* @return array|boolean
|
||||
*/
|
||||
public function getByGithubId($github_id)
|
||||
{
|
||||
if (empty($github_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->db->table(self::TABLE)->eq('github_id', $github_id)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific user by the Gitlab id
|
||||
*
|
||||
* @access public
|
||||
* @param string $gitlab_id Gitlab user id
|
||||
* @return array|boolean
|
||||
*/
|
||||
public function getByGitlabId($gitlab_id)
|
||||
{
|
||||
if (empty($gitlab_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->db->table(self::TABLE)->eq('gitlab_id', $gitlab_id)->findOne();
|
||||
return $this->db->table(self::TABLE)->eq($column, $id)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -289,7 +258,7 @@ class User extends Base
|
||||
}
|
||||
|
||||
$this->removeFields($values, array('confirmation', 'current_password'));
|
||||
$this->resetFields($values, array('is_admin', 'is_ldap_user', 'is_project_admin', 'disable_login_form'));
|
||||
$this->resetFields($values, array('is_ldap_user', 'disable_login_form'));
|
||||
$this->convertNullFields($values, array('gitlab_id'));
|
||||
$this->convertIntegerFields($values, array('gitlab_id'));
|
||||
}
|
||||
@@ -355,10 +324,10 @@ class User extends Base
|
||||
|
||||
// All private projects are removed
|
||||
$project_ids = $db->table(Project::TABLE)
|
||||
->eq('is_private', 1)
|
||||
->eq(ProjectPermission::TABLE.'.user_id', $user_id)
|
||||
->join(ProjectPermission::TABLE, 'project_id', 'id')
|
||||
->findAllByColumn(Project::TABLE.'.id');
|
||||
->eq('is_private', 1)
|
||||
->eq(ProjectUserRole::TABLE.'.user_id', $user_id)
|
||||
->join(ProjectUserRole::TABLE, 'project_id', 'id')
|
||||
->findAllByColumn(Project::TABLE.'.id');
|
||||
|
||||
if (! empty($project_ids)) {
|
||||
$db->table(Project::TABLE)->in('id', $project_ids)->remove();
|
||||
@@ -401,71 +370,6 @@ class User extends Base
|
||||
->save(array('token' => ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of failed login for the user
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @return integer
|
||||
*/
|
||||
public function getFailedLogin($username)
|
||||
{
|
||||
return (int) $this->db->table(self::TABLE)->eq('username', $username)->findOneColumn('nb_failed_login');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to 0 the counter of failed login
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @return boolean
|
||||
*/
|
||||
public function resetFailedLogin($username)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('username', $username)->update(array('nb_failed_login' => 0, 'lock_expiration_date' => 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment failed login counter
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @return boolean
|
||||
*/
|
||||
public function incrementFailedLogin($username)
|
||||
{
|
||||
return $this->db->execute('UPDATE '.self::TABLE.' SET nb_failed_login=nb_failed_login+1 WHERE username=?', array($username)) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the account is locked
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @return boolean
|
||||
*/
|
||||
public function isLocked($username)
|
||||
{
|
||||
return $this->db->table(self::TABLE)
|
||||
->eq('username', $username)
|
||||
->neq('lock_expiration_date', 0)
|
||||
->gte('lock_expiration_date', time())
|
||||
->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock the account for the specified duration
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Username
|
||||
* @param integer $duration Duration in minutes
|
||||
* @return boolean
|
||||
*/
|
||||
public function lock($username, $duration = 15)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('username', $username)->update(array('lock_expiration_date' => time() + $duration * 60));
|
||||
}
|
||||
|
||||
/**
|
||||
* Common validation rules
|
||||
*
|
||||
@@ -475,11 +379,10 @@ class User extends Base
|
||||
private function commonValidationRules()
|
||||
{
|
||||
return array(
|
||||
new Validators\MaxLength('role', t('The maximum length is %d characters', 25), 25),
|
||||
new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), self::TABLE, 'id'),
|
||||
new Validators\Email('email', t('Email address invalid')),
|
||||
new Validators\Integer('is_admin', t('This value must be an integer')),
|
||||
new Validators\Integer('is_project_admin', t('This value must be an integer')),
|
||||
new Validators\Integer('is_ldap_user', t('This value must be an integer')),
|
||||
);
|
||||
}
|
||||
@@ -585,9 +488,7 @@ class User extends Base
|
||||
$v = new Validator($values, array_merge($rules, $this->commonPasswordValidationRules()));
|
||||
|
||||
if ($v->execute()) {
|
||||
|
||||
// Check password
|
||||
if ($this->authentication->authenticate($this->userSession->getUsername(), $values['current_password'])) {
|
||||
if ($this->authenticationManager->passwordAuthentication($this->userSession->getUsername(), $values['current_password'], false)) {
|
||||
return array(true, array());
|
||||
} else {
|
||||
return array(false, array('current_password' => array(t('Wrong password'))));
|
||||
|
||||
80
app/Model/UserFilter.php
Normal file
80
app/Model/UserFilter.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Model;
|
||||
|
||||
/**
|
||||
* User Filter
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class UserFilter extends Base
|
||||
{
|
||||
/**
|
||||
* Search query
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $input;
|
||||
|
||||
/**
|
||||
* Query
|
||||
*
|
||||
* @access protected
|
||||
* @var \PicoDb\Table
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* Initialize filter
|
||||
*
|
||||
* @access public
|
||||
* @param string $input
|
||||
* @return UserFilter
|
||||
*/
|
||||
public function create($input)
|
||||
{
|
||||
$this->query = $this->db->table(User::TABLE);
|
||||
$this->input = $input;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter users by name or username
|
||||
*
|
||||
* @access public
|
||||
* @return UserFilter
|
||||
*/
|
||||
public function filterByUsernameOrByName()
|
||||
{
|
||||
$this->query->beginOr()
|
||||
->ilike('username', '%'.$this->input.'%')
|
||||
->ilike('name', '%'.$this->input.'%')
|
||||
->closeOr();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all results of the filter
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function findAll()
|
||||
{
|
||||
return $this->query->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the PicoDb query
|
||||
*
|
||||
* @access public
|
||||
* @return \PicoDb\Table
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ namespace Kanboard\Model;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
use Kanboard\Core\Security\Role;
|
||||
use Kanboard\Core\Csv;
|
||||
|
||||
/**
|
||||
@@ -36,7 +37,7 @@ class UserImport extends Base
|
||||
'email' => 'Email',
|
||||
'name' => 'Full Name',
|
||||
'is_admin' => 'Administrator',
|
||||
'is_project_admin' => 'Project Administrator',
|
||||
'is_manager' => 'Manager',
|
||||
'is_ldap_user' => 'Remote User',
|
||||
);
|
||||
}
|
||||
@@ -75,10 +76,21 @@ class UserImport extends Base
|
||||
{
|
||||
$row['username'] = strtolower($row['username']);
|
||||
|
||||
foreach (array('is_admin', 'is_project_admin', 'is_ldap_user') as $field) {
|
||||
foreach (array('is_admin', 'is_manager', 'is_ldap_user') as $field) {
|
||||
$row[$field] = Csv::getBooleanValue($row[$field]);
|
||||
}
|
||||
|
||||
if ($row['is_admin'] == 1) {
|
||||
$row['role'] = Role::APP_ADMIN;
|
||||
} elseif ($row['is_manager'] == 1) {
|
||||
$row['role'] = Role::APP_MANAGER;
|
||||
} else {
|
||||
$row['role'] = Role::APP_USER;
|
||||
}
|
||||
|
||||
unset($row['is_admin']);
|
||||
unset($row['is_manager']);
|
||||
|
||||
$this->removeEmptyFields($row, array('password', 'email', 'name'));
|
||||
|
||||
return $row;
|
||||
@@ -98,8 +110,6 @@ class UserImport extends Base
|
||||
new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), User::TABLE, 'id'),
|
||||
new Validators\MinLength('password', t('The minimum length is %d characters', 6), 6),
|
||||
new Validators\Email('email', t('Email address invalid')),
|
||||
new Validators\Integer('is_admin', t('This value must be an integer')),
|
||||
new Validators\Integer('is_project_admin', t('This value must be an integer')),
|
||||
new Validators\Integer('is_ldap_user', t('This value must be an integer')),
|
||||
));
|
||||
|
||||
|
||||
103
app/Model/UserLocking.php
Normal file
103
app/Model/UserLocking.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Model;
|
||||
|
||||
/**
|
||||
* User Locking Model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class UserLocking extends Base
|
||||
{
|
||||
/**
|
||||
* Get the number of failed login for the user
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @return integer
|
||||
*/
|
||||
public function getFailedLogin($username)
|
||||
{
|
||||
return (int) $this->db->table(User::TABLE)
|
||||
->eq('username', $username)
|
||||
->findOneColumn('nb_failed_login');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to 0 the counter of failed login
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @return boolean
|
||||
*/
|
||||
public function resetFailedLogin($username)
|
||||
{
|
||||
return $this->db->table(User::TABLE)
|
||||
->eq('username', $username)
|
||||
->update(array(
|
||||
'nb_failed_login' => 0,
|
||||
'lock_expiration_date' => 0,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment failed login counter
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @return boolean
|
||||
*/
|
||||
public function incrementFailedLogin($username)
|
||||
{
|
||||
return $this->db->table(User::TABLE)
|
||||
->eq('username', $username)
|
||||
->increment('nb_failed_login', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the account is locked
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @return boolean
|
||||
*/
|
||||
public function isLocked($username)
|
||||
{
|
||||
return $this->db->table(User::TABLE)
|
||||
->eq('username', $username)
|
||||
->neq('lock_expiration_date', 0)
|
||||
->gte('lock_expiration_date', time())
|
||||
->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock the account for the specified duration
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Username
|
||||
* @param integer $duration Duration in minutes
|
||||
* @return boolean
|
||||
*/
|
||||
public function lock($username, $duration = 15)
|
||||
{
|
||||
return $this->db->table(User::TABLE)
|
||||
->eq('username', $username)
|
||||
->update(array(
|
||||
'lock_expiration_date' => time() + $duration * 60
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the captcha must be shown
|
||||
*
|
||||
* @access public
|
||||
* @param string $username
|
||||
* @param integer $tries
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasCaptcha($username, $tries = BRUTEFORCE_CAPTCHA)
|
||||
{
|
||||
return $this->getFailedLogin($username) >= $tries;
|
||||
}
|
||||
}
|
||||
@@ -155,7 +155,7 @@ class UserNotification extends Base
|
||||
private function getProjectMembersWithNotificationEnabled($project_id, $exclude_user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(ProjectPermission::TABLE)
|
||||
->table(ProjectUserRole::TABLE)
|
||||
->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name', User::TABLE.'.email', User::TABLE.'.language', User::TABLE.'.notifications_filter')
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->eq('project_id', $project_id)
|
||||
|
||||
@@ -1,195 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Model;
|
||||
|
||||
/**
|
||||
* User Session
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class UserSession extends Base
|
||||
{
|
||||
/**
|
||||
* Update user session
|
||||
*
|
||||
* @access public
|
||||
* @param array $user
|
||||
*/
|
||||
public function initialize(array $user)
|
||||
{
|
||||
if (isset($user['password'])) {
|
||||
unset($user['password']);
|
||||
}
|
||||
|
||||
if (isset($user['twofactor_secret'])) {
|
||||
unset($user['twofactor_secret']);
|
||||
}
|
||||
|
||||
$user['id'] = (int) $user['id'];
|
||||
$user['is_admin'] = isset($user['is_admin']) ? (bool) $user['is_admin'] : false;
|
||||
$user['is_project_admin'] = isset($user['is_project_admin']) ? (bool) $user['is_project_admin'] : false;
|
||||
$user['is_ldap_user'] = isset($user['is_ldap_user']) ? (bool) $user['is_ldap_user'] : false;
|
||||
$user['twofactor_activated'] = isset($user['twofactor_activated']) ? (bool) $user['twofactor_activated'] : false;
|
||||
|
||||
$this->sessionStorage->user = $user;
|
||||
$this->sessionStorage->postAuth = array('validated' => false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the user has validated the 2FA key
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function check2FA()
|
||||
{
|
||||
return isset($this->sessionStorage->postAuth['validated']) && $this->sessionStorage->postAuth['validated'] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the user has 2FA enabled
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function has2FA()
|
||||
{
|
||||
return isset($this->sessionStorage->user['twofactor_activated']) && $this->sessionStorage->user['twofactor_activated'] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable 2FA for the current session
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function disable2FA()
|
||||
{
|
||||
$this->sessionStorage->user['twofactor_activated'] = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the logged user is admin
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isAdmin()
|
||||
{
|
||||
return isset($this->sessionStorage->user['is_admin']) && $this->sessionStorage->user['is_admin'] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the logged user is project admin
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isProjectAdmin()
|
||||
{
|
||||
return isset($this->sessionStorage->user['is_project_admin']) && $this->sessionStorage->user['is_project_admin'] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the connected user id
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return isset($this->sessionStorage->user['id']) ? (int) $this->sessionStorage->user['id'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get username
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return isset($this->sessionStorage->user['username']) ? $this->sessionStorage->user['username'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check is the user is connected
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isLogged()
|
||||
{
|
||||
return isset($this->sessionStorage->user) && ! empty($this->sessionStorage->user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project filters from the session
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @return string
|
||||
*/
|
||||
public function getFilters($project_id)
|
||||
{
|
||||
return ! empty($this->sessionStorage->filters[$project_id]) ? $this->sessionStorage->filters[$project_id] : 'status:open';
|
||||
}
|
||||
|
||||
/**
|
||||
* Save project filters in the session
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param string $filters
|
||||
*/
|
||||
public function setFilters($project_id, $filters)
|
||||
{
|
||||
$this->sessionStorage->filters[$project_id] = $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is board collapsed or expanded
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function isBoardCollapsed($project_id)
|
||||
{
|
||||
return ! empty($this->sessionStorage->boardCollapsed[$project_id]) ? $this->sessionStorage->boardCollapsed[$project_id] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set board display mode
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param boolean $is_collapsed
|
||||
*/
|
||||
public function setBoardDisplayMode($project_id, $is_collapsed)
|
||||
{
|
||||
$this->sessionStorage->boardCollapsed[$project_id] = $is_collapsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set comments sorting
|
||||
*
|
||||
* @access public
|
||||
* @param string $order
|
||||
*/
|
||||
public function setCommentSorting($order)
|
||||
{
|
||||
$this->sessionStorage->commentSorting = $order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get comments sorting direction
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getCommentSorting()
|
||||
{
|
||||
return empty($this->sessionStorage->commentSorting) ? 'ASC' : $this->sessionStorage->commentSorting;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user