Added application and project roles validation for API procedure calls

This commit is contained in:
Frederic Guillot
2016-06-26 10:25:13 -04:00
parent 922e0fb6de
commit 4a230d331e
79 changed files with 1772 additions and 761 deletions

View File

@@ -1,6 +1,10 @@
Version 1.0.31 (unreleased) Version 1.0.31 (unreleased)
-------------- --------------
New features:
* Added application and project roles validation for API procedure calls
Improvements: Improvements:
* Rewrite integration tests to run with Docker containers * Rewrite integration tests to run with Docker containers

View File

@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class ActionAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class ActionAuthorization extends ProjectAuthorization
{
public function check($class, $method, $action_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->actionModel->getProjectId($action_id));
}
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class CategoryAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class CategoryAuthorization extends ProjectAuthorization
{
public function check($class, $method, $category_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->categoryModel->getProjectId($category_id));
}
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class ColumnAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class ColumnAuthorization extends ProjectAuthorization
{
public function check($class, $method, $column_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->columnModel->getProjectId($column_id));
}
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class CommentAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class CommentAuthorization extends ProjectAuthorization
{
public function check($class, $method, $comment_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->commentModel->getProjectId($comment_id));
}
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Kanboard\Api\Authorization;
use JsonRPC\Exception\AccessDeniedException;
use Kanboard\Core\Base;
/**
* Class ProcedureAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class ProcedureAuthorization extends Base
{
private $userSpecificProcedures = array(
'getMe',
'getMyDashboard',
'getMyActivityStream',
'createMyPrivateProject',
'getMyProjectsList',
'getMyProjects',
'getMyOverdueTasks',
);
public function check($procedure)
{
if (! $this->userSession->isLogged() && in_array($procedure, $this->userSpecificProcedures)) {
throw new AccessDeniedException('This procedure is not available with the API credentials');
}
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Kanboard\Api\Authorization;
use JsonRPC\Exception\AccessDeniedException;
use Kanboard\Core\Base;
/**
* Class ProjectAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class ProjectAuthorization extends Base
{
public function check($class, $method, $project_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $project_id);
}
}
protected function checkProjectPermission($class, $method, $project_id)
{
if (empty($project_id)) {
throw new AccessDeniedException('Project not found');
}
$role = $this->projectUserRoleModel->getUserRole($project_id, $this->userSession->getId());
if (! $this->apiProjectAuthorization->isAllowed($class, $method, $role)) {
throw new AccessDeniedException('Project access denied');
}
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class SubtaskAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class SubtaskAuthorization extends ProjectAuthorization
{
public function check($class, $method, $subtask_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->subtaskModel->getProjectId($subtask_id));
}
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class TaskAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class TaskAuthorization extends ProjectAuthorization
{
public function check($class, $method, $category_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->taskFinderModel->getProjectId($category_id));
}
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class TaskFileAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class TaskFileAuthorization extends ProjectAuthorization
{
public function check($class, $method, $file_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->taskFileModel->getProjectId($file_id));
}
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class TaskLinkAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class TaskLinkAuthorization extends ProjectAuthorization
{
public function check($class, $method, $task_link_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->taskLinkModel->getProjectId($task_link_id));
}
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Kanboard\Api\Authorization;
use JsonRPC\Exception\AccessDeniedException;
use Kanboard\Core\Base;
/**
* Class UserAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class UserAuthorization extends Base
{
public function check($class, $method)
{
if ($this->userSession->isLogged() && ! $this->apiAuthorization->isAllowed($class, $method, $this->userSession->getRole())) {
throw new AccessDeniedException('You are not allowed to access to this resource');
}
}
}

View File

@@ -13,46 +13,8 @@ use Kanboard\Core\Base;
* @package Kanboard\Api\Middleware * @package Kanboard\Api\Middleware
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class AuthenticationApiMiddleware extends Base implements MiddlewareInterface class AuthenticationMiddleware extends Base implements MiddlewareInterface
{ {
private $user_allowed_procedures = array(
'getMe',
'getMyDashboard',
'getMyActivityStream',
'createMyPrivateProject',
'getMyProjectsList',
'getMyProjects',
'getMyOverdueTasks',
);
private $both_allowed_procedures = array(
'getTimezone',
'getVersion',
'getDefaultTaskColor',
'getDefaultTaskColors',
'getColorList',
'getProjectById',
'getSubTask',
'getTask',
'getTaskByReference',
'getTimeSpent',
'getAllTasks',
'getAllSubTasks',
'hasTimer',
'logStartTime',
'logEndTime',
'openTask',
'closeTask',
'moveTaskPosition',
'createTask',
'createSubtask',
'updateTask',
'getBoard',
'getProjectActivity',
'getOverdueTasksByProject',
'searchTasks',
);
/** /**
* Execute Middleware * Execute Middleware
* *
@@ -68,11 +30,8 @@ class AuthenticationApiMiddleware extends Base implements MiddlewareInterface
$this->dispatcher->dispatch('app.bootstrap'); $this->dispatcher->dispatch('app.bootstrap');
if ($this->isUserAuthenticated($username, $password)) { if ($this->isUserAuthenticated($username, $password)) {
$this->checkProcedurePermission(true, $procedureName);
$this->userSession->initialize($this->userModel->getByUsername($username)); $this->userSession->initialize($this->userModel->getByUsername($username));
} elseif ($this->isAppAuthenticated($username, $password)) { } elseif (! $this->isAppAuthenticated($username, $password)) {
$this->checkProcedurePermission(false, $procedureName);
} else {
$this->logger->error('API authentication failure for '.$username); $this->logger->error('API authentication failure for '.$username);
throw new AuthenticationFailureException('Wrong credentials'); throw new AuthenticationFailureException('Wrong credentials');
} }
@@ -120,18 +79,4 @@ class AuthenticationApiMiddleware extends Base implements MiddlewareInterface
return $this->configModel->get('api_token'); return $this->configModel->get('api_token');
} }
public function checkProcedurePermission($is_user, $procedure)
{
$is_both_procedure = in_array($procedure, $this->both_allowed_procedures);
$is_user_procedure = in_array($procedure, $this->user_allowed_procedures);
if ($is_user && ! $is_both_procedure && ! $is_user_procedure) {
throw new AccessDeniedException('Permission denied');
} elseif (! $is_user && ! $is_both_procedure && $is_user_procedure) {
throw new AccessDeniedException('Permission denied');
}
$this->logger->debug('API call: '.$procedure);
}
} }

View File

@@ -1,16 +1,17 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base; use Kanboard\Api\Authorization\ActionAuthorization;
use Kanboard\Api\Authorization\ProjectAuthorization;
/** /**
* Action API controller * Action API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class ActionApi extends Base class ActionProcedure extends BaseProcedure
{ {
public function getAvailableActions() public function getAvailableActions()
{ {
@@ -29,16 +30,19 @@ class ActionApi extends Base
public function removeAction($action_id) public function removeAction($action_id)
{ {
ActionAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAction', $action_id);
return $this->actionModel->remove($action_id); return $this->actionModel->remove($action_id);
} }
public function getActions($project_id) public function getActions($project_id)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getActions', $project_id);
return $this->actionModel->getAllByProject($project_id); return $this->actionModel->getAllByProject($project_id);
} }
public function createAction($project_id, $event_name, $action_name, array $params) public function createAction($project_id, $event_name, $action_name, array $params)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createAction', $project_id);
$values = array( $values = array(
'project_id' => $project_id, 'project_id' => $project_id,
'event_name' => $event_name, 'event_name' => $event_name,

View File

@@ -1,16 +1,14 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base;
/** /**
* App API controller * App API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class AppApi extends Base class AppProcedure extends BaseProcedure
{ {
public function getTimezone() public function getTimezone()
{ {

View File

@@ -1,30 +1,25 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use JsonRPC\Exception\AccessDeniedException; use JsonRPC\Exception\AccessDeniedException;
use Kanboard\Api\Authorization\ProcedureAuthorization;
use Kanboard\Api\Authorization\UserAuthorization;
use Kanboard\Core\Base; use Kanboard\Core\Base;
use ReflectionClass;
/** /**
* Base class * Base class
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
abstract class BaseApi extends Base abstract class BaseProcedure extends Base
{ {
public function checkProjectPermission($project_id) public function beforeProcedure($procedure)
{ {
if ($this->userSession->isLogged() && ! $this->projectPermissionModel->isUserAllowed($project_id, $this->userSession->getId())) { ProcedureAuthorization::getInstance($this->container)->check($procedure);
throw new AccessDeniedException('Permission denied'); UserAuthorization::getInstance($this->container)->check($this->getClassName(), $procedure);
}
}
public function checkTaskPermission($task_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($this->taskFinderModel->getProjectId($task_id));
}
} }
protected function formatTask($task) protected function formatTask($task)
@@ -82,4 +77,10 @@ abstract class BaseApi extends Base
return $values; return $values;
} }
protected function getClassName()
{
$reflection = new ReflectionClass(get_called_class());
return $reflection->getShortName();
}
} }

View File

@@ -1,21 +1,22 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Formatter\BoardFormatter; use Kanboard\Formatter\BoardFormatter;
/** /**
* Board API controller * Board API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class BoardApi extends BaseApi class BoardProcedure extends BaseProcedure
{ {
public function getBoard($project_id) public function getBoard($project_id)
{ {
$this->checkProjectPermission($project_id); ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getBoard', $project_id);
return BoardFormatter::getInstance($this->container) return BoardFormatter::getInstance($this->container)
->withProjectId($project_id) ->withProjectId($project_id)
->withQuery($this->taskFinderModel->getExtendedQuery()) ->withQuery($this->taskFinderModel->getExtendedQuery())

View File

@@ -1,34 +1,40 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base; use Kanboard\Api\Authorization\CategoryAuthorization;
use Kanboard\Api\Authorization\ProjectAuthorization;
/** /**
* Category API controller * Category API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class CategoryApi extends Base class CategoryProcedure extends BaseProcedure
{ {
public function getCategory($category_id) public function getCategory($category_id)
{ {
CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'getCategory', $category_id);
return $this->categoryModel->getById($category_id); return $this->categoryModel->getById($category_id);
} }
public function getAllCategories($project_id) public function getAllCategories($project_id)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllCategories', $project_id);
return $this->categoryModel->getAll($project_id); return $this->categoryModel->getAll($project_id);
} }
public function removeCategory($category_id) public function removeCategory($category_id)
{ {
CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeCategory', $category_id);
return $this->categoryModel->remove($category_id); return $this->categoryModel->remove($category_id);
} }
public function createCategory($project_id, $name) public function createCategory($project_id, $name)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createCategory', $project_id);
$values = array( $values = array(
'project_id' => $project_id, 'project_id' => $project_id,
'name' => $name, 'name' => $name,
@@ -40,6 +46,8 @@ class CategoryApi extends Base
public function updateCategory($id, $name) public function updateCategory($id, $name)
{ {
CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateCategory', $id);
$values = array( $values = array(
'id' => $id, 'id' => $id,
'name' => $name, 'name' => $name,

View File

@@ -1,42 +1,51 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ColumnAuthorization;
use Kanboard\Api\Authorization\ProjectAuthorization;
/** /**
* Column API controller * Column API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class ColumnApi extends BaseApi class ColumnProcedure extends BaseProcedure
{ {
public function getColumns($project_id) public function getColumns($project_id)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getColumns', $project_id);
return $this->columnModel->getAll($project_id); return $this->columnModel->getAll($project_id);
} }
public function getColumn($column_id) public function getColumn($column_id)
{ {
ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'getColumn', $column_id);
return $this->columnModel->getById($column_id); return $this->columnModel->getById($column_id);
} }
public function updateColumn($column_id, $title, $task_limit = 0, $description = '') public function updateColumn($column_id, $title, $task_limit = 0, $description = '')
{ {
ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateColumn', $column_id);
return $this->columnModel->update($column_id, $title, $task_limit, $description); return $this->columnModel->update($column_id, $title, $task_limit, $description);
} }
public function addColumn($project_id, $title, $task_limit = 0, $description = '') public function addColumn($project_id, $title, $task_limit = 0, $description = '')
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addColumn', $project_id);
return $this->columnModel->create($project_id, $title, $task_limit, $description); return $this->columnModel->create($project_id, $title, $task_limit, $description);
} }
public function removeColumn($column_id) public function removeColumn($column_id)
{ {
ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeColumn', $column_id);
return $this->columnModel->remove($column_id); return $this->columnModel->remove($column_id);
} }
public function changeColumnPosition($project_id, $column_id, $position) public function changeColumnPosition($project_id, $column_id, $position)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeColumnPosition', $project_id);
return $this->columnModel->changePosition($project_id, $column_id, $position); return $this->columnModel->changePosition($project_id, $column_id, $position);
} }
} }

View File

@@ -1,34 +1,40 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base; use Kanboard\Api\Authorization\CommentAuthorization;
use Kanboard\Api\Authorization\TaskAuthorization;
/** /**
* Comment API controller * Comment API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class CommentApi extends Base class CommentProcedure extends BaseProcedure
{ {
public function getComment($comment_id) public function getComment($comment_id)
{ {
CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'getComment', $comment_id);
return $this->commentModel->getById($comment_id); return $this->commentModel->getById($comment_id);
} }
public function getAllComments($task_id) public function getAllComments($task_id)
{ {
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllComments', $task_id);
return $this->commentModel->getAll($task_id); return $this->commentModel->getAll($task_id);
} }
public function removeComment($comment_id) public function removeComment($comment_id)
{ {
CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeComment', $comment_id);
return $this->commentModel->remove($comment_id); return $this->commentModel->remove($comment_id);
} }
public function createComment($task_id, $user_id, $content, $reference = '') public function createComment($task_id, $user_id, $content, $reference = '')
{ {
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createComment', $task_id);
$values = array( $values = array(
'task_id' => $task_id, 'task_id' => $task_id,
'user_id' => $user_id, 'user_id' => $user_id,
@@ -43,6 +49,8 @@ class CommentApi extends Base
public function updateComment($id, $content) public function updateComment($id, $content)
{ {
CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateComment', $id);
$values = array( $values = array(
'id' => $id, 'id' => $id,
'comment' => $content, 'comment' => $content,

View File

@@ -1,16 +1,14 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base;
/** /**
* Group Member API controller * Group Member API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class GroupMemberApi extends Base class GroupMemberProcedure extends BaseProcedure
{ {
public function getMemberGroups($user_id) public function getMemberGroups($user_id)
{ {

View File

@@ -1,16 +1,14 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base;
/** /**
* Group API controller * Group API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class GroupApi extends Base class GroupProcedure extends BaseProcedure
{ {
public function createGroup($name, $external_id = '') public function createGroup($name, $external_id = '')
{ {

View File

@@ -1,16 +1,14 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base;
/** /**
* Link API controller * Link API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class LinkApi extends Base class LinkProcedure extends BaseProcedure
{ {
/** /**
* Get a link by id * Get a link by id

View File

@@ -1,16 +1,16 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Model\SubtaskModel; use Kanboard\Model\SubtaskModel;
/** /**
* Me API controller * Me API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class MeApi extends BaseApi class MeProcedure extends BaseProcedure
{ {
public function getMe() public function getMe()
{ {

View File

@@ -1,55 +1,69 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base; use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Core\Security\Role; use Kanboard\Core\Security\Role;
/** /**
* Project Permission API controller * Project Permission API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class ProjectPermissionApi extends Base class ProjectPermissionProcedure extends BaseProcedure
{ {
public function getProjectUsers($project_id) public function getProjectUsers($project_id)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectUsers', $project_id);
return $this->projectUserRoleModel->getAllUsers($project_id); return $this->projectUserRoleModel->getAllUsers($project_id);
} }
public function getAssignableUsers($project_id, $prepend_unassigned = false) public function getAssignableUsers($project_id, $prepend_unassigned = false)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAssignableUsers', $project_id);
return $this->projectUserRoleModel->getAssignableUsersList($project_id, $prepend_unassigned); return $this->projectUserRoleModel->getAssignableUsersList($project_id, $prepend_unassigned);
} }
public function addProjectUser($project_id, $user_id, $role = Role::PROJECT_MEMBER) public function addProjectUser($project_id, $user_id, $role = Role::PROJECT_MEMBER)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addProjectUser', $project_id);
return $this->projectUserRoleModel->addUser($project_id, $user_id, $role); return $this->projectUserRoleModel->addUser($project_id, $user_id, $role);
} }
public function addProjectGroup($project_id, $group_id, $role = Role::PROJECT_MEMBER) public function addProjectGroup($project_id, $group_id, $role = Role::PROJECT_MEMBER)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addProjectGroup', $project_id);
return $this->projectGroupRoleModel->addGroup($project_id, $group_id, $role); return $this->projectGroupRoleModel->addGroup($project_id, $group_id, $role);
} }
public function removeProjectUser($project_id, $user_id) public function removeProjectUser($project_id, $user_id)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectUser', $project_id);
return $this->projectUserRoleModel->removeUser($project_id, $user_id); return $this->projectUserRoleModel->removeUser($project_id, $user_id);
} }
public function removeProjectGroup($project_id, $group_id) public function removeProjectGroup($project_id, $group_id)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectGroup', $project_id);
return $this->projectGroupRoleModel->removeGroup($project_id, $group_id); return $this->projectGroupRoleModel->removeGroup($project_id, $group_id);
} }
public function changeProjectUserRole($project_id, $user_id, $role) public function changeProjectUserRole($project_id, $user_id, $role)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeProjectUserRole', $project_id);
return $this->projectUserRoleModel->changeUserRole($project_id, $user_id, $role); return $this->projectUserRoleModel->changeUserRole($project_id, $user_id, $role);
} }
public function changeProjectGroupRole($project_id, $group_id, $role) public function changeProjectGroupRole($project_id, $group_id, $role)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeProjectGroupRole', $project_id);
return $this->projectGroupRoleModel->changeGroupRole($project_id, $group_id, $role); return $this->projectGroupRoleModel->changeGroupRole($project_id, $group_id, $role);
} }
public function getProjectUserRole($project_id, $user_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectUserRole', $project_id);
return $this->projectUserRoleModel->getUserRole($project_id, $user_id);
}
} }

View File

@@ -0,0 +1,106 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Project API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class ProjectProcedure extends BaseProcedure
{
public function getProjectById($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectById', $project_id);
return $this->formatProject($this->projectModel->getById($project_id));
}
public function getProjectByName($name)
{
$project = $this->projectModel->getByName($name);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByName', $project['id']);
return $this->formatProject($project);
}
public function getAllProjects()
{
return $this->formatProjects($this->projectModel->getAll());
}
public function removeProject($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProject', $project_id);
return $this->projectModel->remove($project_id);
}
public function enableProject($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableProject', $project_id);
return $this->projectModel->enable($project_id);
}
public function disableProject($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableProject', $project_id);
return $this->projectModel->disable($project_id);
}
public function enableProjectPublicAccess($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableProjectPublicAccess', $project_id);
return $this->projectModel->enablePublicAccess($project_id);
}
public function disableProjectPublicAccess($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableProjectPublicAccess', $project_id);
return $this->projectModel->disablePublicAccess($project_id);
}
public function getProjectActivities(array $project_ids)
{
foreach ($project_ids as $project_id) {
ProjectAuthorization::getInstance($this->container)
->check($this->getClassName(), 'getProjectActivities', $project_id);
}
return $this->helper->projectActivity->getProjectsEvents($project_ids);
}
public function getProjectActivity($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectActivity', $project_id);
return $this->helper->projectActivity->getProjectEvents($project_id);
}
public function createProject($name, $description = null, $owner_id = 0, $identifier = null)
{
$values = array(
'name' => $name,
'description' => $description,
'identifier' => $identifier,
);
list($valid, ) = $this->projectValidator->validateCreation($values);
return $valid ? $this->projectModel->create($values, $owner_id, $this->userSession->isLogged()) : false;
}
public function updateProject($project_id, $name, $description = null, $owner_id = null, $identifier = null)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateProject', $project_id);
$values = $this->filterValues(array(
'id' => $project_id,
'name' => $name,
'description' => $description,
'owner_id' => $owner_id,
'identifier' => $identifier,
));
list($valid, ) = $this->projectValidator->validateModification($values);
return $valid && $this->projectModel->update($values);
}
}

View File

@@ -1,34 +1,40 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base; use Kanboard\Api\Authorization\SubtaskAuthorization;
use Kanboard\Api\Authorization\TaskAuthorization;
/** /**
* Subtask API controller * Subtask API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class SubtaskApi extends Base class SubtaskProcedure extends BaseProcedure
{ {
public function getSubtask($subtask_id) public function getSubtask($subtask_id)
{ {
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSubtask', $subtask_id);
return $this->subtaskModel->getById($subtask_id); return $this->subtaskModel->getById($subtask_id);
} }
public function getAllSubtasks($task_id) public function getAllSubtasks($task_id)
{ {
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllSubtasks', $task_id);
return $this->subtaskModel->getAll($task_id); return $this->subtaskModel->getAll($task_id);
} }
public function removeSubtask($subtask_id) public function removeSubtask($subtask_id)
{ {
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeSubtask', $subtask_id);
return $this->subtaskModel->remove($subtask_id); return $this->subtaskModel->remove($subtask_id);
} }
public function createSubtask($task_id, $title, $user_id = 0, $time_estimated = 0, $time_spent = 0, $status = 0) public function createSubtask($task_id, $title, $user_id = 0, $time_estimated = 0, $time_spent = 0, $status = 0)
{ {
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createSubtask', $task_id);
$values = array( $values = array(
'title' => $title, 'title' => $title,
'task_id' => $task_id, 'task_id' => $task_id,
@@ -44,6 +50,8 @@ class SubtaskApi extends Base
public function updateSubtask($id, $task_id, $title = null, $user_id = null, $time_estimated = null, $time_spent = null, $status = null) public function updateSubtask($id, $task_id, $title = null, $user_id = null, $time_estimated = null, $time_spent = null, $status = null)
{ {
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateSubtask', $task_id);
$values = array( $values = array(
'id' => $id, 'id' => $id,
'task_id' => $task_id, 'task_id' => $task_id,

View File

@@ -0,0 +1,39 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\SubtaskAuthorization;
/**
* Subtask Time Tracking API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
* @author Nikolaos Georgakis
*/
class SubtaskTimeTrackingProcedure extends BaseProcedure
{
public function hasSubtaskTimer($subtask_id, $user_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'hasSubtaskTimer', $subtask_id);
return $this->subtaskTimeTrackingModel->hasTimer($subtask_id, $user_id);
}
public function logSubtaskStartTime($subtask_id, $user_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'logSubtaskStartTime', $subtask_id);
return $this->subtaskTimeTrackingModel->logStartTime($subtask_id, $user_id);
}
public function logSubtaskEndTime($subtask_id,$user_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'logSubtaskEndTime', $subtask_id);
return $this->subtaskTimeTrackingModel->logEndTime($subtask_id, $user_id);
}
public function getSubtaskTimeSpent($subtask_id,$user_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSubtaskTimeSpent', $subtask_id);
return $this->subtaskTimeTrackingModel->getTimeSpent($subtask_id, $user_id);
}
}

View File

@@ -1,34 +1,39 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base; use Kanboard\Api\Authorization\ProjectAuthorization;
/** /**
* Swimlane API controller * Swimlane API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class SwimlaneApi extends Base class SwimlaneProcedure extends BaseProcedure
{ {
public function getActiveSwimlanes($project_id) public function getActiveSwimlanes($project_id)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getActiveSwimlanes', $project_id);
return $this->swimlaneModel->getSwimlanes($project_id); return $this->swimlaneModel->getSwimlanes($project_id);
} }
public function getAllSwimlanes($project_id) public function getAllSwimlanes($project_id)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllSwimlanes', $project_id);
return $this->swimlaneModel->getAll($project_id); return $this->swimlaneModel->getAll($project_id);
} }
public function getSwimlaneById($swimlane_id) public function getSwimlaneById($swimlane_id)
{ {
return $this->swimlaneModel->getById($swimlane_id); $swimlane = $this->swimlaneModel->getById($swimlane_id);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSwimlaneById', $swimlane['project_id']);
return $swimlane;
} }
public function getSwimlaneByName($project_id, $name) public function getSwimlaneByName($project_id, $name)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSwimlaneByName', $project_id);
return $this->swimlaneModel->getByName($project_id, $name); return $this->swimlaneModel->getByName($project_id, $name);
} }
@@ -39,11 +44,13 @@ class SwimlaneApi extends Base
public function getDefaultSwimlane($project_id) public function getDefaultSwimlane($project_id)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getDefaultSwimlane', $project_id);
return $this->swimlaneModel->getDefault($project_id); return $this->swimlaneModel->getDefault($project_id);
} }
public function addSwimlane($project_id, $name, $description = '') public function addSwimlane($project_id, $name, $description = '')
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addSwimlane', $project_id);
return $this->swimlaneModel->create(array('project_id' => $project_id, 'name' => $name, 'description' => $description)); return $this->swimlaneModel->create(array('project_id' => $project_id, 'name' => $name, 'description' => $description));
} }
@@ -60,21 +67,25 @@ class SwimlaneApi extends Base
public function removeSwimlane($project_id, $swimlane_id) public function removeSwimlane($project_id, $swimlane_id)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeSwimlane', $project_id);
return $this->swimlaneModel->remove($project_id, $swimlane_id); return $this->swimlaneModel->remove($project_id, $swimlane_id);
} }
public function disableSwimlane($project_id, $swimlane_id) public function disableSwimlane($project_id, $swimlane_id)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableSwimlane', $project_id);
return $this->swimlaneModel->disable($project_id, $swimlane_id); return $this->swimlaneModel->disable($project_id, $swimlane_id);
} }
public function enableSwimlane($project_id, $swimlane_id) public function enableSwimlane($project_id, $swimlane_id)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableSwimlane', $project_id);
return $this->swimlaneModel->enable($project_id, $swimlane_id); return $this->swimlaneModel->enable($project_id, $swimlane_id);
} }
public function changeSwimlanePosition($project_id, $swimlane_id, $position) public function changeSwimlanePosition($project_id, $swimlane_id, $position)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeSwimlanePosition', $project_id);
return $this->swimlaneModel->changePosition($project_id, $swimlane_id, $position); return $this->swimlaneModel->changePosition($project_id, $swimlane_id, $position);
} }
} }

View File

@@ -1,29 +1,36 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Api\Authorization\TaskAuthorization;
use Kanboard\Api\Authorization\TaskFileAuthorization;
use Kanboard\Core\ObjectStorage\ObjectStorageException; use Kanboard\Core\ObjectStorage\ObjectStorageException;
/** /**
* Task File API controller * Task File API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class TaskFileApi extends BaseApi class TaskFileProcedure extends BaseProcedure
{ {
public function getTaskFile($file_id) public function getTaskFile($file_id)
{ {
TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskFile', $file_id);
return $this->taskFileModel->getById($file_id); return $this->taskFileModel->getById($file_id);
} }
public function getAllTaskFiles($task_id) public function getAllTaskFiles($task_id)
{ {
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTaskFiles', $task_id);
return $this->taskFileModel->getAll($task_id); return $this->taskFileModel->getAll($task_id);
} }
public function downloadTaskFile($file_id) public function downloadTaskFile($file_id)
{ {
TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'downloadTaskFile', $file_id);
try { try {
$file = $this->taskFileModel->getById($file_id); $file = $this->taskFileModel->getById($file_id);
@@ -39,6 +46,8 @@ class TaskFileApi extends BaseApi
public function createTaskFile($project_id, $task_id, $filename, $blob) public function createTaskFile($project_id, $task_id, $filename, $blob)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskFile', $project_id);
try { try {
return $this->taskFileModel->uploadContent($task_id, $filename, $blob); return $this->taskFileModel->uploadContent($task_id, $filename, $blob);
} catch (ObjectStorageException $e) { } catch (ObjectStorageException $e) {
@@ -49,11 +58,13 @@ class TaskFileApi extends BaseApi
public function removeTaskFile($file_id) public function removeTaskFile($file_id)
{ {
TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskFile', $file_id);
return $this->taskFileModel->remove($file_id); return $this->taskFileModel->remove($file_id);
} }
public function removeAllTaskFiles($task_id) public function removeAllTaskFiles($task_id)
{ {
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAllTaskFiles', $task_id);
return $this->taskFileModel->removeAll($task_id); return $this->taskFileModel->removeAll($task_id);
} }
} }

View File

@@ -1,16 +1,17 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Core\Base; use Kanboard\Api\Authorization\TaskAuthorization;
use Kanboard\Api\Authorization\TaskLinkAuthorization;
/** /**
* TaskLink API controller * TaskLink API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class TaskLinkApi extends Base class TaskLinkProcedure extends BaseProcedure
{ {
/** /**
* Get a task link * Get a task link
@@ -21,6 +22,7 @@ class TaskLinkApi extends Base
*/ */
public function getTaskLinkById($task_link_id) public function getTaskLinkById($task_link_id)
{ {
TaskLinkAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskLinkById', $task_link_id);
return $this->taskLinkModel->getById($task_link_id); return $this->taskLinkModel->getById($task_link_id);
} }
@@ -33,6 +35,7 @@ class TaskLinkApi extends Base
*/ */
public function getAllTaskLinks($task_id) public function getAllTaskLinks($task_id)
{ {
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTaskLinks', $task_id);
return $this->taskLinkModel->getAll($task_id); return $this->taskLinkModel->getAll($task_id);
} }
@@ -47,6 +50,7 @@ class TaskLinkApi extends Base
*/ */
public function createTaskLink($task_id, $opposite_task_id, $link_id) public function createTaskLink($task_id, $opposite_task_id, $link_id)
{ {
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskLink', $task_id);
return $this->taskLinkModel->create($task_id, $opposite_task_id, $link_id); return $this->taskLinkModel->create($task_id, $opposite_task_id, $link_id);
} }
@@ -62,6 +66,7 @@ class TaskLinkApi extends Base
*/ */
public function updateTaskLink($task_link_id, $task_id, $opposite_task_id, $link_id) public function updateTaskLink($task_link_id, $task_id, $opposite_task_id, $link_id)
{ {
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTaskLink', $task_id);
return $this->taskLinkModel->update($task_link_id, $task_id, $opposite_task_id, $link_id); return $this->taskLinkModel->update($task_link_id, $task_id, $opposite_task_id, $link_id);
} }
@@ -74,6 +79,7 @@ class TaskLinkApi extends Base
*/ */
public function removeTaskLink($task_link_id) public function removeTaskLink($task_link_id)
{ {
TaskLinkAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskLink', $task_link_id);
return $this->taskLinkModel->remove($task_link_id); return $this->taskLinkModel->remove($task_link_id);
} }
} }

View File

@@ -1,39 +1,41 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Api\Authorization\TaskAuthorization;
use Kanboard\Filter\TaskProjectFilter; use Kanboard\Filter\TaskProjectFilter;
use Kanboard\Model\TaskModel; use Kanboard\Model\TaskModel;
/** /**
* Task API controller * Task API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class TaskApi extends BaseApi class TaskProcedure extends BaseProcedure
{ {
public function searchTasks($project_id, $query) public function searchTasks($project_id, $query)
{ {
$this->checkProjectPermission($project_id); ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'searchTasks', $project_id);
return $this->taskLexer->build($query)->withFilter(new TaskProjectFilter($project_id))->toArray(); return $this->taskLexer->build($query)->withFilter(new TaskProjectFilter($project_id))->toArray();
} }
public function getTask($task_id) public function getTask($task_id)
{ {
$this->checkTaskPermission($task_id); TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTask', $task_id);
return $this->formatTask($this->taskFinderModel->getById($task_id)); return $this->formatTask($this->taskFinderModel->getById($task_id));
} }
public function getTaskByReference($project_id, $reference) public function getTaskByReference($project_id, $reference)
{ {
$this->checkProjectPermission($project_id); ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskByReference', $project_id);
return $this->formatTask($this->taskFinderModel->getByReference($project_id, $reference)); return $this->formatTask($this->taskFinderModel->getByReference($project_id, $reference));
} }
public function getAllTasks($project_id, $status_id = TaskModel::STATUS_OPEN) public function getAllTasks($project_id, $status_id = TaskModel::STATUS_OPEN)
{ {
$this->checkProjectPermission($project_id); ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTasks', $project_id);
return $this->formatTasks($this->taskFinderModel->getAll($project_id, $status_id)); return $this->formatTasks($this->taskFinderModel->getAll($project_id, $status_id));
} }
@@ -44,40 +46,43 @@ class TaskApi extends BaseApi
public function getOverdueTasksByProject($project_id) public function getOverdueTasksByProject($project_id)
{ {
$this->checkProjectPermission($project_id); ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getOverdueTasksByProject', $project_id);
return $this->taskFinderModel->getOverdueTasksByProject($project_id); return $this->taskFinderModel->getOverdueTasksByProject($project_id);
} }
public function openTask($task_id) public function openTask($task_id)
{ {
$this->checkTaskPermission($task_id); TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'openTask', $task_id);
return $this->taskStatusModel->open($task_id); return $this->taskStatusModel->open($task_id);
} }
public function closeTask($task_id) public function closeTask($task_id)
{ {
$this->checkTaskPermission($task_id); TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'closeTask', $task_id);
return $this->taskStatusModel->close($task_id); return $this->taskStatusModel->close($task_id);
} }
public function removeTask($task_id) public function removeTask($task_id)
{ {
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTask', $task_id);
return $this->taskModel->remove($task_id); return $this->taskModel->remove($task_id);
} }
public function moveTaskPosition($project_id, $task_id, $column_id, $position, $swimlane_id = 0) public function moveTaskPosition($project_id, $task_id, $column_id, $position, $swimlane_id = 0)
{ {
$this->checkProjectPermission($project_id); ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskPosition', $project_id);
return $this->taskPositionModel->movePosition($project_id, $task_id, $column_id, $position, $swimlane_id); return $this->taskPositionModel->movePosition($project_id, $task_id, $column_id, $position, $swimlane_id);
} }
public function moveTaskToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null) public function moveTaskToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskToProject', $project_id);
return $this->taskDuplicationModel->moveToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id); return $this->taskDuplicationModel->moveToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id);
} }
public function duplicateTaskToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null) public function duplicateTaskToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null)
{ {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'duplicateTaskToProject', $project_id);
return $this->taskDuplicationModel->duplicateToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id); return $this->taskDuplicationModel->duplicateToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id);
} }
@@ -86,8 +91,8 @@ class TaskApi extends BaseApi
$recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0, $recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0,
$recurrence_basedate = 0, $reference = '') $recurrence_basedate = 0, $reference = '')
{ {
$this->checkProjectPermission($project_id); ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTask', $project_id);
if ($owner_id !== 0 && ! $this->projectPermissionModel->isAssignable($project_id, $owner_id)) { if ($owner_id !== 0 && ! $this->projectPermissionModel->isAssignable($project_id, $owner_id)) {
return false; return false;
} }
@@ -127,8 +132,7 @@ class TaskApi extends BaseApi
$recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null, $recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null,
$recurrence_timeframe = null, $recurrence_basedate = null, $reference = null) $recurrence_timeframe = null, $recurrence_basedate = null, $reference = null)
{ {
$this->checkTaskPermission($id); TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTask', $id);
$project_id = $this->taskFinderModel->getProjectId($id); $project_id = $this->taskFinderModel->getProjectId($id);
if ($project_id === 0) { if ($project_id === 0) {

View File

@@ -1,6 +1,6 @@
<?php <?php
namespace Kanboard\Api; namespace Kanboard\Api\Procedure;
use LogicException; use LogicException;
use Kanboard\Core\Security\Role; use Kanboard\Core\Security\Role;
@@ -11,10 +11,10 @@ use Kanboard\Core\Ldap\User as LdapUser;
/** /**
* User API controller * User API controller
* *
* @package Kanboard\Api * @package Kanboard\Api\Procedure
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class UserApi extends BaseApi class UserProcedure extends BaseProcedure
{ {
public function getUser($user_id) public function getUser($user_id)
{ {

View File

@@ -1,87 +0,0 @@
<?php
namespace Kanboard\Api;
/**
* Project API controller
*
* @package Kanboard\Api
* @author Frederic Guillot
*/
class ProjectApi extends BaseApi
{
public function getProjectById($project_id)
{
$this->checkProjectPermission($project_id);
return $this->formatProject($this->projectModel->getById($project_id));
}
public function getProjectByName($name)
{
return $this->formatProject($this->projectModel->getByName($name));
}
public function getAllProjects()
{
return $this->formatProjects($this->projectModel->getAll());
}
public function removeProject($project_id)
{
return $this->projectModel->remove($project_id);
}
public function enableProject($project_id)
{
return $this->projectModel->enable($project_id);
}
public function disableProject($project_id)
{
return $this->projectModel->disable($project_id);
}
public function enableProjectPublicAccess($project_id)
{
return $this->projectModel->enablePublicAccess($project_id);
}
public function disableProjectPublicAccess($project_id)
{
return $this->projectModel->disablePublicAccess($project_id);
}
public function getProjectActivities(array $project_ids)
{
return $this->helper->projectActivity->getProjectsEvents($project_ids);
}
public function getProjectActivity($project_id)
{
$this->checkProjectPermission($project_id);
return $this->helper->projectActivity->getProjectEvents($project_id);
}
public function createProject($name, $description = null)
{
$values = array(
'name' => $name,
'description' => $description
);
list($valid, ) = $this->projectValidator->validateCreation($values);
return $valid ? $this->projectModel->create($values) : false;
}
public function updateProject($project_id, $name, $description = null)
{
$values = $this->filterValues(array(
'id' => $project_id,
'name' => $name,
'description' => $description
));
list($valid, ) = $this->projectValidator->validateModification($values);
return $valid && $this->projectModel->update($values);
}
}

View File

@@ -1,34 +0,0 @@
<?php
namespace Kanboard\Api;
use Kanboard\Core\Base;
/**
* Subtask Time Tracking API controller
*
* @package api
* @author Nikolaos Georgakis
*/
class SubtaskTimeTrackingApi extends Base
{
public function hasTimer($subtask_id,$user_id)
{
return $this->subtaskTimeTrackingModel->hasTimer($subtask_id,$user_id);
}
public function logStartTime($subtask_id,$user_id)
{
return $this->subtaskTimeTrackingModel->logStartTime($subtask_id,$user_id);
}
public function logEndTime($subtask_id,$user_id)
{
return $this->subtaskTimeTrackingModel->logEndTime($subtask_id,$user_id);
}
public function getTimeSpent($subtask_id,$user_id)
{
return $this->subtaskTimeTrackingModel->getTimeSpent($subtask_id,$user_id);
}
}

View File

@@ -35,8 +35,12 @@ use Pimple\Container;
* @property \Kanboard\Core\Security\AuthenticationManager $authenticationManager * @property \Kanboard\Core\Security\AuthenticationManager $authenticationManager
* @property \Kanboard\Core\Security\AccessMap $applicationAccessMap * @property \Kanboard\Core\Security\AccessMap $applicationAccessMap
* @property \Kanboard\Core\Security\AccessMap $projectAccessMap * @property \Kanboard\Core\Security\AccessMap $projectAccessMap
* @property \Kanboard\Core\Security\AccessMap $apiAccessMap
* @property \Kanboard\Core\Security\AccessMap $apiProjectAccessMap
* @property \Kanboard\Core\Security\Authorization $applicationAuthorization * @property \Kanboard\Core\Security\Authorization $applicationAuthorization
* @property \Kanboard\Core\Security\Authorization $projectAuthorization * @property \Kanboard\Core\Security\Authorization $projectAuthorization
* @property \Kanboard\Core\Security\Authorization $apiAuthorization
* @property \Kanboard\Core\Security\Authorization $apiProjectAuthorization
* @property \Kanboard\Core\Security\Role $role * @property \Kanboard\Core\Security\Role $role
* @property \Kanboard\Core\Security\Token $token * @property \Kanboard\Core\Security\Token $token
* @property \Kanboard\Core\Session\FlashMessage $flash * @property \Kanboard\Core\Session\FlashMessage $flash

View File

@@ -85,6 +85,18 @@ class ActionModel extends Base
return $action; return $action;
} }
/**
* Get the projectId by the actionId
*
* @access public
* @param integer $action_id
* @return integer
*/
public function getProjectId($action_id)
{
return $this->db->table(self::TABLE)->eq('id', $action_id)->findOneColumn('project_id') ?: 0;
}
/** /**
* Attach parameters to actions * Attach parameters to actions
* *

View File

@@ -55,6 +55,18 @@ class CategoryModel extends Base
return $this->db->table(self::TABLE)->eq('id', $category_id)->findOneColumn('name') ?: ''; return $this->db->table(self::TABLE)->eq('id', $category_id)->findOneColumn('name') ?: '';
} }
/**
* Get the projectId by the category id
*
* @access public
* @param integer $category_id Category id
* @return integer
*/
public function getProjectId($category_id)
{
return $this->db->table(self::TABLE)->eq('id', $category_id)->findOneColumn('project_id') ?: 0;
}
/** /**
* Get a category id by the category name and project id * Get a category id by the category name and project id
* *

View File

@@ -31,6 +31,18 @@ class ColumnModel extends Base
return $this->db->table(self::TABLE)->eq('id', $column_id)->findOne(); return $this->db->table(self::TABLE)->eq('id', $column_id)->findOne();
} }
/**
* Get projectId by the columnId
*
* @access public
* @param integer $column_id Column id
* @return integer
*/
public function getProjectId($column_id)
{
return $this->db->table(self::TABLE)->eq('id', $column_id)->findOneColumn('project_id');
}
/** /**
* Get the first column id for a given project * Get the first column id for a given project
* *

View File

@@ -29,6 +29,22 @@ class CommentModel extends Base
const EVENT_CREATE = 'comment.create'; const EVENT_CREATE = 'comment.create';
const EVENT_USER_MENTION = 'comment.user.mention'; const EVENT_USER_MENTION = 'comment.user.mention';
/**
* Get projectId from commentId
*
* @access public
* @param integer $comment_id
* @return integer
*/
public function getProjectId($comment_id)
{
return $this->db
->table(self::TABLE)
->eq(self::TABLE.'.id', $comment_id)
->join(TaskModel::TABLE, 'id', 'task_id')
->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0;
}
/** /**
* Get all comments for a given task * Get all comments for a given task
* *

View File

@@ -51,6 +51,22 @@ class SubtaskModel extends Base
const EVENT_CREATE = 'subtask.create'; const EVENT_CREATE = 'subtask.create';
const EVENT_DELETE = 'subtask.delete'; const EVENT_DELETE = 'subtask.delete';
/**
* Get projectId from subtaskId
*
* @access public
* @param integer $subtask_id
* @return integer
*/
public function getProjectId($subtask_id)
{
return $this->db
->table(self::TABLE)
->eq(self::TABLE.'.id', $subtask_id)
->join(TaskModel::TABLE, 'id', 'task_id')
->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0;
}
/** /**
* Get available status * Get available status
* *

View File

@@ -72,6 +72,22 @@ class TaskFileModel extends FileModel
return self::EVENT_CREATE; return self::EVENT_CREATE;
} }
/**
* Get projectId from fileId
*
* @access public
* @param integer $file_id
* @return integer
*/
public function getProjectId($file_id)
{
return $this->db
->table(self::TABLE)
->eq(self::TABLE.'.id', $file_id)
->join(TaskModel::TABLE, 'id', 'task_id')
->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0;
}
/** /**
* Handle screenshot upload * Handle screenshot upload
* *

View File

@@ -28,6 +28,22 @@ class TaskLinkModel extends Base
*/ */
const EVENT_CREATE_UPDATE = 'tasklink.create_update'; const EVENT_CREATE_UPDATE = 'tasklink.create_update';
/**
* Get projectId from $task_link_id
*
* @access public
* @param integer $task_link_id
* @return integer
*/
public function getProjectId($task_link_id)
{
return $this->db
->table(self::TABLE)
->eq(self::TABLE.'.id', $task_link_id)
->join(TaskModel::TABLE, 'id', 'task_id')
->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0;
}
/** /**
* Get a task link * Get a task link
* *

View File

@@ -3,26 +3,26 @@
namespace Kanboard\ServiceProvider; namespace Kanboard\ServiceProvider;
use JsonRPC\Server; use JsonRPC\Server;
use Kanboard\Api\ActionApi; use Kanboard\Api\Procedure\ActionProcedure;
use Kanboard\Api\AppApi; use Kanboard\Api\Procedure\AppProcedure;
use Kanboard\Api\BoardApi; use Kanboard\Api\Procedure\BoardProcedure;
use Kanboard\Api\CategoryApi; use Kanboard\Api\Procedure\CategoryProcedure;
use Kanboard\Api\ColumnApi; use Kanboard\Api\Procedure\ColumnProcedure;
use Kanboard\Api\CommentApi; use Kanboard\Api\Procedure\CommentProcedure;
use Kanboard\Api\TaskFileApi; use Kanboard\Api\Procedure\TaskFileProcedure;
use Kanboard\Api\GroupApi; use Kanboard\Api\Procedure\GroupProcedure;
use Kanboard\Api\GroupMemberApi; use Kanboard\Api\Procedure\GroupMemberProcedure;
use Kanboard\Api\LinkApi; use Kanboard\Api\Procedure\LinkProcedure;
use Kanboard\Api\MeApi; use Kanboard\Api\Procedure\MeProcedure;
use Kanboard\Api\Middleware\AuthenticationApiMiddleware; use Kanboard\Api\Middleware\AuthenticationMiddleware;
use Kanboard\Api\ProjectApi; use Kanboard\Api\Procedure\ProjectProcedure;
use Kanboard\Api\ProjectPermissionApi; use Kanboard\Api\Procedure\ProjectPermissionProcedure;
use Kanboard\Api\SubtaskApi; use Kanboard\Api\Procedure\SubtaskProcedure;
use Kanboard\Api\SubtaskTimeTrackingApi; use Kanboard\Api\Procedure\SubtaskTimeTrackingProcedure;
use Kanboard\Api\SwimlaneApi; use Kanboard\Api\Procedure\SwimlaneProcedure;
use Kanboard\Api\TaskApi; use Kanboard\Api\Procedure\TaskProcedure;
use Kanboard\Api\TaskLinkApi; use Kanboard\Api\Procedure\TaskLinkProcedure;
use Kanboard\Api\UserApi; use Kanboard\Api\Procedure\UserProcedure;
use Pimple\Container; use Pimple\Container;
use Pimple\ServiceProviderInterface; use Pimple\ServiceProviderInterface;
@@ -45,31 +45,32 @@ class ApiProvider implements ServiceProviderInterface
$server = new Server(); $server = new Server();
$server->setAuthenticationHeader(API_AUTHENTICATION_HEADER); $server->setAuthenticationHeader(API_AUTHENTICATION_HEADER);
$server->getMiddlewareHandler() $server->getMiddlewareHandler()
->withMiddleware(new AuthenticationApiMiddleware($container)) ->withMiddleware(new AuthenticationMiddleware($container))
; ;
$server->getProcedureHandler() $server->getProcedureHandler()
->withObject(new MeApi($container)) ->withObject(new MeProcedure($container))
->withObject(new ActionApi($container)) ->withObject(new ActionProcedure($container))
->withObject(new AppApi($container)) ->withObject(new AppProcedure($container))
->withObject(new BoardApi($container)) ->withObject(new BoardProcedure($container))
->withObject(new ColumnApi($container)) ->withObject(new ColumnProcedure($container))
->withObject(new CategoryApi($container)) ->withObject(new CategoryProcedure($container))
->withObject(new CommentApi($container)) ->withObject(new CommentProcedure($container))
->withObject(new TaskFileApi($container)) ->withObject(new TaskFileProcedure($container))
->withObject(new LinkApi($container)) ->withObject(new LinkProcedure($container))
->withObject(new ProjectApi($container)) ->withObject(new ProjectProcedure($container))
->withObject(new ProjectPermissionApi($container)) ->withObject(new ProjectPermissionProcedure($container))
->withObject(new SubtaskApi($container)) ->withObject(new SubtaskProcedure($container))
->withObject(new SubtaskTimeTrackingApi($container)) ->withObject(new SubtaskTimeTrackingProcedure($container))
->withObject(new SwimlaneApi($container)) ->withObject(new SwimlaneProcedure($container))
->withObject(new TaskApi($container)) ->withObject(new TaskProcedure($container))
->withObject(new TaskLinkApi($container)) ->withObject(new TaskLinkProcedure($container))
->withObject(new UserApi($container)) ->withObject(new UserProcedure($container))
->withObject(new GroupApi($container)) ->withObject(new GroupProcedure($container))
->withObject(new GroupMemberApi($container)) ->withObject(new GroupMemberProcedure($container))
->withBeforeMethod('beforeProcedure')
; ;
$container['api'] = $server; $container['api'] = $server;
return $container; return $container;
} }

View File

@@ -46,9 +46,13 @@ class AuthenticationProvider implements ServiceProviderInterface
$container['projectAccessMap'] = $this->getProjectAccessMap(); $container['projectAccessMap'] = $this->getProjectAccessMap();
$container['applicationAccessMap'] = $this->getApplicationAccessMap(); $container['applicationAccessMap'] = $this->getApplicationAccessMap();
$container['apiAccessMap'] = $this->getApiAccessMap();
$container['apiProjectAccessMap'] = $this->getApiProjectAccessMap();
$container['projectAuthorization'] = new Authorization($container['projectAccessMap']); $container['projectAuthorization'] = new Authorization($container['projectAccessMap']);
$container['applicationAuthorization'] = new Authorization($container['applicationAccessMap']); $container['applicationAuthorization'] = new Authorization($container['applicationAccessMap']);
$container['apiAuthorization'] = new Authorization($container['apiAccessMap']);
$container['apiProjectAuthorization'] = new Authorization($container['apiProjectAccessMap']);
return $container; return $container;
} }
@@ -151,4 +155,57 @@ class AuthenticationProvider implements ServiceProviderInterface
return $acl; return $acl;
} }
/**
* Get ACL for the API
*
* @access public
* @return AccessMap
*/
public function getApiAccessMap()
{
$acl = new AccessMap;
$acl->setDefaultRole(Role::APP_USER);
$acl->setRoleHierarchy(Role::APP_ADMIN, array(Role::APP_MANAGER, Role::APP_USER, Role::APP_PUBLIC));
$acl->setRoleHierarchy(Role::APP_MANAGER, array(Role::APP_USER, Role::APP_PUBLIC));
$acl->add('UserProcedure', '*', Role::APP_ADMIN);
$acl->add('GroupMemberProcedure', '*', Role::APP_ADMIN);
$acl->add('GroupProcedure', '*', Role::APP_ADMIN);
$acl->add('LinkProcedure', '*', Role::APP_ADMIN);
$acl->add('TaskProcedure', array('getOverdueTasks'), Role::APP_ADMIN);
$acl->add('ProjectProcedure', array('getAllProjects'), Role::APP_ADMIN);
$acl->add('ProjectProcedure', array('createProject'), Role::APP_MANAGER);
return $acl;
}
/**
* Get ACL for the API
*
* @access public
* @return AccessMap
*/
public function getApiProjectAccessMap()
{
$acl = new AccessMap;
$acl->setDefaultRole(Role::PROJECT_VIEWER);
$acl->setRoleHierarchy(Role::PROJECT_MANAGER, array(Role::PROJECT_MEMBER, Role::PROJECT_VIEWER));
$acl->setRoleHierarchy(Role::PROJECT_MEMBER, array(Role::PROJECT_VIEWER));
$acl->add('ActionProcedure', array('removeAction', 'getActions', 'createAction'), Role::PROJECT_MANAGER);
$acl->add('CategoryProcedure', '*', Role::PROJECT_MANAGER);
$acl->add('ColumnProcedure', '*', Role::PROJECT_MANAGER);
$acl->add('CommentProcedure', array('removeComment', 'createComment', 'updateComment'), Role::PROJECT_MEMBER);
$acl->add('ProjectPermissionProcedure', '*', Role::PROJECT_MANAGER);
$acl->add('ProjectProcedure', array('updateProject', 'removeProject', 'enableProject', 'disableProject', 'enableProjectPublicAccess', 'disableProjectPublicAccess'), Role::PROJECT_MANAGER);
$acl->add('SubtaskProcedure', '*', Role::PROJECT_MEMBER);
$acl->add('SubtaskTimeTrackingProcedure', '*', Role::PROJECT_MEMBER);
$acl->add('SwimlaneProcedure', '*', Role::PROJECT_MANAGER);
$acl->add('TaskFileProcedure', '*', Role::PROJECT_MEMBER);
$acl->add('TaskLinkProcedure', '*', Role::PROJECT_MEMBER);
$acl->add('TaskProcedure', '*', Role::PROJECT_MEMBER);
return $acl;
}
} }

View File

@@ -26,7 +26,7 @@
"christian-riesen/otp" : "1.4", "christian-riesen/otp" : "1.4",
"eluceo/ical": "0.8.0", "eluceo/ical": "0.8.0",
"erusev/parsedown" : "1.6.0", "erusev/parsedown" : "1.6.0",
"fguillot/json-rpc" : "1.2.0", "fguillot/json-rpc" : "1.2.1",
"fguillot/picodb" : "1.0.12", "fguillot/picodb" : "1.0.12",
"fguillot/simpleLogger" : "1.0.1", "fguillot/simpleLogger" : "1.0.1",
"fguillot/simple-validator" : "1.0.0", "fguillot/simple-validator" : "1.0.0",

261
composer.lock generated
View File

@@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"hash": "2de2026649db7bc41653bef80f974c6a", "hash": "283af0b856598f5bc3d8ee0b226959e5",
"content-hash": "ea8ef74f1f1cf53b9f96b7609d756873", "content-hash": "18c0bbff5406ceb8b567d9655de26746",
"packages": [ "packages": [
{ {
"name": "christian-riesen/base32", "name": "christian-riesen/base32",
@@ -203,16 +203,16 @@
}, },
{ {
"name": "fguillot/json-rpc", "name": "fguillot/json-rpc",
"version": "v1.2.0", "version": "v1.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/fguillot/JsonRPC.git", "url": "https://github.com/fguillot/JsonRPC.git",
"reference": "b002320b10aa1eeb7aee83f7b703cd6a6e99ff78" "reference": "d491bb549bfa11aff4c37abcea2ffb28c9523f69"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/b002320b10aa1eeb7aee83f7b703cd6a6e99ff78", "url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/d491bb549bfa11aff4c37abcea2ffb28c9523f69",
"reference": "b002320b10aa1eeb7aee83f7b703cd6a6e99ff78", "reference": "d491bb549bfa11aff4c37abcea2ffb28c9523f69",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -238,7 +238,7 @@
], ],
"description": "Simple Json-RPC client/server library that just works", "description": "Simple Json-RPC client/server library that just works",
"homepage": "https://github.com/fguillot/JsonRPC", "homepage": "https://github.com/fguillot/JsonRPC",
"time": "2016-05-29 13:06:36" "time": "2016-06-25 23:11:10"
}, },
{ {
"name": "fguillot/picodb", "name": "fguillot/picodb",
@@ -682,16 +682,16 @@
}, },
{ {
"name": "symfony/console", "name": "symfony/console",
"version": "v2.8.6", "version": "v2.8.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/console.git", "url": "https://github.com/symfony/console.git",
"reference": "48221d3de4dc22d2cd57c97e8b9361821da86609" "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/48221d3de4dc22d2cd57c97e8b9361821da86609", "url": "https://api.github.com/repos/symfony/console/zipball/5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3",
"reference": "48221d3de4dc22d2cd57c97e8b9361821da86609", "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -738,20 +738,20 @@
], ],
"description": "Symfony Console Component", "description": "Symfony Console Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2016-04-26 12:00:47" "time": "2016-06-06 15:06:25"
}, },
{ {
"name": "symfony/event-dispatcher", "name": "symfony/event-dispatcher",
"version": "v2.8.6", "version": "v2.8.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/event-dispatcher.git", "url": "https://github.com/symfony/event-dispatcher.git",
"reference": "a158f13992a3147d466af7a23b564ac719a4ddd8" "reference": "2a6b8713f8bdb582058cfda463527f195b066110"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a158f13992a3147d466af7a23b564ac719a4ddd8", "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2a6b8713f8bdb582058cfda463527f195b066110",
"reference": "a158f13992a3147d466af7a23b564ac719a4ddd8", "reference": "2a6b8713f8bdb582058cfda463527f195b066110",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -798,7 +798,7 @@
], ],
"description": "Symfony EventDispatcher Component", "description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2016-05-03 18:59:18" "time": "2016-06-06 11:11:27"
}, },
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
@@ -916,38 +916,87 @@
"time": "2015-06-14 21:17:01" "time": "2015-06-14 21:17:01"
}, },
{ {
"name": "phpdocumentor/reflection-docblock", "name": "phpdocumentor/reflection-common",
"version": "2.0.4", "version": "1.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
"reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
"reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.3" "php": ">=5.5"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "~4.0" "phpunit/phpunit": "^4.6"
},
"suggest": {
"dflydev/markdown": "~1.0",
"erusev/parsedown": "~1.0"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "2.0.x-dev" "dev-master": "1.0.x-dev"
} }
}, },
"autoload": { "autoload": {
"psr-0": { "psr-4": {
"phpDocumentor": [ "phpDocumentor\\Reflection\\": [
"src"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jaap van Otterdijk",
"email": "opensource@ijaap.nl"
}
],
"description": "Common reflection classes used by phpdocumentor to reflect the code structure",
"homepage": "http://www.phpdoc.org",
"keywords": [
"FQSEN",
"phpDocumentor",
"phpdoc",
"reflection",
"static analysis"
],
"time": "2015-12-27 11:43:31"
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "3.1.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "9270140b940ff02e58ec577c237274e92cd40cdd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9270140b940ff02e58ec577c237274e92cd40cdd",
"reference": "9270140b940ff02e58ec577c237274e92cd40cdd",
"shasum": ""
},
"require": {
"php": ">=5.5",
"phpdocumentor/reflection-common": "^1.0@dev",
"phpdocumentor/type-resolver": "^0.2.0",
"webmozart/assert": "^1.0"
},
"require-dev": {
"mockery/mockery": "^0.9.4",
"phpunit/phpunit": "^4.4"
},
"type": "library",
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": [
"src/" "src/"
] ]
} }
@@ -959,39 +1008,87 @@
"authors": [ "authors": [
{ {
"name": "Mike van Riel", "name": "Mike van Riel",
"email": "mike.vanriel@naenius.com" "email": "me@mikevanriel.com"
} }
], ],
"time": "2015-02-03 12:10:50" "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"time": "2016-06-10 09:48:41"
}, },
{ {
"name": "phpspec/prophecy", "name": "phpdocumentor/type-resolver",
"version": "v1.6.0", "version": "0.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpspec/prophecy.git", "url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "3c91bdf81797d725b14cb62906f9a4ce44235972" "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/3c91bdf81797d725b14cb62906f9a4ce44235972", "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b39c7a5b194f9ed7bd0dd345c751007a41862443",
"reference": "3c91bdf81797d725b14cb62906f9a4ce44235972", "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443",
"shasum": ""
},
"require": {
"php": ">=5.5",
"phpdocumentor/reflection-common": "^1.0"
},
"require-dev": {
"mockery/mockery": "^0.9.4",
"phpunit/phpunit": "^5.2||^4.8.24"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mike van Riel",
"email": "me@mikevanriel.com"
}
],
"time": "2016-06-10 07:14:17"
},
{
"name": "phpspec/prophecy",
"version": "v1.6.1",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "58a8137754bc24b25740d4281399a4a3596058e0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0",
"reference": "58a8137754bc24b25740d4281399a4a3596058e0",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"doctrine/instantiator": "^1.0.2", "doctrine/instantiator": "^1.0.2",
"php": "^5.3|^7.0", "php": "^5.3|^7.0",
"phpdocumentor/reflection-docblock": "~2.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2",
"sebastian/comparator": "~1.1", "sebastian/comparator": "^1.1",
"sebastian/recursion-context": "~1.0" "sebastian/recursion-context": "^1.0"
}, },
"require-dev": { "require-dev": {
"phpspec/phpspec": "~2.0" "phpspec/phpspec": "^2.0"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.5.x-dev" "dev-master": "1.6.x-dev"
} }
}, },
"autoload": { "autoload": {
@@ -1024,7 +1121,7 @@
"spy", "spy",
"stub" "stub"
], ],
"time": "2016-02-15 07:46:21" "time": "2016-06-07 08:13:47"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
@@ -1629,16 +1726,16 @@
}, },
{ {
"name": "sebastian/exporter", "name": "sebastian/exporter",
"version": "1.2.1", "version": "1.2.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git", "url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "7ae5513327cb536431847bcc0c10edba2701064e" "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4",
"reference": "7ae5513327cb536431847bcc0c10edba2701064e", "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1646,12 +1743,13 @@
"sebastian/recursion-context": "~1.0" "sebastian/recursion-context": "~1.0"
}, },
"require-dev": { "require-dev": {
"ext-mbstring": "*",
"phpunit/phpunit": "~4.4" "phpunit/phpunit": "~4.4"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "1.2.x-dev" "dev-master": "1.3.x-dev"
} }
}, },
"autoload": { "autoload": {
@@ -1691,7 +1789,7 @@
"export", "export",
"exporter" "exporter"
], ],
"time": "2015-06-21 07:55:53" "time": "2016-06-17 09:04:28"
}, },
{ {
"name": "sebastian/global-state", "name": "sebastian/global-state",
@@ -1834,16 +1932,16 @@
}, },
{ {
"name": "symfony/stopwatch", "name": "symfony/stopwatch",
"version": "v2.8.6", "version": "v2.8.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/stopwatch.git", "url": "https://github.com/symfony/stopwatch.git",
"reference": "9e24824b2a9a16e17ab997f61d70bc03948e434e" "reference": "5e628055488bcc42dbace3af65be435d094e37e4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/stopwatch/zipball/9e24824b2a9a16e17ab997f61d70bc03948e434e", "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5e628055488bcc42dbace3af65be435d094e37e4",
"reference": "9e24824b2a9a16e17ab997f61d70bc03948e434e", "reference": "5e628055488bcc42dbace3af65be435d094e37e4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1879,7 +1977,7 @@
], ],
"description": "Symfony Stopwatch Component", "description": "Symfony Stopwatch Component",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2016-03-04 07:54:35" "time": "2016-06-06 11:11:27"
}, },
{ {
"name": "symfony/yaml", "name": "symfony/yaml",
@@ -1927,6 +2025,55 @@
"description": "Symfony Yaml Component", "description": "Symfony Yaml Component",
"homepage": "http://symfony.com", "homepage": "http://symfony.com",
"time": "2012-08-22 13:48:41" "time": "2012-08-22 13:48:41"
},
{
"name": "webmozart/assert",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/webmozart/assert.git",
"reference": "30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozart/assert/zipball/30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde",
"reference": "30eed06dd6bc88410a4ff7f77b6d22f3ce13dbde",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "^4.6"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-4": {
"Webmozart\\Assert\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
}
],
"description": "Assertions to validate method input/output with nice error messages.",
"keywords": [
"assert",
"check",
"validate"
],
"time": "2015-08-24 13:29:44"
} }
], ],
"aliases": [], "aliases": [],

View File

@@ -1,48 +1,26 @@
API Authentication API Authentication
================== ==================
API endpoint
------------
URL: `https://YOUR_SERVER/jsonrpc.php`
Default method (HTTP Basic) Default method (HTTP Basic)
--------------------------- ---------------------------
The API credentials are available on the settings page. ### Application credentials
- API end-point: `https://YOUR_SERVER/jsonrpc.php`
If you want to use the "application api":
- Username: `jsonrpc` - Username: `jsonrpc`
- Password: API token on the settings page - Password: API token on the settings page
Otherwise for the "user api", just use the real username/passsword. ### User credentials
- Use the real username and password
The API use the [HTTP Basic Authentication Scheme described in the RFC2617](http://www.ietf.org/rfc/rfc2617.txt). The API use the [HTTP Basic Authentication Scheme described in the RFC2617](http://www.ietf.org/rfc/rfc2617.txt).
If there is an authentication error, you will receive the HTTP status code `401 Not Authorized`.
### Authorized User API procedures
- getMe
- getMyDashboard
- getMyActivityStream
- createMyPrivateProject
- getMyProjectsList
- getMyProjects
- getTimezone
- getVersion
- getDefaultTaskColor
- getDefaultTaskColors
- getColorList
- getProjectById
- getTask
- getTaskByReference
- getAllTasks
- openTask
- closeTask
- moveTaskPosition
- createTask
- updateTask
- getBoard
- getProjectActivity
- getMyOverdueTasks
Custom HTTP header Custom HTTP header
------------------ ------------------
@@ -64,3 +42,14 @@ curl \
-d '{"jsonrpc": "2.0", "method": "getAllProjects", "id": 1}' \ -d '{"jsonrpc": "2.0", "method": "getAllProjects", "id": 1}' \
http://localhost/kanboard/jsonrpc.php http://localhost/kanboard/jsonrpc.php
``` ```
Authentication error
--------------------
If the credentials are wrong, you will receive a `401 Not Authorized` and the corresponding JSON response.
Authorization error
-------------------
If the connected user is not allowed to access to the resource, you will receive a `403 Forbidden`.

View File

@@ -8,25 +8,25 @@ There are two types of API access:
### Application API ### Application API
- Access to the API with the user "jsonrpc" and the token available in settings - Access to the API with the user "jsonrpc" and the token available on the settings page
- Access to all procedures - Access to all procedures
- No permission checked - No permission checked
- There is no user session on the server - There is no user session on the server
- No access to procedures that starts with "My..." (example: "getMe" or "getMyProjects")
- Example of possible clients: tools to migrate/import data, create tasks from another system, etc... - Example of possible clients: tools to migrate/import data, create tasks from another system, etc...
### User API ### User API
- Access to the API with the user credentials (username and password) - Access to the API with the user credentials (username and password)
- Access to a restricted set of procedures - Application role and project permissions are checked for each procedure
- The project permissions are checked
- A user session is created on the server - A user session is created on the server
- Example of possible clients: mobile/desktop application, command line utility, etc... - Example of possible clients: native mobile/desktop application, command line utility, etc...
Security Security
-------- --------
- Always use HTTPS with a valid certificate - Always use HTTPS with a valid certificate (avoid clear text communication)
- If you make a mobile application, it's your job to store securely the user credentials on the device - If you make a mobile application, it's your responsability to store securely the user credentials on the device
- After 3 authentication failure on the user api, the end-user have to unlock his account by using the login form - After 3 authentication failure on the user api, the end-user have to unlock his account by using the login form
- Two factor authentication is not yet available through the API - Two factor authentication is not yet available through the API

View File

@@ -272,3 +272,36 @@ Response example:
"result": true "result": true
} }
``` ```
## getProjectUserRole
- Purpose: **Get the role of a user for a given project**
- Parameters:
- **project_id** (integer, required)
- **user_id** (integer, required)
- Result on success: **role name**
- Result on failure: **false**
Request example:
```json
{
"jsonrpc": "2.0",
"method": "getProjectUserRole",
"id": 2114673298,
"params": [
"2",
"3"
]
}
```
Response example:
```json
{
"jsonrpc": "2.0",
"id": 2114673298,
"result": "project-viewer"
}
```

View File

@@ -7,6 +7,8 @@ API Project Procedures
- Parameters: - Parameters:
- **name** (string, required) - **name** (string, required)
- **description** (string, optional) - **description** (string, optional)
- **owner_id** (integer, optional)
- **identifier** (string, optional)
- Result on success: **project_id** - Result on success: **project_id**
- Result on failure: **false** - Result on failure: **false**
@@ -186,6 +188,8 @@ Response example:
- **project_id** (integer, required) - **project_id** (integer, required)
- **name** (string, required) - **name** (string, required)
- **description** (string, optional) - **description** (string, optional)
- **owner_id** (integer, optional)
- **identifier** (string, optional)
- Result on success: **true** - Result on success: **true**
- Result on failure: **false** - Result on failure: **false**

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class ActionTest extends BaseIntegrationTest class ActionProcedureTest extends BaseProcedureTest
{ {
protected $projectName = 'My project to test actions'; protected $projectName = 'My project to test actions';

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class AppTest extends BaseIntegrationTest class AppProcedureTest extends BaseProcedureTest
{ {
public function testGetTimezone() public function testGetTimezone()
{ {

View File

@@ -2,7 +2,7 @@
require_once __DIR__.'/../../vendor/autoload.php'; require_once __DIR__.'/../../vendor/autoload.php';
abstract class BaseIntegrationTest extends PHPUnit_Framework_TestCase abstract class BaseProcedureTest extends PHPUnit_Framework_TestCase
{ {
protected $app = null; protected $app = null;
protected $admin = null; protected $admin = null;

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class BoardTest extends BaseIntegrationTest class BoardProcedureTest extends BaseProcedureTest
{ {
protected $projectName = 'My project to test board'; protected $projectName = 'My project to test board';

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class CategoryTest extends BaseIntegrationTest class CategoryProcedureTest extends BaseProcedureTest
{ {
protected $projectName = 'My project to test categories'; protected $projectName = 'My project to test categories';
private $categoryId = 0; private $categoryId = 0;

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class ColumnTest extends BaseIntegrationTest class ColumnProcedureTest extends BaseProcedureTest
{ {
protected $projectName = 'My project to test columns'; protected $projectName = 'My project to test columns';
private $columns = array(); private $columns = array();

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class CommentTest extends BaseIntegrationTest class CommentProcedureTest extends BaseProcedureTest
{ {
protected $projectName = 'My project to test comments'; protected $projectName = 'My project to test comments';
private $commentId = 0; private $commentId = 0;

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class GroupMemberTest extends BaseIntegrationTest class GroupMemberProcedureTest extends BaseProcedureTest
{ {
protected $username = 'user-group-member'; protected $username = 'user-group-member';
protected $groupName1 = 'My group member A'; protected $groupName1 = 'My group member A';

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class GroupTest extends BaseIntegrationTest class GroupProcedureTest extends BaseProcedureTest
{ {
public function testAll() public function testAll()
{ {

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class LinkTest extends BaseIntegrationTest class LinkProcedureTest extends BaseProcedureTest
{ {
public function testGetAllLinks() public function testGetAllLinks()
{ {

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class MeTest extends BaseIntegrationTest class MeProcedureTest extends BaseProcedureTest
{ {
protected $projectName = 'My private project'; protected $projectName = 'My private project';
@@ -41,11 +41,6 @@ class MeTest extends BaseIntegrationTest
{ {
$projects = $this->user->getMyProjects(); $projects = $this->user->getMyProjects();
$this->assertNotEmpty($projects); $this->assertNotEmpty($projects);
$this->assertCount(1, $projects);
$this->assertEquals($this->projectName, $projects[0]['name']);
$this->assertNotEmpty($projects[0]['url']['calendar']);
$this->assertNotEmpty($projects[0]['url']['board']);
$this->assertNotEmpty($projects[0]['url']['list']);
} }
public function assertCreateTask() public function assertCreateTask()

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class OverdueTaskTest extends BaseIntegrationTest class OverdueTaskProcedureTest extends BaseProcedureTest
{ {
protected $projectName = 'My project to test overdue tasks'; protected $projectName = 'My project to test overdue tasks';

View File

@@ -0,0 +1,306 @@
<?php
require_once __DIR__.'/BaseProcedureTest.php';
class ProcedureAuthorizationTest extends BaseProcedureTest
{
public function testApiCredentialDoNotHaveAccessToUserCredentialProcedure()
{
$this->setExpectedException('JsonRPC\Exception\AccessDeniedException');
$this->app->getMe();
}
public function testUserCredentialDoNotHaveAccessToAdminProcedures()
{
$this->setExpectedException('JsonRPC\Exception\AccessDeniedException');
$this->user->getUser(1);
}
public function testManagerCredentialDoNotHaveAccessToAdminProcedures()
{
$this->setExpectedException('JsonRPC\Exception\AccessDeniedException');
$this->user->getAllProjects();
}
public function testUserCredentialDoNotHaveAccessToManagerProcedures()
{
$this->setExpectedException('JsonRPC\Exception\AccessDeniedException');
$this->user->createProject('Team project creation are only for app managers');
}
public function testAppManagerCanCreateTeamProject()
{
$this->assertNotFalse($this->manager->createProject('Team project created by app manager'));
}
public function testAdminManagerCanCreateTeamProject()
{
$projectId = $this->admin->createProject('Team project created by admin');
$this->assertNotFalse($projectId);
$this->setExpectedException('JsonRPC\Exception\AccessDeniedException');
$this->assertNotNull($this->manager->getProjectById($projectId));
}
public function testProjectManagerCanUpdateHisProject()
{
$projectId = $this->manager->createProject(array(
'name' => 'Team project can be updated',
'owner_id' => $this->managerUserId,
));
$this->assertNotFalse($projectId);
$this->assertEquals('project-manager', $this->app->getProjectUserRole($projectId, $this->managerUserId));
$this->assertNotNull($this->manager->getProjectById($projectId));
$this->assertTrue($this->manager->updateProject($projectId, 'My team project have been updated'));
}
public function testProjectAuthorizationForbidden()
{
$projectId = $this->manager->createProject('A team project without members');
$this->assertNotFalse($projectId);
$this->setExpectedException('JsonRPC\Exception\AccessDeniedException');
$this->user->getProjectById($projectId);
}
public function testProjectAuthorizationGranted()
{
$projectId = $this->manager->createProject(array(
'name' => 'A team project with members',
'owner_id' => $this->managerUserId,
));
$this->assertNotFalse($projectId);
$this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId));
$this->assertNotNull($this->user->getProjectById($projectId));
}
public function testActionAuthorizationForbidden()
{
$projectId = $this->manager->createProject(array(
'name' => 'Test Project',
'owner_id' => $this->managerUserId,
));
$this->assertNotFalse($projectId);
$actionId = $this->manager->createAction($projectId, 'task.move.column', '\Kanboard\Action\TaskCloseColumn', array('column_id' => 1));
$this->assertNotFalse($actionId);
$this->setExpectedException('JsonRPC\Exception\AccessDeniedException');
$this->user->removeAction($projectId);
}
public function testActionAuthorizationForbiddenBecauseNotProjectManager()
{
$projectId = $this->manager->createProject(array(
'name' => 'Test Project',
'owner_id' => $this->managerUserId,
));
$this->assertNotFalse($projectId);
$actionId = $this->manager->createAction($projectId, 'task.move.column', '\Kanboard\Action\TaskCloseColumn', array('column_id' => 1));
$this->assertNotFalse($actionId);
$this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-member'));
$this->setExpectedException('JsonRPC\Exception\AccessDeniedException');
$this->user->removeAction($actionId);
}
public function testActionAuthorizationGranted()
{
$projectId = $this->manager->createProject(array(
'name' => 'Test Project',
'owner_id' => $this->managerUserId,
));
$this->assertNotFalse($projectId);
$actionId = $this->manager->createAction($projectId, 'task.move.column', '\Kanboard\Action\TaskCloseColumn', array('column_id' => 1));
$this->assertNotFalse($actionId);
$this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-manager'));
$this->assertTrue($this->user->removeAction($actionId));
}
public function testCategoryAuthorizationForbidden()
{
$projectId = $this->manager->createProject(array(
'name' => 'Test Project',
'owner_id' => $this->managerUserId,
));
$this->assertNotFalse($projectId);
$categoryId = $this->manager->createCategory($projectId, 'Test');
$this->assertNotFalse($categoryId);
$this->setExpectedException('JsonRPC\Exception\AccessDeniedException');
$this->user->removeCategory($categoryId);
}
public function testCategoryAuthorizationForbiddenBecauseNotProjectManager()
{
$projectId = $this->manager->createProject(array(
'name' => 'Test Project',
'owner_id' => $this->managerUserId,
));
$this->assertNotFalse($projectId);
$categoryId = $this->manager->createCategory($projectId, 'Test');
$this->assertNotFalse($categoryId);
$this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-member'));
$this->setExpectedException('JsonRPC\Exception\AccessDeniedException');
$this->user->removeCategory($categoryId);
}
public function testCategoryAuthorizationGranted()
{
$projectId = $this->manager->createProject(array(
'name' => 'Test Project',
'owner_id' => $this->managerUserId,
));
$this->assertNotFalse($projectId);
$categoryId = $this->manager->createCategory($projectId, 'Test');
$this->assertNotFalse($categoryId);
$this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-manager'));
$this->assertTrue($this->user->removeCategory($categoryId));
}
public function testColumnAuthorizationForbidden()
{
$projectId = $this->manager->createProject(array(
'name' => 'Test Project',
'owner_id' => $this->managerUserId,
));
$this->assertNotFalse($projectId);
$columnId = $this->manager->addColumn($projectId, 'Test');
$this->assertNotFalse($columnId);
$this->setExpectedException('JsonRPC\Exception\AccessDeniedException');
$this->user->removeColumn($columnId);
}
public function testColumnAuthorizationForbiddenBecauseNotProjectManager()
{
$projectId = $this->manager->createProject(array(
'name' => 'Test Project',
'owner_id' => $this->managerUserId,
));
$this->assertNotFalse($projectId);
$columnId = $this->manager->addColumn($projectId, 'Test');
$this->assertNotFalse($columnId);
$this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-member'));
$this->setExpectedException('JsonRPC\Exception\AccessDeniedException');
$this->user->removeColumn($columnId);
}
public function testColumnAuthorizationGranted()
{
$projectId = $this->manager->createProject(array(
'name' => 'Test Project',
'owner_id' => $this->managerUserId,
));
$this->assertNotFalse($projectId);
$columnId = $this->manager->addColumn($projectId, 'Test');
$this->assertNotFalse($columnId);
$this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-manager'));
$this->assertTrue($this->user->removeColumn($columnId));
}
public function testCommentAuthorizationForbidden()
{
$projectId = $this->manager->createProject(array(
'name' => 'Test Project',
'owner_id' => $this->managerUserId,
));
$this->assertNotFalse($projectId);
$this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-viewer'));
$taskId = $this->manager->createTask('My Task', $projectId);
$this->assertNotFalse($taskId);
$commentId = $this->manager->createComment($taskId, $this->userUserId, 'My comment');
$this->assertNotFalse($commentId);
$this->setExpectedException('JsonRPC\Exception\AccessDeniedException');
$this->user->updateComment($commentId, 'something else');
}
public function testCommentAuthorizationGranted()
{
$projectId = $this->manager->createProject(array(
'name' => 'Test Project',
'owner_id' => $this->managerUserId,
));
$this->assertNotFalse($projectId);
$this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-member'));
$taskId = $this->user->createTask('My Task', $projectId);
$this->assertNotFalse($taskId);
$commentId = $this->user->createComment($taskId, $this->userUserId, 'My comment');
$this->assertNotFalse($commentId);
$this->assertTrue($this->user->updateComment($commentId, 'something else'));
}
public function testSubtaskAuthorizationForbidden()
{
$projectId = $this->manager->createProject(array(
'name' => 'Test Project',
'owner_id' => $this->managerUserId,
));
$this->assertNotFalse($projectId);
$this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-viewer'));
$taskId = $this->manager->createTask('My Task', $projectId);
$this->assertNotFalse($taskId);
$subtaskId = $this->manager->createSubtask($taskId, 'My subtask');
$this->assertNotFalse($subtaskId);
$this->setExpectedException('JsonRPC\Exception\AccessDeniedException');
$this->user->removeSubtask($subtaskId);
}
public function testSubtaskAuthorizationGranted()
{
$projectId = $this->manager->createProject(array(
'name' => 'Test Project',
'owner_id' => $this->managerUserId,
));
$this->assertNotFalse($projectId);
$this->assertTrue($this->manager->addProjectUser($projectId, $this->userUserId, 'project-member'));
$taskId = $this->user->createTask('My Task', $projectId);
$this->assertNotFalse($taskId);
$subtaskId = $this->manager->createSubtask($taskId, 'My subtask');
$this->assertNotFalse($subtaskId);
$this->assertTrue($this->user->removeSubtask($subtaskId));
}
}

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class ProjectPermissionTest extends BaseIntegrationTest class ProjectPermissionProcedureTest extends BaseProcedureTest
{ {
protected $projectName = 'Project with permission'; protected $projectName = 'Project with permission';
protected $username = 'user-project-permission'; protected $username = 'user-project-permission';

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class ProjectTest extends BaseIntegrationTest class ProjectProcedureTest extends BaseProcedureTest
{ {
protected $projectName = 'My team project'; protected $projectName = 'My team project';

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class SubtaskTest extends BaseIntegrationTest class SubtaskProcedureTest extends BaseProcedureTest
{ {
protected $projectName = 'My project to test subtasks'; protected $projectName = 'My project to test subtasks';
private $subtaskId = 0; private $subtaskId = 0;

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class SwimlaneTest extends BaseIntegrationTest class SwimlaneProcedureTest extends BaseProcedureTest
{ {
protected $projectName = 'My project to test swimlanes'; protected $projectName = 'My project to test swimlanes';
private $swimlaneId = 0; private $swimlaneId = 0;

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class TaskFileTest extends BaseIntegrationTest class TaskFileProcedureTest extends BaseProcedureTest
{ {
protected $projectName = 'My project to test task files'; protected $projectName = 'My project to test task files';
protected $fileId; protected $fileId;

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class TaskLinkTest extends BaseIntegrationTest class TaskLinkProcedureTest extends BaseProcedureTest
{ {
protected $projectName = 'My project to test task links'; protected $projectName = 'My project to test task links';
protected $taskLinkId; protected $taskLinkId;

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class TaskTest extends BaseIntegrationTest class TaskProcedureTest extends BaseProcedureTest
{ {
protected $projectName = 'My project to test tasks'; protected $projectName = 'My project to test tasks';

View File

@@ -1,8 +1,8 @@
<?php <?php
require_once __DIR__.'/BaseIntegrationTest.php'; require_once __DIR__.'/BaseProcedureTest.php';
class UserTest extends BaseIntegrationTest class UserProcedureTest extends BaseProcedureTest
{ {
public function testAll() public function testAll()
{ {

View File

@@ -11,7 +11,7 @@ use Kanboard\Model\CategoryModel;
use Kanboard\Model\ProjectUserRoleModel; use Kanboard\Model\ProjectUserRoleModel;
use Kanboard\Core\Security\Role; use Kanboard\Core\Security\Role;
class ActionTest extends Base class ActionModelTest extends Base
{ {
public function testCreate() public function testCreate()
{ {
@@ -69,6 +69,24 @@ class ActionTest extends Base
$this->assertEquals(array('column_id' => 1, 'color_id' => 'red'), $action['params']); $this->assertEquals(array('column_id' => 1, 'color_id' => 'red'), $action['params']);
} }
public function testGetProjectId()
{
$projectModel = new ProjectModel($this->container);
$actionModel = new ActionModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'test')));
$this->assertEquals(1, $actionModel->create(array(
'project_id' => 1,
'event_name' => TaskModel::EVENT_CREATE,
'action_name' => '\Kanboard\Action\TaskAssignColorColumn',
'params' => array('column_id' => 1, 'color_id' => 'red'),
)));
$this->assertEquals(1, $actionModel->getProjectId(1));
$this->assertSame(0, $actionModel->getProjectId(42));
}
public function testGetAll() public function testGetAll()
{ {
$projectModel = new ProjectModel($this->container); $projectModel = new ProjectModel($this->container);

View File

@@ -8,7 +8,7 @@ use Kanboard\Model\TaskFinderModel;
use Kanboard\Model\ProjectModel; use Kanboard\Model\ProjectModel;
use Kanboard\Model\CategoryModel; use Kanboard\Model\CategoryModel;
class CategoryTest extends Base class CategoryModelTest extends Base
{ {
public function testCreation() public function testCreation()
{ {
@@ -81,6 +81,18 @@ class CategoryTest extends Base
$this->assertSame(0, $categoryModel->getIdByName(1, 'Category #2')); $this->assertSame(0, $categoryModel->getIdByName(1, 'Category #2'));
} }
public function testGetProjectId()
{
$projectModel = new ProjectModel($this->container);
$categoryModel = new CategoryModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'Project #1')));
$this->assertEquals(1, $categoryModel->create(array('name' => 'Category #1', 'project_id' => 1, 'description' => 'test')));
$this->assertEquals(1, $categoryModel->getProjectId(1));
$this->assertSame(0, $categoryModel->getProjectId(2));
}
public function testGetList() public function testGetList()
{ {
$projectModel = new ProjectModel($this->container); $projectModel = new ProjectModel($this->container);

View File

@@ -10,16 +10,16 @@ class CommentTest extends Base
{ {
public function testCreate() public function testCreate()
{ {
$c = new CommentModel($this->container); $commentModel = new CommentModel($this->container);
$tc = new TaskCreationModel($this->container); $taskCreationModel = new TaskCreationModel($this->container);
$p = new ProjectModel($this->container); $projectModel = new ProjectModel($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test1'))); $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
$this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1)));
$this->assertEquals(1, $c->create(array('task_id' => 1, 'comment' => 'bla bla', 'user_id' => 1))); $this->assertEquals(1, $commentModel->create(array('task_id' => 1, 'comment' => 'bla bla', 'user_id' => 1)));
$this->assertEquals(2, $c->create(array('task_id' => 1, 'comment' => 'bla bla'))); $this->assertEquals(2, $commentModel->create(array('task_id' => 1, 'comment' => 'bla bla')));
$comment = $c->getById(1); $comment = $commentModel->getById(1);
$this->assertNotEmpty($comment); $this->assertNotEmpty($comment);
$this->assertEquals('bla bla', $comment['comment']); $this->assertEquals('bla bla', $comment['comment']);
$this->assertEquals(1, $comment['task_id']); $this->assertEquals(1, $comment['task_id']);
@@ -27,7 +27,7 @@ class CommentTest extends Base
$this->assertEquals('admin', $comment['username']); $this->assertEquals('admin', $comment['username']);
$this->assertEquals(time(), $comment['date_creation'], '', 3); $this->assertEquals(time(), $comment['date_creation'], '', 3);
$comment = $c->getById(2); $comment = $commentModel->getById(2);
$this->assertNotEmpty($comment); $this->assertNotEmpty($comment);
$this->assertEquals('bla bla', $comment['comment']); $this->assertEquals('bla bla', $comment['comment']);
$this->assertEquals(1, $comment['task_id']); $this->assertEquals(1, $comment['task_id']);
@@ -38,17 +38,17 @@ class CommentTest extends Base
public function testGetAll() public function testGetAll()
{ {
$c = new CommentModel($this->container); $commentModel = new CommentModel($this->container);
$tc = new TaskCreationModel($this->container); $taskCreationModel = new TaskCreationModel($this->container);
$p = new ProjectModel($this->container); $projectModel = new ProjectModel($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test1'))); $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
$this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1)));
$this->assertNotFalse($c->create(array('task_id' => 1, 'comment' => 'c1', 'user_id' => 1))); $this->assertEquals(1, $commentModel->create(array('task_id' => 1, 'comment' => 'c1', 'user_id' => 1)));
$this->assertNotFalse($c->create(array('task_id' => 1, 'comment' => 'c2', 'user_id' => 1))); $this->assertEquals(2, $commentModel->create(array('task_id' => 1, 'comment' => 'c2', 'user_id' => 1)));
$this->assertNotFalse($c->create(array('task_id' => 1, 'comment' => 'c3', 'user_id' => 1))); $this->assertEquals(3, $commentModel->create(array('task_id' => 1, 'comment' => 'c3', 'user_id' => 1)));
$comments = $c->getAll(1); $comments = $commentModel->getAll(1);
$this->assertNotEmpty($comments); $this->assertNotEmpty($comments);
$this->assertEquals(3, count($comments)); $this->assertEquals(3, count($comments));
@@ -56,37 +56,51 @@ class CommentTest extends Base
$this->assertEquals(2, $comments[1]['id']); $this->assertEquals(2, $comments[1]['id']);
$this->assertEquals(3, $comments[2]['id']); $this->assertEquals(3, $comments[2]['id']);
$this->assertEquals(3, $c->count(1)); $this->assertEquals(3, $commentModel->count(1));
} }
public function testUpdate() public function testUpdate()
{ {
$c = new CommentModel($this->container); $commentModel = new CommentModel($this->container);
$tc = new TaskCreationModel($this->container); $taskCreationModel = new TaskCreationModel($this->container);
$p = new ProjectModel($this->container); $projectModel = new ProjectModel($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test1'))); $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
$this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1)));
$this->assertNotFalse($c->create(array('task_id' => 1, 'comment' => 'c1', 'user_id' => 1))); $this->assertEquals(1, $commentModel->create(array('task_id' => 1, 'comment' => 'c1', 'user_id' => 1)));
$this->assertTrue($c->update(array('id' => 1, 'comment' => 'bla'))); $this->assertTrue($commentModel->update(array('id' => 1, 'comment' => 'bla')));
$comment = $c->getById(1); $comment = $commentModel->getById(1);
$this->assertNotEmpty($comment); $this->assertNotEmpty($comment);
$this->assertEquals('bla', $comment['comment']); $this->assertEquals('bla', $comment['comment']);
} }
public function validateRemove() public function validateRemove()
{ {
$c = new CommentModel($this->container); $commentModel = new CommentModel($this->container);
$tc = new TaskCreationModel($this->container); $taskCreationModel = new TaskCreationModel($this->container);
$p = new ProjectModel($this->container); $projectModel = new ProjectModel($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test1'))); $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
$this->assertEquals(1, $tc->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1)));
$this->assertTrue($c->create(array('task_id' => 1, 'comment' => 'c1', 'user_id' => 1))); $this->assertEquals(1, $commentModel->create(array('task_id' => 1, 'comment' => 'c1', 'user_id' => 1)));
$this->assertTrue($c->remove(1)); $this->assertTrue($commentModel->remove(1));
$this->assertFalse($c->remove(1)); $this->assertFalse($commentModel->remove(1));
$this->assertFalse($c->remove(1111)); $this->assertFalse($commentModel->remove(1111));
}
public function testGetProjectId()
{
$commentModel = new CommentModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$projectModel = new ProjectModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1)));
$this->assertEquals(1, $commentModel->create(array('task_id' => 1, 'comment' => 'c1', 'user_id' => 1)));
$this->assertEquals(1, $commentModel->getProjectId(1));
$this->assertSame(0, $commentModel->getProjectId(2));
} }
} }

View File

@@ -5,10 +5,9 @@ require_once __DIR__.'/../Base.php';
use Kanboard\Model\TaskCreationModel; use Kanboard\Model\TaskCreationModel;
use Kanboard\Model\SubtaskModel; use Kanboard\Model\SubtaskModel;
use Kanboard\Model\ProjectModel; use Kanboard\Model\ProjectModel;
use Kanboard\Core\User\UserSession;
use Kanboard\Model\TaskFinderModel; use Kanboard\Model\TaskFinderModel;
class SubtaskTest extends Base class SubtaskModelTest extends Base
{ {
public function onSubtaskCreated($event) public function onSubtaskCreated($event)
{ {
@@ -70,18 +69,18 @@ class SubtaskTest extends Base
public function testCreation() public function testCreation()
{ {
$tc = new TaskCreationModel($this->container); $taskCreationModel = new TaskCreationModel($this->container);
$s = new SubtaskModel($this->container); $subtaskModel = new SubtaskModel($this->container);
$p = new ProjectModel($this->container); $projectModel = new ProjectModel($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test'))); $this->assertEquals(1, $projectModel->create(array('name' => 'test')));
$this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1))); $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1)));
$this->container['dispatcher']->addListener(SubtaskModel::EVENT_CREATE, array($this, 'onSubtaskCreated')); $this->container['dispatcher']->addListener(SubtaskModel::EVENT_CREATE, array($this, 'onSubtaskCreated'));
$this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1))); $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1)));
$subtask = $s->getById(1); $subtask = $subtaskModel->getById(1);
$this->assertNotEmpty($subtask); $this->assertNotEmpty($subtask);
$this->assertEquals(1, $subtask['id']); $this->assertEquals(1, $subtask['id']);
$this->assertEquals(1, $subtask['task_id']); $this->assertEquals(1, $subtask['task_id']);
@@ -95,19 +94,19 @@ class SubtaskTest extends Base
public function testModification() public function testModification()
{ {
$tc = new TaskCreationModel($this->container); $taskCreationModel = new TaskCreationModel($this->container);
$s = new SubtaskModel($this->container); $subtaskModel = new SubtaskModel($this->container);
$p = new ProjectModel($this->container); $projectModel = new ProjectModel($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test'))); $this->assertEquals(1, $projectModel->create(array('name' => 'test')));
$this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1))); $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1)));
$this->container['dispatcher']->addListener(SubtaskModel::EVENT_UPDATE, array($this, 'onSubtaskUpdated')); $this->container['dispatcher']->addListener(SubtaskModel::EVENT_UPDATE, array($this, 'onSubtaskUpdated'));
$this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1))); $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1)));
$this->assertTrue($s->update(array('id' => 1, 'user_id' => 1, 'status' => SubtaskModel::STATUS_INPROGRESS))); $this->assertTrue($subtaskModel->update(array('id' => 1, 'user_id' => 1, 'status' => SubtaskModel::STATUS_INPROGRESS)));
$subtask = $s->getById(1); $subtask = $subtaskModel->getById(1);
$this->assertNotEmpty($subtask); $this->assertNotEmpty($subtask);
$this->assertEquals(1, $subtask['id']); $this->assertEquals(1, $subtask['id']);
$this->assertEquals(1, $subtask['task_id']); $this->assertEquals(1, $subtask['task_id']);
@@ -121,61 +120,61 @@ class SubtaskTest extends Base
public function testRemove() public function testRemove()
{ {
$tc = new TaskCreationModel($this->container); $taskCreationModel = new TaskCreationModel($this->container);
$s = new SubtaskModel($this->container); $subtaskModel = new SubtaskModel($this->container);
$p = new ProjectModel($this->container); $projectModel = new ProjectModel($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test'))); $this->assertEquals(1, $projectModel->create(array('name' => 'test')));
$this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1))); $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1)));
$this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1))); $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1)));
$this->container['dispatcher']->addListener(SubtaskModel::EVENT_DELETE, array($this, 'onSubtaskDeleted')); $this->container['dispatcher']->addListener(SubtaskModel::EVENT_DELETE, array($this, 'onSubtaskDeleted'));
$subtask = $s->getById(1); $subtask = $subtaskModel->getById(1);
$this->assertNotEmpty($subtask); $this->assertNotEmpty($subtask);
$this->assertTrue($s->remove(1)); $this->assertTrue($subtaskModel->remove(1));
$subtask = $s->getById(1); $subtask = $subtaskModel->getById(1);
$this->assertEmpty($subtask); $this->assertEmpty($subtask);
} }
public function testToggleStatusWithoutSession() public function testToggleStatusWithoutSession()
{ {
$tc = new TaskCreationModel($this->container); $taskCreationModel = new TaskCreationModel($this->container);
$s = new SubtaskModel($this->container); $subtaskModel = new SubtaskModel($this->container);
$p = new ProjectModel($this->container); $projectModel = new ProjectModel($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test1'))); $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
$this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1))); $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1)));
$this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1))); $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1)));
$subtask = $s->getById(1); $subtask = $subtaskModel->getById(1);
$this->assertNotEmpty($subtask); $this->assertNotEmpty($subtask);
$this->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']); $this->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']);
$this->assertEquals(0, $subtask['user_id']); $this->assertEquals(0, $subtask['user_id']);
$this->assertEquals(1, $subtask['task_id']); $this->assertEquals(1, $subtask['task_id']);
$this->assertEquals(SubtaskModel::STATUS_INPROGRESS, $s->toggleStatus(1)); $this->assertEquals(SubtaskModel::STATUS_INPROGRESS, $subtaskModel->toggleStatus(1));
$subtask = $s->getById(1); $subtask = $subtaskModel->getById(1);
$this->assertNotEmpty($subtask); $this->assertNotEmpty($subtask);
$this->assertEquals(SubtaskModel::STATUS_INPROGRESS, $subtask['status']); $this->assertEquals(SubtaskModel::STATUS_INPROGRESS, $subtask['status']);
$this->assertEquals(0, $subtask['user_id']); $this->assertEquals(0, $subtask['user_id']);
$this->assertEquals(1, $subtask['task_id']); $this->assertEquals(1, $subtask['task_id']);
$this->assertEquals(SubtaskModel::STATUS_DONE, $s->toggleStatus(1)); $this->assertEquals(SubtaskModel::STATUS_DONE, $subtaskModel->toggleStatus(1));
$subtask = $s->getById(1); $subtask = $subtaskModel->getById(1);
$this->assertNotEmpty($subtask); $this->assertNotEmpty($subtask);
$this->assertEquals(SubtaskModel::STATUS_DONE, $subtask['status']); $this->assertEquals(SubtaskModel::STATUS_DONE, $subtask['status']);
$this->assertEquals(0, $subtask['user_id']); $this->assertEquals(0, $subtask['user_id']);
$this->assertEquals(1, $subtask['task_id']); $this->assertEquals(1, $subtask['task_id']);
$this->assertEquals(SubtaskModel::STATUS_TODO, $s->toggleStatus(1)); $this->assertEquals(SubtaskModel::STATUS_TODO, $subtaskModel->toggleStatus(1));
$subtask = $s->getById(1); $subtask = $subtaskModel->getById(1);
$this->assertNotEmpty($subtask); $this->assertNotEmpty($subtask);
$this->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']); $this->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']);
$this->assertEquals(0, $subtask['user_id']); $this->assertEquals(0, $subtask['user_id']);
@@ -184,17 +183,16 @@ class SubtaskTest extends Base
public function testToggleStatusWithSession() public function testToggleStatusWithSession()
{ {
$tc = new TaskCreationModel($this->container); $taskCreationModel = new TaskCreationModel($this->container);
$s = new SubtaskModel($this->container); $subtaskModel = new SubtaskModel($this->container);
$p = new ProjectModel($this->container); $projectModel = new ProjectModel($this->container);
$us = new UserSession($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test1'))); $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
$this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1))); $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1)));
$this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1))); $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1)));
$subtask = $s->getById(1); $subtask = $subtaskModel->getById(1);
$this->assertNotEmpty($subtask); $this->assertNotEmpty($subtask);
$this->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']); $this->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']);
$this->assertEquals(0, $subtask['user_id']); $this->assertEquals(0, $subtask['user_id']);
@@ -203,25 +201,25 @@ class SubtaskTest extends Base
// Set the current logged user // Set the current logged user
$this->container['sessionStorage']->user = array('id' => 1); $this->container['sessionStorage']->user = array('id' => 1);
$this->assertEquals(SubtaskModel::STATUS_INPROGRESS, $s->toggleStatus(1)); $this->assertEquals(SubtaskModel::STATUS_INPROGRESS, $subtaskModel->toggleStatus(1));
$subtask = $s->getById(1); $subtask = $subtaskModel->getById(1);
$this->assertNotEmpty($subtask); $this->assertNotEmpty($subtask);
$this->assertEquals(SubtaskModel::STATUS_INPROGRESS, $subtask['status']); $this->assertEquals(SubtaskModel::STATUS_INPROGRESS, $subtask['status']);
$this->assertEquals(1, $subtask['user_id']); $this->assertEquals(1, $subtask['user_id']);
$this->assertEquals(1, $subtask['task_id']); $this->assertEquals(1, $subtask['task_id']);
$this->assertEquals(SubtaskModel::STATUS_DONE, $s->toggleStatus(1)); $this->assertEquals(SubtaskModel::STATUS_DONE, $subtaskModel->toggleStatus(1));
$subtask = $s->getById(1); $subtask = $subtaskModel->getById(1);
$this->assertNotEmpty($subtask); $this->assertNotEmpty($subtask);
$this->assertEquals(SubtaskModel::STATUS_DONE, $subtask['status']); $this->assertEquals(SubtaskModel::STATUS_DONE, $subtask['status']);
$this->assertEquals(1, $subtask['user_id']); $this->assertEquals(1, $subtask['user_id']);
$this->assertEquals(1, $subtask['task_id']); $this->assertEquals(1, $subtask['task_id']);
$this->assertEquals(SubtaskModel::STATUS_TODO, $s->toggleStatus(1)); $this->assertEquals(SubtaskModel::STATUS_TODO, $subtaskModel->toggleStatus(1));
$subtask = $s->getById(1); $subtask = $subtaskModel->getById(1);
$this->assertNotEmpty($subtask); $this->assertNotEmpty($subtask);
$this->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']); $this->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']);
$this->assertEquals(1, $subtask['user_id']); $this->assertEquals(1, $subtask['user_id']);
@@ -230,19 +228,19 @@ class SubtaskTest extends Base
public function testCloseAll() public function testCloseAll()
{ {
$tc = new TaskCreationModel($this->container); $taskCreationModel = new TaskCreationModel($this->container);
$s = new SubtaskModel($this->container); $subtaskModel = new SubtaskModel($this->container);
$p = new ProjectModel($this->container); $projectModel = new ProjectModel($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test1'))); $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
$this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1))); $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1)));
$this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1))); $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1)));
$this->assertEquals(2, $s->create(array('title' => 'subtask #2', 'task_id' => 1))); $this->assertEquals(2, $subtaskModel->create(array('title' => 'subtask #2', 'task_id' => 1)));
$this->assertTrue($s->closeAll(1)); $this->assertTrue($subtaskModel->closeAll(1));
$subtasks = $s->getAll(1); $subtasks = $subtaskModel->getAll(1);
$this->assertNotEmpty($subtasks); $this->assertNotEmpty($subtasks);
foreach ($subtasks as $subtask) { foreach ($subtasks as $subtask) {
@@ -252,24 +250,24 @@ class SubtaskTest extends Base
public function testDuplicate() public function testDuplicate()
{ {
$tc = new TaskCreationModel($this->container); $taskCreationModel = new TaskCreationModel($this->container);
$s = new SubtaskModel($this->container); $subtaskModel = new SubtaskModel($this->container);
$p = new ProjectModel($this->container); $projectModel = new ProjectModel($this->container);
// We create a project // We create a project
$this->assertEquals(1, $p->create(array('name' => 'test1'))); $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
// We create 2 tasks // We create 2 tasks
$this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 1))); $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 1)));
$this->assertEquals(2, $tc->create(array('title' => 'test 2', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 0))); $this->assertEquals(2, $taskCreationModel->create(array('title' => 'test 2', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 0)));
// We create many subtasks for the first task // We create many subtasks for the first task
$this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1, 'time_estimated' => 5, 'time_spent' => 3, 'status' => 1, 'another_subtask' => 'on'))); $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1, 'time_estimated' => 5, 'time_spent' => 3, 'status' => 1, 'another_subtask' => 'on')));
$this->assertEquals(2, $s->create(array('title' => 'subtask #2', 'task_id' => 1, 'time_estimated' => '', 'time_spent' => '', 'status' => 2, 'user_id' => 1))); $this->assertEquals(2, $subtaskModel->create(array('title' => 'subtask #2', 'task_id' => 1, 'time_estimated' => '', 'time_spent' => '', 'status' => 2, 'user_id' => 1)));
// We duplicate our subtasks // We duplicate our subtasks
$this->assertTrue($s->duplicate(1, 2)); $this->assertTrue($subtaskModel->duplicate(1, 2));
$subtasks = $s->getAll(2); $subtasks = $subtaskModel->getAll(2);
$this->assertNotFalse($subtasks); $this->assertNotFalse($subtasks);
$this->assertNotEmpty($subtasks); $this->assertNotEmpty($subtasks);
@@ -385,4 +383,18 @@ class SubtaskTest extends Base
$this->assertEquals(2, $task['time_spent']); $this->assertEquals(2, $task['time_spent']);
$this->assertEquals(3, $task['time_estimated']); $this->assertEquals(3, $task['time_estimated']);
} }
public function testGetProjectId()
{
$taskCreationModel = new TaskCreationModel($this->container);
$subtaskModel = new SubtaskModel($this->container);
$projectModel = new ProjectModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
$this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1)));
$this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1)));
$this->assertEquals(1, $subtaskModel->getProjectId(1));
$this->assertEquals(0, $subtaskModel->getProjectId(2));
}
} }

View File

@@ -6,7 +6,7 @@ use Kanboard\Model\TaskFileModel;
use Kanboard\Model\TaskCreationModel; use Kanboard\Model\TaskCreationModel;
use Kanboard\Model\ProjectModel; use Kanboard\Model\ProjectModel;
class TaskFileTest extends Base class TaskFileModelTest extends Base
{ {
public function testCreation() public function testCreation()
{ {
@@ -442,4 +442,17 @@ class TaskFileTest extends Base
$this->assertTrue($fileModel->removeAll(1)); $this->assertTrue($fileModel->removeAll(1));
} }
public function testGetProjectId()
{
$projectModel = new ProjectModel($this->container);
$fileModel = new TaskFileModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'test')));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test')));
$this->assertEquals(1, $fileModel->create(1, 'test', '/tmp/foobar', 10));
$this->assertEquals(1, $fileModel->getProjectId(1));
$this->assertEquals(0, $fileModel->getProjectId(2));
}
} }

View File

@@ -0,0 +1,211 @@
<?php
require_once __DIR__.'/../Base.php';
use Kanboard\Model\TaskFinderModel;
use Kanboard\Model\TaskLinkModel;
use Kanboard\Model\TaskCreationModel;
use Kanboard\Model\ProjectModel;
class TaskLinkModelTest extends Base
{
// Check postgres issue: "Cardinality violation: 7 ERROR: more than one row returned by a subquery used as an expression"
public function testGetTaskWithMultipleMilestoneLink()
{
$taskFinderModel = new TaskFinderModel($this->container);
$taskLinkModel = new TaskLinkModel($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'test')));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'A')));
$this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'B')));
$this->assertEquals(3, $taskCreationModel->create(array('project_id' => 1, 'title' => 'C')));
$this->assertNotFalse($taskLinkModel->create(1, 2, 9));
$this->assertNotFalse($taskLinkModel->create(1, 3, 9));
$task = $taskFinderModel->getExtendedQuery()->findOne();
$this->assertNotEmpty($task);
}
public function testCreateTaskLinkWithNoOpposite()
{
$taskLinkModel = new TaskLinkModel($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'test')));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'A')));
$this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'B')));
$this->assertEquals(1, $taskLinkModel->create(1, 2, 1));
$links = $taskLinkModel->getAll(1);
$this->assertNotEmpty($links);
$this->assertCount(1, $links);
$this->assertEquals('relates to', $links[0]['label']);
$this->assertEquals('B', $links[0]['title']);
$this->assertEquals(2, $links[0]['task_id']);
$this->assertEquals(1, $links[0]['is_active']);
$links = $taskLinkModel->getAll(2);
$this->assertNotEmpty($links);
$this->assertCount(1, $links);
$this->assertEquals('relates to', $links[0]['label']);
$this->assertEquals('A', $links[0]['title']);
$this->assertEquals(1, $links[0]['task_id']);
$this->assertEquals(1, $links[0]['is_active']);
$task_link = $taskLinkModel->getById(1);
$this->assertNotEmpty($task_link);
$this->assertEquals(1, $task_link['id']);
$this->assertEquals(1, $task_link['task_id']);
$this->assertEquals(2, $task_link['opposite_task_id']);
$this->assertEquals(1, $task_link['link_id']);
$opposite_task_link = $taskLinkModel->getOppositeTaskLink($task_link);
$this->assertNotEmpty($opposite_task_link);
$this->assertEquals(2, $opposite_task_link['id']);
$this->assertEquals(2, $opposite_task_link['task_id']);
$this->assertEquals(1, $opposite_task_link['opposite_task_id']);
$this->assertEquals(1, $opposite_task_link['link_id']);
}
public function testCreateTaskLinkWithOpposite()
{
$taskLinkModel = new TaskLinkModel($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'test')));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'A')));
$this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'B')));
$this->assertEquals(1, $taskLinkModel->create(1, 2, 2));
$links = $taskLinkModel->getAll(1);
$this->assertNotEmpty($links);
$this->assertCount(1, $links);
$this->assertEquals('blocks', $links[0]['label']);
$this->assertEquals('B', $links[0]['title']);
$this->assertEquals(2, $links[0]['task_id']);
$this->assertEquals(1, $links[0]['is_active']);
$links = $taskLinkModel->getAll(2);
$this->assertNotEmpty($links);
$this->assertCount(1, $links);
$this->assertEquals('is blocked by', $links[0]['label']);
$this->assertEquals('A', $links[0]['title']);
$this->assertEquals(1, $links[0]['task_id']);
$this->assertEquals(1, $links[0]['is_active']);
$task_link = $taskLinkModel->getById(1);
$this->assertNotEmpty($task_link);
$this->assertEquals(1, $task_link['id']);
$this->assertEquals(1, $task_link['task_id']);
$this->assertEquals(2, $task_link['opposite_task_id']);
$this->assertEquals(2, $task_link['link_id']);
$opposite_task_link = $taskLinkModel->getOppositeTaskLink($task_link);
$this->assertNotEmpty($opposite_task_link);
$this->assertEquals(2, $opposite_task_link['id']);
$this->assertEquals(2, $opposite_task_link['task_id']);
$this->assertEquals(1, $opposite_task_link['opposite_task_id']);
$this->assertEquals(3, $opposite_task_link['link_id']);
}
public function testGroupByLabel()
{
$taskLinkModel = new TaskLinkModel($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'test')));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'A')));
$this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'B')));
$this->assertEquals(3, $taskCreationModel->create(array('project_id' => 1, 'title' => 'C')));
$this->assertNotFalse($taskLinkModel->create(1, 2, 2));
$this->assertNotFalse($taskLinkModel->create(1, 3, 2));
$links = $taskLinkModel->getAllGroupedByLabel(1);
$this->assertCount(1, $links);
$this->assertArrayHasKey('blocks', $links);
$this->assertCount(2, $links['blocks']);
$this->assertEquals('test', $links['blocks'][0]['project_name']);
$this->assertEquals('Backlog', $links['blocks'][0]['column_title']);
$this->assertEquals('blocks', $links['blocks'][0]['label']);
}
public function testUpdate()
{
$taskLinkModel = new TaskLinkModel($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
$this->assertEquals(2, $projectModel->create(array('name' => 'test2')));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'A')));
$this->assertEquals(2, $taskCreationModel->create(array('project_id' => 2, 'title' => 'B')));
$this->assertEquals(3, $taskCreationModel->create(array('project_id' => 1, 'title' => 'C')));
$this->assertEquals(1, $taskLinkModel->create(1, 2, 5));
$this->assertTrue($taskLinkModel->update(1, 1, 3, 11));
$links = $taskLinkModel->getAll(1);
$this->assertNotEmpty($links);
$this->assertCount(1, $links);
$this->assertEquals('is fixed by', $links[0]['label']);
$this->assertEquals('C', $links[0]['title']);
$this->assertEquals(3, $links[0]['task_id']);
$links = $taskLinkModel->getAll(2);
$this->assertEmpty($links);
$links = $taskLinkModel->getAll(3);
$this->assertNotEmpty($links);
$this->assertCount(1, $links);
$this->assertEquals('fixes', $links[0]['label']);
$this->assertEquals('A', $links[0]['title']);
$this->assertEquals(1, $links[0]['task_id']);
}
public function testRemove()
{
$taskLinkModel = new TaskLinkModel($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'test')));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'A')));
$this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'B')));
$this->assertEquals(1, $taskLinkModel->create(1, 2, 2));
$links = $taskLinkModel->getAll(1);
$this->assertNotEmpty($links);
$links = $taskLinkModel->getAll(2);
$this->assertNotEmpty($links);
$this->assertTrue($taskLinkModel->remove($links[0]['id']));
$links = $taskLinkModel->getAll(1);
$this->assertEmpty($links);
$links = $taskLinkModel->getAll(2);
$this->assertEmpty($links);
}
public function testGetProjectId()
{
$taskLinkModel = new TaskLinkModel($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'test')));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'A')));
$this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'B')));
$this->assertEquals(1, $taskLinkModel->create(1, 2, 2));
$this->assertEquals(1, $taskLinkModel->getProjectId(1));
$this->assertEquals(0, $taskLinkModel->getProjectId(42));
}
}

View File

@@ -1,196 +0,0 @@
<?php
require_once __DIR__.'/../Base.php';
use Kanboard\Model\TaskFinderModel;
use Kanboard\Model\TaskLinkModel;
use Kanboard\Model\TaskCreationModel;
use Kanboard\Model\ProjectModel;
class TaskLinkTest extends Base
{
// Check postgres issue: "Cardinality violation: 7 ERROR: more than one row returned by a subquery used as an expression"
public function testGetTaskWithMultipleMilestoneLink()
{
$tf = new TaskFinderModel($this->container);
$tl = new TaskLinkModel($this->container);
$p = new ProjectModel($this->container);
$tc = new TaskCreationModel($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test')));
$this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'A')));
$this->assertEquals(2, $tc->create(array('project_id' => 1, 'title' => 'B')));
$this->assertEquals(3, $tc->create(array('project_id' => 1, 'title' => 'C')));
$this->assertNotFalse($tl->create(1, 2, 9));
$this->assertNotFalse($tl->create(1, 3, 9));
$task = $tf->getExtendedQuery()->findOne();
$this->assertNotEmpty($task);
}
public function testCreateTaskLinkWithNoOpposite()
{
$tl = new TaskLinkModel($this->container);
$p = new ProjectModel($this->container);
$tc = new TaskCreationModel($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test')));
$this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'A')));
$this->assertEquals(2, $tc->create(array('project_id' => 1, 'title' => 'B')));
$this->assertEquals(1, $tl->create(1, 2, 1));
$links = $tl->getAll(1);
$this->assertNotEmpty($links);
$this->assertCount(1, $links);
$this->assertEquals('relates to', $links[0]['label']);
$this->assertEquals('B', $links[0]['title']);
$this->assertEquals(2, $links[0]['task_id']);
$this->assertEquals(1, $links[0]['is_active']);
$links = $tl->getAll(2);
$this->assertNotEmpty($links);
$this->assertCount(1, $links);
$this->assertEquals('relates to', $links[0]['label']);
$this->assertEquals('A', $links[0]['title']);
$this->assertEquals(1, $links[0]['task_id']);
$this->assertEquals(1, $links[0]['is_active']);
$task_link = $tl->getById(1);
$this->assertNotEmpty($task_link);
$this->assertEquals(1, $task_link['id']);
$this->assertEquals(1, $task_link['task_id']);
$this->assertEquals(2, $task_link['opposite_task_id']);
$this->assertEquals(1, $task_link['link_id']);
$opposite_task_link = $tl->getOppositeTaskLink($task_link);
$this->assertNotEmpty($opposite_task_link);
$this->assertEquals(2, $opposite_task_link['id']);
$this->assertEquals(2, $opposite_task_link['task_id']);
$this->assertEquals(1, $opposite_task_link['opposite_task_id']);
$this->assertEquals(1, $opposite_task_link['link_id']);
}
public function testCreateTaskLinkWithOpposite()
{
$tl = new TaskLinkModel($this->container);
$p = new ProjectModel($this->container);
$tc = new TaskCreationModel($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test')));
$this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'A')));
$this->assertEquals(2, $tc->create(array('project_id' => 1, 'title' => 'B')));
$this->assertEquals(1, $tl->create(1, 2, 2));
$links = $tl->getAll(1);
$this->assertNotEmpty($links);
$this->assertCount(1, $links);
$this->assertEquals('blocks', $links[0]['label']);
$this->assertEquals('B', $links[0]['title']);
$this->assertEquals(2, $links[0]['task_id']);
$this->assertEquals(1, $links[0]['is_active']);
$links = $tl->getAll(2);
$this->assertNotEmpty($links);
$this->assertCount(1, $links);
$this->assertEquals('is blocked by', $links[0]['label']);
$this->assertEquals('A', $links[0]['title']);
$this->assertEquals(1, $links[0]['task_id']);
$this->assertEquals(1, $links[0]['is_active']);
$task_link = $tl->getById(1);
$this->assertNotEmpty($task_link);
$this->assertEquals(1, $task_link['id']);
$this->assertEquals(1, $task_link['task_id']);
$this->assertEquals(2, $task_link['opposite_task_id']);
$this->assertEquals(2, $task_link['link_id']);
$opposite_task_link = $tl->getOppositeTaskLink($task_link);
$this->assertNotEmpty($opposite_task_link);
$this->assertEquals(2, $opposite_task_link['id']);
$this->assertEquals(2, $opposite_task_link['task_id']);
$this->assertEquals(1, $opposite_task_link['opposite_task_id']);
$this->assertEquals(3, $opposite_task_link['link_id']);
}
public function testGroupByLabel()
{
$tl = new TaskLinkModel($this->container);
$p = new ProjectModel($this->container);
$tc = new TaskCreationModel($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test')));
$this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'A')));
$this->assertEquals(2, $tc->create(array('project_id' => 1, 'title' => 'B')));
$this->assertEquals(3, $tc->create(array('project_id' => 1, 'title' => 'C')));
$this->assertNotFalse($tl->create(1, 2, 2));
$this->assertNotFalse($tl->create(1, 3, 2));
$links = $tl->getAllGroupedByLabel(1);
$this->assertCount(1, $links);
$this->assertArrayHasKey('blocks', $links);
$this->assertCount(2, $links['blocks']);
$this->assertEquals('test', $links['blocks'][0]['project_name']);
$this->assertEquals('Backlog', $links['blocks'][0]['column_title']);
$this->assertEquals('blocks', $links['blocks'][0]['label']);
}
public function testUpdate()
{
$tl = new TaskLinkModel($this->container);
$p = new ProjectModel($this->container);
$tc = new TaskCreationModel($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test1')));
$this->assertEquals(2, $p->create(array('name' => 'test2')));
$this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'A')));
$this->assertEquals(2, $tc->create(array('project_id' => 2, 'title' => 'B')));
$this->assertEquals(3, $tc->create(array('project_id' => 1, 'title' => 'C')));
$this->assertEquals(1, $tl->create(1, 2, 5));
$this->assertTrue($tl->update(1, 1, 3, 11));
$links = $tl->getAll(1);
$this->assertNotEmpty($links);
$this->assertCount(1, $links);
$this->assertEquals('is fixed by', $links[0]['label']);
$this->assertEquals('C', $links[0]['title']);
$this->assertEquals(3, $links[0]['task_id']);
$links = $tl->getAll(2);
$this->assertEmpty($links);
$links = $tl->getAll(3);
$this->assertNotEmpty($links);
$this->assertCount(1, $links);
$this->assertEquals('fixes', $links[0]['label']);
$this->assertEquals('A', $links[0]['title']);
$this->assertEquals(1, $links[0]['task_id']);
}
public function testRemove()
{
$tl = new TaskLinkModel($this->container);
$p = new ProjectModel($this->container);
$tc = new TaskCreationModel($this->container);
$this->assertEquals(1, $p->create(array('name' => 'test')));
$this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'A')));
$this->assertEquals(2, $tc->create(array('project_id' => 1, 'title' => 'B')));
$this->assertEquals(1, $tl->create(1, 2, 2));
$links = $tl->getAll(1);
$this->assertNotEmpty($links);
$links = $tl->getAll(2);
$this->assertNotEmpty($links);
$this->assertTrue($tl->remove($links[0]['id']));
$links = $tl->getAll(1);
$this->assertEmpty($links);
$links = $tl->getAll(2);
$this->assertEmpty($links);
}
}