Add project restrictions for custom roles

This commit is contained in:
Frederic Guillot 2016-09-11 16:08:03 -04:00
parent a0227cad69
commit d8f6d85683
No known key found for this signature in database
GPG Key ID: 92D77191BA7FBC99
25 changed files with 700 additions and 288 deletions

View File

@ -28,14 +28,8 @@ class BoardAjaxController extends BaseController
} }
$values = $this->request->getJson(); $values = $this->request->getJson();
$canMoveTask = $this->columnMoveRestrictionModel->isAllowed(
$project_id,
$this->helper->user->getProjectUserRole($project_id),
$values['src_column_id'],
$values['dst_column_id']
);
if (! $canMoveTask) { if (! $this->helper->projectRole->canMoveTask($project_id, $values['src_column_id'], $values['dst_column_id'])) {
throw new AccessForbiddenException(e("You don't have the permission to move this task")); throw new AccessForbiddenException(e("You don't have the permission to move this task"));
} }

View File

@ -45,14 +45,14 @@ class ColumnMoveRestrictionController extends BaseController
list($valid, $errors) = $this->columnMoveRestrictionValidator->validateCreation($values); list($valid, $errors) = $this->columnMoveRestrictionValidator->validateCreation($values);
if ($valid) { if ($valid) {
$role_id = $this->columnMoveRestrictionModel->create( $restriction_id = $this->columnMoveRestrictionModel->create(
$project['id'], $project['id'],
$values['role_id'], $values['role_id'],
$values['src_column_id'], $values['src_column_id'],
$values['dst_column_id'] $values['dst_column_id']
); );
if ($role_id !== false) { if ($restriction_id !== false) {
$this->flash->success(t('The column restriction has been created successfully.')); $this->flash->success(t('The column restriction has been created successfully.'));
} else { } else {
$this->flash->failure(t('Unable to create this column restriction.')); $this->flash->failure(t('Unable to create this column restriction.'));

View File

@ -0,0 +1,96 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
/**
* Class ProjectRoleRestrictionController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ProjectRoleRestrictionController extends BaseController
{
/**
* Show form to create a new project restriction
*
* @param array $values
* @param array $errors
* @throws AccessForbiddenException
*/
public function create(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$role_id = $this->request->getIntegerParam('role_id');
$role = $this->projectRoleModel->getById($project['id'], $role_id);
$this->response->html($this->template->render('project_role_restriction/create', array(
'project' => $project,
'role' => $role,
'values' => $values + array('project_id' => $project['id'], 'role_id' => $role['role_id']),
'errors' => $errors,
'restrictions' => $this->projectRoleRestrictionModel->getRules(),
)));
}
/**
* Save new restriction
*/
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
$restriction_id = $this->projectRoleRestrictionModel->create(
$project['id'],
$values['role_id'],
$values['rule']
);
if ($restriction_id !== false) {
$this->flash->success(t('The project restriction has been created successfully.'));
} else {
$this->flash->failure(t('Unable to create this project restriction.'));
}
$this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id'])));
}
/**
* Confirm suppression
*
* @access public
*/
public function confirm()
{
$project = $this->getProject();
$restriction_id = $this->request->getIntegerParam('restriction_id');
$this->response->html($this->helper->layout->project('project_role_restriction/remove', array(
'project' => $project,
'restriction' => $this->projectRoleRestrictionModel->getById($project['id'], $restriction_id),
'restrictions' => $this->projectRoleRestrictionModel->getRules(),
)));
}
/**
* Remove a restriction
*
* @access public
*/
public function remove()
{
$project = $this->getProject();
$this->checkCSRFParam();
$restriction_id = $this->request->getIntegerParam('restriction_id');
if ($this->projectRoleRestrictionModel->remove($restriction_id)) {
$this->flash->success(t('Project restriction removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this restriction.'));
}
$this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id'])));
}
}

View File

@ -19,7 +19,7 @@ class TaskSuppressionController extends BaseController
{ {
$task = $this->getTask(); $task = $this->getTask();
if (! $this->helper->user->canRemoveTask($task)) { if (! $this->helper->projectRole->canRemoveTask($task)) {
throw new AccessForbiddenException(); throw new AccessForbiddenException();
} }
@ -37,7 +37,7 @@ class TaskSuppressionController extends BaseController
$task = $this->getTask(); $task = $this->getTask();
$this->checkCSRFParam(); $this->checkCSRFParam();
if (! $this->helper->user->canRemoveTask($task)) { if (! $this->helper->projectRole->canRemoveTask($task)) {
throw new AccessForbiddenException(); throw new AccessForbiddenException();
} }

View File

@ -91,6 +91,7 @@ use Pimple\Container;
* @property \Kanboard\Model\ProjectNotificationModel $projectNotificationModel * @property \Kanboard\Model\ProjectNotificationModel $projectNotificationModel
* @property \Kanboard\Model\ProjectNotificationTypeModel $projectNotificationTypeModel * @property \Kanboard\Model\ProjectNotificationTypeModel $projectNotificationTypeModel
* @property \Kanboard\Model\ProjectRoleModel $projectRoleModel * @property \Kanboard\Model\ProjectRoleModel $projectRoleModel
* @property \Kanboard\Model\ProjectRoleRestrictionModel $projectRoleRestrictionModel
* @property \Kanboard\Model\ProjectTaskDuplicationModel $projectTaskDuplicationModel * @property \Kanboard\Model\ProjectTaskDuplicationModel $projectTaskDuplicationModel
* @property \Kanboard\Model\ProjectTaskPriorityModel $projectTaskPriorityModel * @property \Kanboard\Model\ProjectTaskPriorityModel $projectTaskPriorityModel
* @property \Kanboard\Model\RememberMeSessionModel $rememberMeSessionModel * @property \Kanboard\Model\RememberMeSessionModel $rememberMeSessionModel

View File

@ -26,6 +26,7 @@ use Pimple\Container;
* @property \Kanboard\Helper\UrlHelper $url * @property \Kanboard\Helper\UrlHelper $url
* @property \Kanboard\Helper\UserHelper $user * @property \Kanboard\Helper\UserHelper $user
* @property \Kanboard\Helper\LayoutHelper $layout * @property \Kanboard\Helper\LayoutHelper $layout
* @property \Kanboard\Helper\ProjectRoleHelper $projectRole
* @property \Kanboard\Helper\ProjectHeaderHelper $projectHeader * @property \Kanboard\Helper\ProjectHeaderHelper $projectHeader
* @property \Kanboard\Helper\ProjectActivityHelper $projectActivity * @property \Kanboard\Helper\ProjectActivityHelper $projectActivity
* @property \Kanboard\Helper\MailHelper $mail * @property \Kanboard\Helper\MailHelper $mail

View File

@ -81,7 +81,7 @@ class BoardTaskFormatter extends BaseFormatter implements FormatterInterface
array_merge_relation($tasks, $this->tags, 'tags', 'id'); array_merge_relation($tasks, $this->tags, 'tags', 'id');
foreach ($tasks as &$task) { foreach ($tasks as &$task) {
$task['is_draggable'] = $this->helper->board->isDraggable($task); $task['is_draggable'] = $this->helper->projectRole->isDraggable($task);
} }
return $tasks; return $tasks;

View File

@ -24,26 +24,4 @@ class BoardHelper extends Base
{ {
return $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_BOARD_COLLAPSED.$project_id, 0) == 1; return $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_BOARD_COLLAPSED.$project_id, 0) == 1;
} }
/**
* Return true if the task can be moved by the connected user
*
* @param array $task
* @return bool
*/
public function isDraggable(array $task)
{
if ($task['is_active'] == 1 && $this->helper->user->hasProjectAccess('BoardViewController', 'save', $task['project_id'])) {
$role = $this->helper->user->getProjectUserRole($task['project_id']);
if ($this->role->isCustomProjectRole($role)) {
$srcColumnIds = $this->columnMoveRestrictionCacheDecorator->getAllSrcColumns($task['project_id'], $role);
return isset($srcColumnIds[$task['column_id']]);
}
return true;
}
return false;
}
} }

View File

@ -0,0 +1,130 @@
<?php
namespace Kanboard\Helper;
use Kanboard\Core\Base;
use Kanboard\Core\Security\Role;
/**
* Class ProjectRoleHelper
*
* @package Kanboard\Helper
* @author Frederic Guillot
*/
class ProjectRoleHelper extends Base
{
/**
* Get project role for the current user
*
* @access public
* @param integer $project_id
* @return string
*/
public function getProjectUserRole($project_id)
{
return $this->memoryCache->proxy($this->projectUserRoleModel, 'getUserRole', $project_id, $this->userSession->getId());
}
/**
* Return true if the task can be moved by the connected user
*
* @param array $task
* @return bool
*/
public function isDraggable(array $task)
{
if ($task['is_active'] == 1 && $this->helper->user->hasProjectAccess('BoardViewController', 'save', $task['project_id'])) {
$role = $this->getProjectUserRole($task['project_id']);
if ($this->role->isCustomProjectRole($role)) {
$srcColumnIds = $this->columnMoveRestrictionCacheDecorator->getAllSrcColumns($task['project_id'], $role);
return isset($srcColumnIds[$task['column_id']]);
}
return true;
}
return false;
}
/**
* Check if the user can move a task
*
* @param int $project_id
* @param int $src_column_id
* @param int $dst_column_id
* @return bool|int
*/
public function canMoveTask($project_id, $src_column_id, $dst_column_id)
{
$role = $this->getProjectUserRole($project_id);
if ($this->role->isCustomProjectRole($role)) {
return $this->columnMoveRestrictionModel->isAllowed(
$project_id,
$role,
$src_column_id,
$dst_column_id
);
}
return true;
}
/**
* Return true if the user can remove a task
*
* Regular users can't remove tasks from other people
*
* @public
* @param array $task
* @return bool
*/
public function canRemoveTask(array $task)
{
if (isset($task['creator_id']) && $task['creator_id'] == $this->userSession->getId()) {
return true;
}
if ($this->userSession->isAdmin() || $this->getProjectUserRole($task['project_id']) === Role::PROJECT_MANAGER) {
return true;
}
return false;
}
/**
* Check project access
*
* @param string $controller
* @param string $action
* @param integer $project_id
* @return bool
*/
public function checkProjectAccess($controller, $action, $project_id)
{
if (! $this->userSession->isLogged()) {
return false;
}
if ($this->userSession->isAdmin()) {
return true;
}
if (! $this->helper->user->hasAccess($controller, $action)) {
return false;
}
$role = $this->getProjectUserRole($project_id);
if ($this->role->isCustomProjectRole($role)) {
$restrictions = $this->projectRoleRestrictionModel->getAllByRole($project_id, $role);
$result = $this->projectRoleRestrictionModel->isAllowed($restrictions, $controller, $action);
$result = $result && $this->projectAuthorization->isAllowed($controller, $action, Role::PROJECT_MEMBER);
} else {
$result = $this->projectAuthorization->isAllowed($controller, $action, $role);
}
return $result;
}
}

View File

@ -3,7 +3,6 @@
namespace Kanboard\Helper; namespace Kanboard\Helper;
use Kanboard\Core\Base; use Kanboard\Core\Base;
use Kanboard\Core\Security\Role;
/** /**
* User helpers * User helpers
@ -133,66 +132,14 @@ class UserHelper extends Base
*/ */
public function hasProjectAccess($controller, $action, $project_id) public function hasProjectAccess($controller, $action, $project_id)
{ {
if (! $this->userSession->isLogged()) {
return false;
}
if ($this->userSession->isAdmin()) {
return true;
}
if (! $this->hasAccess($controller, $action)) {
return false;
}
$key = 'project_access:'.$controller.$action.$project_id; $key = 'project_access:'.$controller.$action.$project_id;
$result = $this->memoryCache->get($key); $result = $this->memoryCache->get($key);
if ($result === null) { if ($result === null) {
$role = $this->getProjectUserRole($project_id); $result = $this->helper->projectRole->checkProjectAccess($controller, $action, $project_id);
if ($this->role->isCustomProjectRole($role)) {
$role = Role::PROJECT_MEMBER;
}
$result = $this->projectAuthorization->isAllowed($controller, $action, $role);
$this->memoryCache->set($key, $result); $this->memoryCache->set($key, $result);
} }
return $result; return $result;
} }
/**
* Get project role for the current user
*
* @access public
* @param integer $project_id
* @return string
*/
public function getProjectUserRole($project_id)
{
return $this->memoryCache->proxy($this->projectUserRoleModel, 'getUserRole', $project_id, $this->userSession->getId());
}
/**
* Return true if the user can remove a task
*
* Regular users can't remove tasks from other people
*
* @public
* @param array $task
* @return bool
*/
public function canRemoveTask(array $task)
{
if (isset($task['creator_id']) && $task['creator_id'] == $this->userSession->getId()) {
return true;
}
if ($this->userSession->isAdmin() || $this->getProjectUserRole($task['project_id']) === Role::PROJECT_MANAGER) {
return true;
}
return false;
}
} }

View File

@ -17,7 +17,7 @@ class ProjectRoleModel extends Base
/** /**
* Get list of project roles * Get list of project roles
* *
* @param int $project_id * @param int $project_id
* @return array * @return array
*/ */
@ -70,9 +70,14 @@ class ProjectRoleModel extends Base
public function getAllWithRestrictions($project_id) public function getAllWithRestrictions($project_id)
{ {
$roles = $this->getAll($project_id); $roles = $this->getAll($project_id);
$restrictions = $this->columnMoveRestrictionModel->getAll($project_id);
$restrictions = array_column_index($restrictions, 'role_id'); $column_restrictions = $this->columnMoveRestrictionModel->getAll($project_id);
array_merge_relation($roles, $restrictions, 'restrictions', 'role_id'); $column_restrictions = array_column_index($column_restrictions, 'role_id');
array_merge_relation($roles, $column_restrictions, 'column_restrictions', 'role_id');
$project_restrictions = $this->projectRoleRestrictionModel->getAll($project_id);
$project_restrictions = array_column_index($project_restrictions, 'role_id');
array_merge_relation($roles, $project_restrictions, 'project_restrictions', 'role_id');
return $roles; return $roles;
} }

View File

@ -0,0 +1,164 @@
<?php
namespace Kanboard\Model;
use Kanboard\Core\Base;
/**
* Class ProjectRoleRestrictionModel
*
* @package Kanboard\Model
* @author Frederic Guillot
*/
class ProjectRoleRestrictionModel extends Base
{
const TABLE = 'project_role_has_restrictions';
const RULE_TASK_CREATION = 'task_creation';
protected $ruleMapping = array(
self::RULE_TASK_CREATION => array(
array('controller' => 'TaskCreationController', 'method' => '*'),
)
);
/**
* Get rules
*
* @return array
*/
public function getRules()
{
return array(
self::RULE_TASK_CREATION => t('Task creation is not permitted'),
);
}
/**
* Get a single restriction
*
* @param integer $project_id
* @param integer $restriction_id
* @return array|null
*/
public function getById($project_id, $restriction_id)
{
return $this->db
->table(self::TABLE)
->eq('project_id', $project_id)
->eq('restriction_id', $restriction_id)
->findOne();
}
/**
* Get restrictions
*
* @param int $project_id
* @return array
*/
public function getAll($project_id)
{
$rules = $this->getRules();
$restrictions = $this->db
->table(self::TABLE)
->columns(
self::TABLE.'.restriction_id',
self::TABLE.'.project_id',
self::TABLE.'.role_id',
self::TABLE.'.rule'
)
->eq(self::TABLE.'.project_id', $project_id)
->findAll();
foreach ($restrictions as &$restriction) {
$restriction['title'] = $rules[$restriction['rule']];
}
return $restrictions;
}
/**
* Get restrictions
*
* @param int $project_id
* @param string $role
* @return array
*/
public function getAllByRole($project_id, $role)
{
$rules = $this->db
->table(self::TABLE)
->columns(
self::TABLE.'.restriction_id',
self::TABLE.'.project_id',
self::TABLE.'.role_id',
self::TABLE.'.rule',
'pr.role'
)
->eq(self::TABLE.'.project_id', $project_id)
->eq('role', $role)
->left(ProjectRoleModel::TABLE, 'pr', 'role_id', self::TABLE, 'role_id')
->findAll();
foreach ($rules as &$rule) {
$rule['acl'] = $this->ruleMapping[$rule['rule']];
}
return $rules;
}
/**
* Create a new restriction
*
* @param int $project_id
* @param int $role_id
* @param string $rule
* @return bool|int
*/
public function create($project_id, $role_id, $rule)
{
return $this->db->table(self::TABLE)
->persist(array(
'project_id' => $project_id,
'role_id' => $role_id,
'rule' => $rule,
));
}
/**
* Remove a restriction
*
* @param integer $restriction_id
* @return bool
*/
public function remove($restriction_id)
{
return $this->db->table(self::TABLE)->eq('restriction_id', $restriction_id)->remove();
}
/**
* Check if the controller/method is allowed
*
* @param array $restrictions
* @param string $controller
* @param string $method
* @return bool
*/
public function isAllowed(array $restrictions, $controller, $method)
{
$controller = strtolower($controller);
$method = strtolower($method);
foreach ($restrictions as $restriction) {
foreach ($restriction['acl'] as $acl) {
$acl['controller'] = strtolower($acl['controller']);
$acl['method'] = strtolower($acl['method']);
if ($acl['controller'] === $controller && ($acl['method'] === '*' || $acl['method'] === $method)) {
return false;
}
}
}
return true;
}
}

View File

@ -6,7 +6,22 @@ use PDO;
use Kanboard\Core\Security\Token; use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role; use Kanboard\Core\Security\Role;
const VERSION = 113; const VERSION = 114;
function version_114(PDO $pdo)
{
$pdo->exec("
CREATE TABLE project_role_has_restrictions (
restriction_id INT NOT NULL AUTO_INCREMENT,
project_id INT NOT NULL,
role_id INT NOT NULL,
rule VARCHAR(255) NOT NULL,
UNIQUE(role_id, rule),
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
FOREIGN KEY(role_id) REFERENCES project_has_roles(role_id) ON DELETE CASCADE
) ENGINE=InnoDB CHARSET=utf8
");
}
function version_113(PDO $pdo) function version_113(PDO $pdo)
{ {

View File

@ -6,7 +6,22 @@ use PDO;
use Kanboard\Core\Security\Token; use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role; use Kanboard\Core\Security\Role;
const VERSION = 92; const VERSION = 93;
function version_93(PDO $pdo)
{
$pdo->exec("
CREATE TABLE project_role_has_restrictions (
restriction_id SERIAL PRIMARY KEY,
project_id INTEGER NOT NULL,
role_id INTEGER NOT NULL,
rule VARCHAR(255) NOT NULL,
UNIQUE(role_id, rule),
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
FOREIGN KEY(role_id) REFERENCES project_has_roles(role_id) ON DELETE CASCADE
)
");
}
function version_92(PDO $pdo) function version_92(PDO $pdo)
{ {

View File

@ -6,7 +6,22 @@ use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role; use Kanboard\Core\Security\Role;
use PDO; use PDO;
const VERSION = 104; const VERSION = 105;
function version_105(PDO $pdo)
{
$pdo->exec("
CREATE TABLE project_role_has_restrictions (
restriction_id INTEGER PRIMARY KEY,
project_id INTEGER NOT NULL,
role_id INTEGER NOT NULL,
rule VARCHAR(255) NOT NULL,
UNIQUE(role_id, rule),
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
FOREIGN KEY(role_id) REFERENCES project_has_roles(role_id) ON DELETE CASCADE
)
");
}
function version_104(PDO $pdo) function version_104(PDO $pdo)
{ {

View File

@ -57,6 +57,7 @@ class ClassProvider implements ServiceProviderInterface
'ProjectMetadataModel', 'ProjectMetadataModel',
'ProjectGroupRoleModel', 'ProjectGroupRoleModel',
'ProjectRoleModel', 'ProjectRoleModel',
'ProjectRoleRestrictionModel',
'ProjectTaskDuplicationModel', 'ProjectTaskDuplicationModel',
'ProjectTaskPriorityModel', 'ProjectTaskPriorityModel',
'ProjectUserRoleModel', 'ProjectUserRoleModel',

View File

@ -35,6 +35,7 @@ class HelperProvider implements ServiceProviderInterface
$container['helper']->register('url', '\Kanboard\Helper\UrlHelper'); $container['helper']->register('url', '\Kanboard\Helper\UrlHelper');
$container['helper']->register('user', '\Kanboard\Helper\UserHelper'); $container['helper']->register('user', '\Kanboard\Helper\UserHelper');
$container['helper']->register('avatar', '\Kanboard\Helper\AvatarHelper'); $container['helper']->register('avatar', '\Kanboard\Helper\AvatarHelper');
$container['helper']->register('projectRole', '\Kanboard\Helper\ProjectRoleHelper');
$container['helper']->register('projectHeader', '\Kanboard\Helper\ProjectHeaderHelper'); $container['helper']->register('projectHeader', '\Kanboard\Helper\ProjectHeaderHelper');
$container['helper']->register('projectActivity', '\Kanboard\Helper\ProjectActivityHelper'); $container['helper']->register('projectActivity', '\Kanboard\Helper\ProjectActivityHelper');
$container['helper']->register('mail', '\Kanboard\Helper\MailHelper'); $container['helper']->register('mail', '\Kanboard\Helper\MailHelper');

View File

@ -18,6 +18,10 @@
<div class="dropdown"> <div class="dropdown">
<a href="#" class="dropdown-menu"><?= t('Restrictions for the role "%s"', $role['role']) ?> <i class="fa fa-caret-down"></i></a> <a href="#" class="dropdown-menu"><?= t('Restrictions for the role "%s"', $role['role']) ?> <i class="fa fa-caret-down"></i></a>
<ul> <ul>
<li>
<i class="fa fa-plus fa-fw" aria-hidden="true"></i>
<?= $this->url->link(t('Add a new project restriction'), 'ProjectRoleRestrictionController', 'create', array('project_id' => $project['id'], 'role_id' => $role['role_id']), false, 'popover') ?>
</li>
<li> <li>
<i class="fa fa-plus fa-fw" aria-hidden="true"></i> <i class="fa fa-plus fa-fw" aria-hidden="true"></i>
<?= $this->url->link(t('Add a new column restriction'), 'ColumnMoveRestrictionController', 'create', array('project_id' => $project['id'], 'role_id' => $role['role_id']), false, 'popover') ?> <?= $this->url->link(t('Add a new column restriction'), 'ColumnMoveRestrictionController', 'create', array('project_id' => $project['id'], 'role_id' => $role['role_id']), false, 'popover') ?>
@ -33,15 +37,26 @@
<?= t('Actions') ?> <?= t('Actions') ?>
</th> </th>
</tr> </tr>
<?php if (empty($role['restrictions'])): ?> <?php if (empty($role['project_restrictions']) && empty($role['column_restrictions'])): ?>
<tr> <tr>
<td colspan="2"><?= t('There is no restriction for this role.') ?></td> <td colspan="2"><?= t('There is no restriction for this role.') ?></td>
</tr> </tr>
<?php else: ?> <?php else: ?>
<?php foreach ($role['restrictions'] as $restriction): ?> <?php foreach ($role['project_restrictions'] as $restriction): ?>
<tr> <tr>
<td> <td>
<?= t('Moving task from the column "%s" to "%s" is permitted', $restriction['src_column_title'], $restriction['dst_column_title']) ?> <?= $this->text->e($restriction['title']) ?>
</td>
<td>
<i class="fa fa-trash-o fa-fw" aria-hidden="true"></i>
<?= $this->url->link(t('Remove'), 'ProjectRoleRestrictionController', 'confirm', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id']), false, 'popover') ?>
</td>
</tr>
<?php endforeach ?>
<?php foreach ($role['column_restrictions'] as $restriction): ?>
<tr>
<td>
<?= t('Only moving task from the column "%s" to "%s" is permitted', $restriction['src_column_title'], $restriction['dst_column_title']) ?>
</td> </td>
<td> <td>
<i class="fa fa-trash-o fa-fw" aria-hidden="true"></i> <i class="fa fa-trash-o fa-fw" aria-hidden="true"></i>

View File

@ -0,0 +1,19 @@
<section id="main">
<div class="page-header">
<h2><?= t('New project restriction for the role "%s"', $role['role']) ?></h2>
</div>
<form class="popover-form" method="post" action="<?= $this->url->href('ProjectRoleRestrictionController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('project_id', $values) ?>
<?= $this->form->hidden('role_id', $values) ?>
<?= $this->form->label(t('Restriction'), 'rule') ?>
<?= $this->form->select('rule', $restrictions, $values, $errors) ?>
<div class="form-actions">
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
<?= t('or') ?>
<?= $this->url->link(t('cancel'), 'ProjectRoleController', 'show', array(), false, 'close-popover') ?>
</div>
</form>
</section>

View File

@ -0,0 +1,14 @@
<div class="page-header">
<h2><?= t('Remove a project restriction') ?></h2>
</div>
<div class="confirm">
<p class="alert alert-info">
<?= t('Do you really want to remove this project restriction: "%s"?', $this->text->in($restriction['rule'], $restrictions)) ?>
</p>
<div class="form-actions">
<?= $this->url->link(t('Yes'), 'ProjectRoleRestrictionController', 'remove', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id']), true, 'btn btn-red') ?>
<?= t('or') ?> <?= $this->url->link(t('cancel'), 'ProjectRoleController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?>
</div>
</div>

View File

@ -43,7 +43,7 @@
<i class="fa fa-clone fa-fw"></i> <i class="fa fa-clone fa-fw"></i>
<?= $this->url->link(t('Move to another project'), 'TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> <?= $this->url->link(t('Move to another project'), 'TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
</li> </li>
<?php if ($this->user->canRemoveTask($task)): ?> <?php if ($this->projectRole->canRemoveTask($task)): ?>
<li> <li>
<i class="fa fa-trash-o fa-fw"></i> <i class="fa fa-trash-o fa-fw"></i>
<?= $this->url->link(t('Remove'), 'TaskSuppressionController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> <?= $this->url->link(t('Remove'), 'TaskSuppressionController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>

View File

@ -93,7 +93,7 @@
<?= $this->url->link(t('Open this task'), 'TaskStatusController', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?> <?= $this->url->link(t('Open this task'), 'TaskStatusController', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
</li> </li>
<?php endif ?> <?php endif ?>
<?php if ($this->user->canRemoveTask($task)): ?> <?php if ($this->projectRole->canRemoveTask($task)): ?>
<li> <li>
<i class="fa fa-trash-o fa-fw"></i> <i class="fa fa-trash-o fa-fw"></i>
<?= $this->url->link(t('Remove'), 'TaskSuppressionController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board'), false, 'popover') ?> <?= $this->url->link(t('Remove'), 'TaskSuppressionController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board'), false, 'popover') ?>

View File

@ -1,98 +0,0 @@
<?php
use Kanboard\Core\Security\Role;
use Kanboard\Helper\BoardHelper;
use Kanboard\Model\ColumnMoveRestrictionModel;
use Kanboard\Model\ProjectModel;
use Kanboard\Model\ProjectRoleModel;
use Kanboard\Model\ProjectUserRoleModel;
use Kanboard\Model\TaskCreationModel;
use Kanboard\Model\TaskFinderModel;
use Kanboard\Model\TaskStatusModel;
use Kanboard\Model\UserModel;
require_once __DIR__.'/../Base.php';
class BoardHelperTest extends Base
{
public function testIsDraggableWithProjectMember()
{
$boardHelper = new BoardHelper($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$taskFinderModel = new TaskFinderModel($this->container);
$projectUserRole = new ProjectUserRoleModel($this->container);
$userModel = new UserModel($this->container);
$this->container['sessionStorage']->user = array(
'id' => 2,
'role' => Role::APP_USER,
);
$this->assertEquals(2, $userModel->create(array('username' => 'user')));
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertTrue($projectUserRole->addUser(1, 2, Role::PROJECT_MEMBER));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test')));
$task = $taskFinderModel->getById(1);
$this->assertTrue($boardHelper->isDraggable($task));
}
public function testIsDraggableWithClosedTask()
{
$boardHelper = new BoardHelper($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$taskFinderModel = new TaskFinderModel($this->container);
$taskStatusModel = new TaskStatusModel($this->container);
$projectUserRole = new ProjectUserRoleModel($this->container);
$userModel = new UserModel($this->container);
$this->container['sessionStorage']->user = array(
'id' => 2,
'role' => Role::APP_USER,
);
$this->assertEquals(2, $userModel->create(array('username' => 'user')));
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertTrue($projectUserRole->addUser(1, 2, Role::PROJECT_MEMBER));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test')));
$this->assertTrue($taskStatusModel->close(1));
$task = $taskFinderModel->getById(1);
$this->assertFalse($boardHelper->isDraggable($task));
}
public function testIsDraggableWithColumnRestrictions()
{
$boardHelper = new BoardHelper($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$taskFinderModel = new TaskFinderModel($this->container);
$projectUserRole = new ProjectUserRoleModel($this->container);
$userModel = new UserModel($this->container);
$projectRoleModel = new ProjectRoleModel($this->container);
$columnMoveRestrictionModel = new ColumnMoveRestrictionModel($this->container);
$this->container['sessionStorage']->user = array(
'id' => 2,
'role' => Role::APP_USER,
);
$this->assertEquals(2, $userModel->create(array('username' => 'user')));
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertEquals(1, $projectRoleModel->create(1, 'Custom Role'));
$this->assertEquals(1, $columnMoveRestrictionModel->create(1, 1, 2, 3));
$this->assertTrue($projectUserRole->addUser(1, 2, 'Custom Role'));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test', 'column_id' => 2)));
$this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test', 'column_id' => 3)));
$task = $taskFinderModel->getById(1);
$this->assertTrue($boardHelper->isDraggable($task));
$task = $taskFinderModel->getById(2);
$this->assertFalse($boardHelper->isDraggable($task));
}
}

View File

@ -0,0 +1,189 @@
<?php
use Kanboard\Core\Security\Role;
use Kanboard\Core\User\UserSession;
use Kanboard\Helper\ProjectRoleHelper;
use Kanboard\Model\ColumnMoveRestrictionModel;
use Kanboard\Model\ProjectModel;
use Kanboard\Model\ProjectRoleModel;
use Kanboard\Model\ProjectUserRoleModel;
use Kanboard\Model\TaskCreationModel;
use Kanboard\Model\TaskFinderModel;
use Kanboard\Model\TaskStatusModel;
use Kanboard\Model\UserModel;
require_once __DIR__.'/../Base.php';
class ProjectRoleHelperTest extends Base
{
public function testIsDraggableWithProjectMember()
{
$projectRoleHelper = new ProjectRoleHelper($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$taskFinderModel = new TaskFinderModel($this->container);
$projectUserRole = new ProjectUserRoleModel($this->container);
$userModel = new UserModel($this->container);
$this->container['sessionStorage']->user = array(
'id' => 2,
'role' => Role::APP_USER,
);
$this->assertEquals(2, $userModel->create(array('username' => 'user')));
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertTrue($projectUserRole->addUser(1, 2, Role::PROJECT_MEMBER));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test')));
$task = $taskFinderModel->getById(1);
$this->assertTrue($projectRoleHelper->isDraggable($task));
}
public function testIsDraggableWithClosedTask()
{
$projectRoleHelper = new ProjectRoleHelper($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$taskFinderModel = new TaskFinderModel($this->container);
$taskStatusModel = new TaskStatusModel($this->container);
$projectUserRole = new ProjectUserRoleModel($this->container);
$userModel = new UserModel($this->container);
$this->container['sessionStorage']->user = array(
'id' => 2,
'role' => Role::APP_USER,
);
$this->assertEquals(2, $userModel->create(array('username' => 'user')));
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertTrue($projectUserRole->addUser(1, 2, Role::PROJECT_MEMBER));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test')));
$this->assertTrue($taskStatusModel->close(1));
$task = $taskFinderModel->getById(1);
$this->assertFalse($projectRoleHelper->isDraggable($task));
}
public function testIsDraggableWithColumnRestrictions()
{
$projectRoleHelper = new ProjectRoleHelper($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$taskFinderModel = new TaskFinderModel($this->container);
$projectUserRole = new ProjectUserRoleModel($this->container);
$userModel = new UserModel($this->container);
$projectRoleModel = new ProjectRoleModel($this->container);
$columnMoveRestrictionModel = new ColumnMoveRestrictionModel($this->container);
$this->container['sessionStorage']->user = array(
'id' => 2,
'role' => Role::APP_USER,
);
$this->assertEquals(2, $userModel->create(array('username' => 'user')));
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertEquals(1, $projectRoleModel->create(1, 'Custom Role'));
$this->assertEquals(1, $columnMoveRestrictionModel->create(1, 1, 2, 3));
$this->assertTrue($projectUserRole->addUser(1, 2, 'Custom Role'));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test', 'column_id' => 2)));
$this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test', 'column_id' => 3)));
$task = $taskFinderModel->getById(1);
$this->assertTrue($projectRoleHelper->isDraggable($task));
$task = $taskFinderModel->getById(2);
$this->assertFalse($projectRoleHelper->isDraggable($task));
}
public function testCanRemoveTask()
{
$taskCreationModel = new TaskCreationModel($this->container);
$taskFinderModel = new TaskFinderModel($this->container);
$projectRoleHelper = new ProjectRoleHelper($this->container);
$projectModel = new ProjectModel($this->container);
$userModel = new UserModel($this->container);
$userSessionModel = new UserSession($this->container);
$this->assertNotFalse($userModel->create(array('username' => 'toto', 'password' => '123456')));
$this->assertNotFalse($userModel->create(array('username' => 'toto2', 'password' => '123456')));
$this->assertEquals(1, $projectModel->create(array('name' => 'Project #1')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'TaskViewController #1', 'project_id' => 1, 'creator_id' => 1)));
$this->assertEquals(2, $taskCreationModel->create(array('title' => 'TaskViewController #2', 'project_id' => 1, 'creator_id' => 2)));
$this->assertEquals(3, $taskCreationModel->create(array('title' => 'TaskViewController #3', 'project_id' => 1, 'creator_id' => 3)));
$this->assertEquals(4, $taskCreationModel->create(array('title' => 'TaskViewController #4', 'project_id' => 1)));
// User #1 can remove everything
$user = $userModel->getById(1);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(1);
$this->assertNotEmpty($task);
$this->assertTrue($projectRoleHelper->canRemoveTask($task));
// User #2 can't remove the TaskViewController #1
$user = $userModel->getById(2);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(1);
$this->assertNotEmpty($task);
$this->assertFalse($projectRoleHelper->canRemoveTask($task));
// User #1 can remove everything
$user = $userModel->getById(1);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(2);
$this->assertNotEmpty($task);
$this->assertTrue($projectRoleHelper->canRemoveTask($task));
// User #2 can remove his own TaskViewController
$user = $userModel->getById(2);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(2);
$this->assertNotEmpty($task);
$this->assertTrue($projectRoleHelper->canRemoveTask($task));
// User #1 can remove everything
$user = $userModel->getById(1);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(3);
$this->assertNotEmpty($task);
$this->assertTrue($projectRoleHelper->canRemoveTask($task));
// User #2 can't remove the TaskViewController #3
$user = $userModel->getById(2);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(3);
$this->assertNotEmpty($task);
$this->assertFalse($projectRoleHelper->canRemoveTask($task));
// User #1 can remove everything
$user = $userModel->getById(1);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(4);
$this->assertNotEmpty($task);
$this->assertTrue($projectRoleHelper->canRemoveTask($task));
// User #2 can't remove the TaskViewController #4
$user = $userModel->getById(2);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(4);
$this->assertNotEmpty($task);
$this->assertFalse($projectRoleHelper->canRemoveTask($task));
}
}

View File

@ -294,94 +294,4 @@ class UserHelperTest extends Base
$this->assertFalse($helper->hasProjectAccess('TaskViewController', 'show', 2)); $this->assertFalse($helper->hasProjectAccess('TaskViewController', 'show', 2));
$this->assertFalse($helper->hasProjectAccess('TaskCreationController', 'save', 2)); $this->assertFalse($helper->hasProjectAccess('TaskCreationController', 'save', 2));
} }
public function testCanRemoveTask()
{
$taskCreationModel = new TaskCreationModel($this->container);
$taskFinderModel = new TaskFinderModel($this->container);
$helper = new UserHelper($this->container);
$projectModel = new ProjectModel($this->container);
$userModel = new UserModel($this->container);
$userSessionModel = new UserSession($this->container);
$this->assertNotFalse($userModel->create(array('username' => 'toto', 'password' => '123456')));
$this->assertNotFalse($userModel->create(array('username' => 'toto2', 'password' => '123456')));
$this->assertEquals(1, $projectModel->create(array('name' => 'Project #1')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'TaskViewController #1', 'project_id' => 1, 'creator_id' => 1)));
$this->assertEquals(2, $taskCreationModel->create(array('title' => 'TaskViewController #2', 'project_id' => 1, 'creator_id' => 2)));
$this->assertEquals(3, $taskCreationModel->create(array('title' => 'TaskViewController #3', 'project_id' => 1, 'creator_id' => 3)));
$this->assertEquals(4, $taskCreationModel->create(array('title' => 'TaskViewController #4', 'project_id' => 1)));
// User #1 can remove everything
$user = $userModel->getById(1);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(1);
$this->assertNotEmpty($task);
$this->assertTrue($helper->canRemoveTask($task));
// User #2 can't remove the TaskViewController #1
$user = $userModel->getById(2);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(1);
$this->assertNotEmpty($task);
$this->assertFalse($helper->canRemoveTask($task));
// User #1 can remove everything
$user = $userModel->getById(1);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(2);
$this->assertNotEmpty($task);
$this->assertTrue($helper->canRemoveTask($task));
// User #2 can remove his own TaskViewController
$user = $userModel->getById(2);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(2);
$this->assertNotEmpty($task);
$this->assertTrue($helper->canRemoveTask($task));
// User #1 can remove everything
$user = $userModel->getById(1);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(3);
$this->assertNotEmpty($task);
$this->assertTrue($helper->canRemoveTask($task));
// User #2 can't remove the TaskViewController #3
$user = $userModel->getById(2);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(3);
$this->assertNotEmpty($task);
$this->assertFalse($helper->canRemoveTask($task));
// User #1 can remove everything
$user = $userModel->getById(1);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(4);
$this->assertNotEmpty($task);
$this->assertTrue($helper->canRemoveTask($task));
// User #2 can't remove the TaskViewController #4
$user = $userModel->getById(2);
$this->assertNotEmpty($user);
$userSessionModel->initialize($user);
$task = $taskFinderModel->getById(4);
$this->assertNotEmpty($task);
$this->assertFalse($helper->canRemoveTask($task));
}
} }