summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--app/Api/Authorization/ActionAuthorization.php19
-rw-r--r--app/Api/Authorization/CategoryAuthorization.php19
-rw-r--r--app/Api/Authorization/ColumnAuthorization.php19
-rw-r--r--app/Api/Authorization/CommentAuthorization.php19
-rw-r--r--app/Api/Authorization/ProcedureAuthorization.php32
-rw-r--r--app/Api/Authorization/ProjectAuthorization.php35
-rw-r--r--app/Api/Authorization/SubtaskAuthorization.php19
-rw-r--r--app/Api/Authorization/TaskAuthorization.php19
-rw-r--r--app/Api/Authorization/TaskFileAuthorization.php19
-rw-r--r--app/Api/Authorization/TaskLinkAuthorization.php19
-rw-r--r--app/Api/Authorization/UserAuthorization.php22
-rw-r--r--app/Api/Middleware/AuthenticationMiddleware.php (renamed from app/Api/Middleware/AuthenticationApiMiddleware.php)59
-rw-r--r--app/Api/Procedure/ActionProcedure.php (renamed from app/Api/ActionApi.php)12
-rw-r--r--app/Api/Procedure/AppProcedure.php (renamed from app/Api/AppApi.php)8
-rw-r--r--app/Api/Procedure/BaseProcedure.php (renamed from app/Api/BaseApi.php)29
-rw-r--r--app/Api/Procedure/BoardProcedure.php (renamed from app/Api/BoardApi.php)11
-rw-r--r--app/Api/Procedure/CategoryProcedure.php (renamed from app/Api/CategoryApi.php)16
-rw-r--r--app/Api/Procedure/ColumnProcedure.php (renamed from app/Api/ColumnApi.php)15
-rw-r--r--app/Api/Procedure/CommentProcedure.php (renamed from app/Api/CommentApi.php)16
-rw-r--r--app/Api/Procedure/GroupMemberProcedure.php (renamed from app/Api/GroupMemberApi.php)8
-rw-r--r--app/Api/Procedure/GroupProcedure.php (renamed from app/Api/GroupApi.php)8
-rw-r--r--app/Api/Procedure/LinkProcedure.php (renamed from app/Api/LinkApi.php)8
-rw-r--r--app/Api/Procedure/MeProcedure.php (renamed from app/Api/MeApi.php)6
-rw-r--r--app/Api/Procedure/ProjectPermissionProcedure.php (renamed from app/Api/ProjectPermissionApi.php)22
-rw-r--r--app/Api/Procedure/ProjectProcedure.php106
-rw-r--r--app/Api/Procedure/SubtaskProcedure.php (renamed from app/Api/SubtaskApi.php)16
-rw-r--r--app/Api/Procedure/SubtaskTimeTrackingProcedure.php39
-rw-r--r--app/Api/Procedure/SwimlaneProcedure.php (renamed from app/Api/SwimlaneApi.php)21
-rw-r--r--app/Api/Procedure/TaskFileProcedure.php (renamed from app/Api/TaskFileApi.php)17
-rw-r--r--app/Api/Procedure/TaskLinkProcedure.php (renamed from app/Api/TaskLinkApi.php)14
-rw-r--r--app/Api/Procedure/TaskProcedure.php (renamed from app/Api/TaskApi.php)34
-rw-r--r--app/Api/Procedure/UserProcedure.php (renamed from app/Api/UserApi.php)6
-rw-r--r--app/Api/ProjectApi.php87
-rw-r--r--app/Api/SubtaskTimeTrackingApi.php34
-rw-r--r--app/Core/Base.php4
-rw-r--r--app/Model/ActionModel.php12
-rw-r--r--app/Model/CategoryModel.php12
-rw-r--r--app/Model/ColumnModel.php12
-rw-r--r--app/Model/CommentModel.php16
-rw-r--r--app/Model/SubtaskModel.php16
-rw-r--r--app/Model/TaskFileModel.php16
-rw-r--r--app/Model/TaskLinkModel.php16
-rw-r--r--app/ServiceProvider/ApiProvider.php83
-rw-r--r--app/ServiceProvider/AuthenticationProvider.php57
-rw-r--r--composer.json2
-rw-r--r--composer.lock251
-rw-r--r--doc/api-authentication.markdown55
-rw-r--r--doc/api-json-rpc.markdown12
-rw-r--r--doc/api-project-permission-procedures.markdown33
-rw-r--r--doc/api-project-procedures.markdown4
-rw-r--r--tests/integration/ActionProcedureTest.php (renamed from tests/integration/ActionTest.php)4
-rw-r--r--tests/integration/AppProcedureTest.php (renamed from tests/integration/AppTest.php)4
-rw-r--r--tests/integration/BaseProcedureTest.php (renamed from tests/integration/BaseIntegrationTest.php)2
-rw-r--r--tests/integration/BoardProcedureTest.php (renamed from tests/integration/BoardTest.php)4
-rw-r--r--tests/integration/CategoryProcedureTest.php (renamed from tests/integration/CategoryTest.php)4
-rw-r--r--tests/integration/ColumnProcedureTest.php (renamed from tests/integration/ColumnTest.php)4
-rw-r--r--tests/integration/CommentProcedureTest.php (renamed from tests/integration/CommentTest.php)4
-rw-r--r--tests/integration/GroupMemberProcedureTest.php (renamed from tests/integration/GroupMemberTest.php)4
-rw-r--r--tests/integration/GroupProcedureTest.php (renamed from tests/integration/GroupTest.php)4
-rw-r--r--tests/integration/LinkProcedureTest.php (renamed from tests/integration/LinkTest.php)4
-rw-r--r--tests/integration/MeProcedureTest.php (renamed from tests/integration/MeTest.php)9
-rw-r--r--tests/integration/OverdueTaskProcedureTest.php (renamed from tests/integration/OverdueTaskTest.php)4
-rw-r--r--tests/integration/ProcedureAuthorizationTest.php306
-rw-r--r--tests/integration/ProjectPermissionProcedureTest.php (renamed from tests/integration/ProjectPermissionTest.php)4
-rw-r--r--tests/integration/ProjectProcedureTest.php (renamed from tests/integration/ProjectTest.php)4
-rw-r--r--tests/integration/SubtaskProcedureTest.php (renamed from tests/integration/SubtaskTest.php)4
-rw-r--r--tests/integration/SwimlaneProcedureTest.php (renamed from tests/integration/SwimlaneTest.php)4
-rw-r--r--tests/integration/TaskFileProcedureTest.php (renamed from tests/integration/TaskFileTest.php)4
-rw-r--r--tests/integration/TaskLinkProcedureTest.php (renamed from tests/integration/TaskLinkTest.php)4
-rw-r--r--tests/integration/TaskProcedureTest.php (renamed from tests/integration/TaskTest.php)4
-rw-r--r--tests/integration/UserProcedureTest.php (renamed from tests/integration/UserTest.php)4
-rw-r--r--tests/units/Model/ActionModelTest.php (renamed from tests/units/Model/ActionTest.php)20
-rw-r--r--tests/units/Model/CategoryModelTest.php (renamed from tests/units/Model/CategoryTest.php)14
-rw-r--r--tests/units/Model/CommentTest.php86
-rw-r--r--tests/units/Model/SubtaskModelTest.php (renamed from tests/units/Model/SubtaskTest.php)156
-rw-r--r--tests/units/Model/TaskFileModelTest.php (renamed from tests/units/Model/TaskFileTest.php)15
-rw-r--r--tests/units/Model/TaskLinkModelTest.php211
-rw-r--r--tests/units/Model/TaskLinkTest.php196
79 files changed, 1768 insertions, 757 deletions
diff --git a/ChangeLog b/ChangeLog
index b86fea57..42af8ee3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,10 @@
Version 1.0.31 (unreleased)
--------------
+New features:
+
+* Added application and project roles validation for API procedure calls
+
Improvements:
* Rewrite integration tests to run with Docker containers
diff --git a/app/Api/Authorization/ActionAuthorization.php b/app/Api/Authorization/ActionAuthorization.php
new file mode 100644
index 00000000..4b41ad82
--- /dev/null
+++ b/app/Api/Authorization/ActionAuthorization.php
@@ -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));
+ }
+ }
+}
diff --git a/app/Api/Authorization/CategoryAuthorization.php b/app/Api/Authorization/CategoryAuthorization.php
new file mode 100644
index 00000000..f17265a2
--- /dev/null
+++ b/app/Api/Authorization/CategoryAuthorization.php
@@ -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));
+ }
+ }
+}
diff --git a/app/Api/Authorization/ColumnAuthorization.php b/app/Api/Authorization/ColumnAuthorization.php
new file mode 100644
index 00000000..37aecda2
--- /dev/null
+++ b/app/Api/Authorization/ColumnAuthorization.php
@@ -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));
+ }
+ }
+}
diff --git a/app/Api/Authorization/CommentAuthorization.php b/app/Api/Authorization/CommentAuthorization.php
new file mode 100644
index 00000000..ed15512e
--- /dev/null
+++ b/app/Api/Authorization/CommentAuthorization.php
@@ -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));
+ }
+ }
+}
diff --git a/app/Api/Authorization/ProcedureAuthorization.php b/app/Api/Authorization/ProcedureAuthorization.php
new file mode 100644
index 00000000..070a6371
--- /dev/null
+++ b/app/Api/Authorization/ProcedureAuthorization.php
@@ -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');
+ }
+ }
+}
diff --git a/app/Api/Authorization/ProjectAuthorization.php b/app/Api/Authorization/ProjectAuthorization.php
new file mode 100644
index 00000000..21ecf311
--- /dev/null
+++ b/app/Api/Authorization/ProjectAuthorization.php
@@ -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');
+ }
+ }
+}
diff --git a/app/Api/Authorization/SubtaskAuthorization.php b/app/Api/Authorization/SubtaskAuthorization.php
new file mode 100644
index 00000000..fcb57929
--- /dev/null
+++ b/app/Api/Authorization/SubtaskAuthorization.php
@@ -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));
+ }
+ }
+}
diff --git a/app/Api/Authorization/TaskAuthorization.php b/app/Api/Authorization/TaskAuthorization.php
new file mode 100644
index 00000000..db93b76b
--- /dev/null
+++ b/app/Api/Authorization/TaskAuthorization.php
@@ -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));
+ }
+ }
+}
diff --git a/app/Api/Authorization/TaskFileAuthorization.php b/app/Api/Authorization/TaskFileAuthorization.php
new file mode 100644
index 00000000..e40783eb
--- /dev/null
+++ b/app/Api/Authorization/TaskFileAuthorization.php
@@ -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));
+ }
+ }
+}
diff --git a/app/Api/Authorization/TaskLinkAuthorization.php b/app/Api/Authorization/TaskLinkAuthorization.php
new file mode 100644
index 00000000..2f5fc8d5
--- /dev/null
+++ b/app/Api/Authorization/TaskLinkAuthorization.php
@@ -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));
+ }
+ }
+}
diff --git a/app/Api/Authorization/UserAuthorization.php b/app/Api/Authorization/UserAuthorization.php
new file mode 100644
index 00000000..3fd6865c
--- /dev/null
+++ b/app/Api/Authorization/UserAuthorization.php
@@ -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');
+ }
+ }
+}
diff --git a/app/Api/Middleware/AuthenticationApiMiddleware.php b/app/Api/Middleware/AuthenticationMiddleware.php
index b16e10b8..8e309593 100644
--- a/app/Api/Middleware/AuthenticationApiMiddleware.php
+++ b/app/Api/Middleware/AuthenticationMiddleware.php
@@ -13,46 +13,8 @@ use Kanboard\Core\Base;
* @package Kanboard\Api\Middleware
* @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
*
@@ -68,11 +30,8 @@ class AuthenticationApiMiddleware extends Base implements MiddlewareInterface
$this->dispatcher->dispatch('app.bootstrap');
if ($this->isUserAuthenticated($username, $password)) {
- $this->checkProcedurePermission(true, $procedureName);
$this->userSession->initialize($this->userModel->getByUsername($username));
- } elseif ($this->isAppAuthenticated($username, $password)) {
- $this->checkProcedurePermission(false, $procedureName);
- } else {
+ } elseif (! $this->isAppAuthenticated($username, $password)) {
$this->logger->error('API authentication failure for '.$username);
throw new AuthenticationFailureException('Wrong credentials');
}
@@ -120,18 +79,4 @@ class AuthenticationApiMiddleware extends Base implements MiddlewareInterface
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);
- }
}
diff --git a/app/Api/ActionApi.php b/app/Api/Procedure/ActionProcedure.php
index 116742d8..4043dbb9 100644
--- a/app/Api/ActionApi.php
+++ b/app/Api/Procedure/ActionProcedure.php
@@ -1,16 +1,17 @@
<?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
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class ActionApi extends Base
+class ActionProcedure extends BaseProcedure
{
public function getAvailableActions()
{
@@ -29,16 +30,19 @@ class ActionApi extends Base
public function removeAction($action_id)
{
+ ActionAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAction', $action_id);
return $this->actionModel->remove($action_id);
}
public function getActions($project_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getActions', $project_id);
return $this->actionModel->getAllByProject($project_id);
}
public function createAction($project_id, $event_name, $action_name, array $params)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createAction', $project_id);
$values = array(
'project_id' => $project_id,
'event_name' => $event_name,
diff --git a/app/Api/AppApi.php b/app/Api/Procedure/AppProcedure.php
index 637de5c5..60af4a60 100644
--- a/app/Api/AppApi.php
+++ b/app/Api/Procedure/AppProcedure.php
@@ -1,16 +1,14 @@
<?php
-namespace Kanboard\Api;
-
-use Kanboard\Core\Base;
+namespace Kanboard\Api\Procedure;
/**
* App API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class AppApi extends Base
+class AppProcedure extends BaseProcedure
{
public function getTimezone()
{
diff --git a/app/Api/BaseApi.php b/app/Api/Procedure/BaseProcedure.php
index 8f18802c..0aa43428 100644
--- a/app/Api/BaseApi.php
+++ b/app/Api/Procedure/BaseProcedure.php
@@ -1,30 +1,25 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
use JsonRPC\Exception\AccessDeniedException;
+use Kanboard\Api\Authorization\ProcedureAuthorization;
+use Kanboard\Api\Authorization\UserAuthorization;
use Kanboard\Core\Base;
+use ReflectionClass;
/**
* Base class
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @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())) {
- throw new AccessDeniedException('Permission denied');
- }
- }
-
- public function checkTaskPermission($task_id)
- {
- if ($this->userSession->isLogged()) {
- $this->checkProjectPermission($this->taskFinderModel->getProjectId($task_id));
- }
+ ProcedureAuthorization::getInstance($this->container)->check($procedure);
+ UserAuthorization::getInstance($this->container)->check($this->getClassName(), $procedure);
}
protected function formatTask($task)
@@ -82,4 +77,10 @@ abstract class BaseApi extends Base
return $values;
}
+
+ protected function getClassName()
+ {
+ $reflection = new ReflectionClass(get_called_class());
+ return $reflection->getShortName();
+ }
}
diff --git a/app/Api/BoardApi.php b/app/Api/Procedure/BoardProcedure.php
index 70f21c0e..674b5466 100644
--- a/app/Api/BoardApi.php
+++ b/app/Api/Procedure/BoardProcedure.php
@@ -1,21 +1,22 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
+use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Formatter\BoardFormatter;
/**
* Board API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class BoardApi extends BaseApi
+class BoardProcedure extends BaseProcedure
{
public function getBoard($project_id)
{
- $this->checkProjectPermission($project_id);
-
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getBoard', $project_id);
+
return BoardFormatter::getInstance($this->container)
->withProjectId($project_id)
->withQuery($this->taskFinderModel->getExtendedQuery())
diff --git a/app/Api/CategoryApi.php b/app/Api/Procedure/CategoryProcedure.php
index c56cfb35..3ebbd908 100644
--- a/app/Api/CategoryApi.php
+++ b/app/Api/Procedure/CategoryProcedure.php
@@ -1,34 +1,40 @@
<?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
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class CategoryApi extends Base
+class CategoryProcedure extends BaseProcedure
{
public function getCategory($category_id)
{
+ CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'getCategory', $category_id);
return $this->categoryModel->getById($category_id);
}
public function getAllCategories($project_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllCategories', $project_id);
return $this->categoryModel->getAll($project_id);
}
public function removeCategory($category_id)
{
+ CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeCategory', $category_id);
return $this->categoryModel->remove($category_id);
}
public function createCategory($project_id, $name)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createCategory', $project_id);
+
$values = array(
'project_id' => $project_id,
'name' => $name,
@@ -40,6 +46,8 @@ class CategoryApi extends Base
public function updateCategory($id, $name)
{
+ CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateCategory', $id);
+
$values = array(
'id' => $id,
'name' => $name,
diff --git a/app/Api/ColumnApi.php b/app/Api/Procedure/ColumnProcedure.php
index aa4026f6..ab9d173b 100644
--- a/app/Api/ColumnApi.php
+++ b/app/Api/Procedure/ColumnProcedure.php
@@ -1,42 +1,51 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
+
+use Kanboard\Api\Authorization\ColumnAuthorization;
+use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Column API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class ColumnApi extends BaseApi
+class ColumnProcedure extends BaseProcedure
{
public function getColumns($project_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getColumns', $project_id);
return $this->columnModel->getAll($project_id);
}
public function getColumn($column_id)
{
+ ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'getColumn', $column_id);
return $this->columnModel->getById($column_id);
}
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);
}
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);
}
public function removeColumn($column_id)
{
+ ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeColumn', $column_id);
return $this->columnModel->remove($column_id);
}
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);
}
}
diff --git a/app/Api/CommentApi.php b/app/Api/Procedure/CommentProcedure.php
index 8358efee..019a49bb 100644
--- a/app/Api/CommentApi.php
+++ b/app/Api/Procedure/CommentProcedure.php
@@ -1,34 +1,40 @@
<?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
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class CommentApi extends Base
+class CommentProcedure extends BaseProcedure
{
public function getComment($comment_id)
{
+ CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'getComment', $comment_id);
return $this->commentModel->getById($comment_id);
}
public function getAllComments($task_id)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllComments', $task_id);
return $this->commentModel->getAll($task_id);
}
public function removeComment($comment_id)
{
+ CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeComment', $comment_id);
return $this->commentModel->remove($comment_id);
}
public function createComment($task_id, $user_id, $content, $reference = '')
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createComment', $task_id);
+
$values = array(
'task_id' => $task_id,
'user_id' => $user_id,
@@ -43,6 +49,8 @@ class CommentApi extends Base
public function updateComment($id, $content)
{
+ CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateComment', $id);
+
$values = array(
'id' => $id,
'comment' => $content,
diff --git a/app/Api/GroupMemberApi.php b/app/Api/Procedure/GroupMemberProcedure.php
index e09f6975..081d6ac8 100644
--- a/app/Api/GroupMemberApi.php
+++ b/app/Api/Procedure/GroupMemberProcedure.php
@@ -1,16 +1,14 @@
<?php
-namespace Kanboard\Api;
-
-use Kanboard\Core\Base;
+namespace Kanboard\Api\Procedure;
/**
* Group Member API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class GroupMemberApi extends Base
+class GroupMemberProcedure extends BaseProcedure
{
public function getMemberGroups($user_id)
{
diff --git a/app/Api/GroupApi.php b/app/Api/Procedure/GroupProcedure.php
index 1701edc3..804940a2 100644
--- a/app/Api/GroupApi.php
+++ b/app/Api/Procedure/GroupProcedure.php
@@ -1,16 +1,14 @@
<?php
-namespace Kanboard\Api;
-
-use Kanboard\Core\Base;
+namespace Kanboard\Api\Procedure;
/**
* Group API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class GroupApi extends Base
+class GroupProcedure extends BaseProcedure
{
public function createGroup($name, $external_id = '')
{
diff --git a/app/Api/LinkApi.php b/app/Api/Procedure/LinkProcedure.php
index d8e525e4..b4cecf3a 100644
--- a/app/Api/LinkApi.php
+++ b/app/Api/Procedure/LinkProcedure.php
@@ -1,16 +1,14 @@
<?php
-namespace Kanboard\Api;
-
-use Kanboard\Core\Base;
+namespace Kanboard\Api\Procedure;
/**
* Link API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class LinkApi extends Base
+class LinkProcedure extends BaseProcedure
{
/**
* Get a link by id
diff --git a/app/Api/MeApi.php b/app/Api/Procedure/MeProcedure.php
index 497749b6..e59e6522 100644
--- a/app/Api/MeApi.php
+++ b/app/Api/Procedure/MeProcedure.php
@@ -1,16 +1,16 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
use Kanboard\Model\SubtaskModel;
/**
* Me API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class MeApi extends BaseApi
+class MeProcedure extends BaseProcedure
{
public function getMe()
{
diff --git a/app/Api/ProjectPermissionApi.php b/app/Api/Procedure/ProjectPermissionProcedure.php
index 37c5e13c..e22e1d62 100644
--- a/app/Api/ProjectPermissionApi.php
+++ b/app/Api/Procedure/ProjectPermissionProcedure.php
@@ -1,55 +1,69 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
-use Kanboard\Core\Base;
+use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Core\Security\Role;
/**
* Project Permission API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class ProjectPermissionApi extends Base
+class ProjectPermissionProcedure extends BaseProcedure
{
public function getProjectUsers($project_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectUsers', $project_id);
return $this->projectUserRoleModel->getAllUsers($project_id);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
+
+ 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);
+ }
}
diff --git a/app/Api/Procedure/ProjectProcedure.php b/app/Api/Procedure/ProjectProcedure.php
new file mode 100644
index 00000000..9187f221
--- /dev/null
+++ b/app/Api/Procedure/ProjectProcedure.php
@@ -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);
+ }
+}
diff --git a/app/Api/SubtaskApi.php b/app/Api/Procedure/SubtaskProcedure.php
index 5764ff7d..e2400912 100644
--- a/app/Api/SubtaskApi.php
+++ b/app/Api/Procedure/SubtaskProcedure.php
@@ -1,34 +1,40 @@
<?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
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class SubtaskApi extends Base
+class SubtaskProcedure extends BaseProcedure
{
public function getSubtask($subtask_id)
{
+ SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSubtask', $subtask_id);
return $this->subtaskModel->getById($subtask_id);
}
public function getAllSubtasks($task_id)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllSubtasks', $task_id);
return $this->subtaskModel->getAll($task_id);
}
public function removeSubtask($subtask_id)
{
+ SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeSubtask', $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)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createSubtask', $task_id);
+
$values = array(
'title' => $title,
'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)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateSubtask', $task_id);
+
$values = array(
'id' => $id,
'task_id' => $task_id,
diff --git a/app/Api/Procedure/SubtaskTimeTrackingProcedure.php b/app/Api/Procedure/SubtaskTimeTrackingProcedure.php
new file mode 100644
index 00000000..5d1988d6
--- /dev/null
+++ b/app/Api/Procedure/SubtaskTimeTrackingProcedure.php
@@ -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);
+ }
+}
diff --git a/app/Api/SwimlaneApi.php b/app/Api/Procedure/SwimlaneProcedure.php
index c3c56a71..9b7d181d 100644
--- a/app/Api/SwimlaneApi.php
+++ b/app/Api/Procedure/SwimlaneProcedure.php
@@ -1,34 +1,39 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
-use Kanboard\Core\Base;
+use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Swimlane API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class SwimlaneApi extends Base
+class SwimlaneProcedure extends BaseProcedure
{
public function getActiveSwimlanes($project_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getActiveSwimlanes', $project_id);
return $this->swimlaneModel->getSwimlanes($project_id);
}
public function getAllSwimlanes($project_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllSwimlanes', $project_id);
return $this->swimlaneModel->getAll($project_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)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSwimlaneByName', $project_id);
return $this->swimlaneModel->getByName($project_id, $name);
}
@@ -39,11 +44,13 @@ class SwimlaneApi extends Base
public function getDefaultSwimlane($project_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getDefaultSwimlane', $project_id);
return $this->swimlaneModel->getDefault($project_id);
}
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));
}
@@ -60,21 +67,25 @@ class SwimlaneApi extends Base
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);
}
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);
}
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);
}
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);
}
}
diff --git a/app/Api/TaskFileApi.php b/app/Api/Procedure/TaskFileProcedure.php
index 7b27477c..5aa7ea0b 100644
--- a/app/Api/TaskFileApi.php
+++ b/app/Api/Procedure/TaskFileProcedure.php
@@ -1,29 +1,36 @@
<?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;
/**
* Task File API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class TaskFileApi extends BaseApi
+class TaskFileProcedure extends BaseProcedure
{
public function getTaskFile($file_id)
{
+ TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskFile', $file_id);
return $this->taskFileModel->getById($file_id);
}
public function getAllTaskFiles($task_id)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTaskFiles', $task_id);
return $this->taskFileModel->getAll($task_id);
}
public function downloadTaskFile($file_id)
{
+ TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'downloadTaskFile', $file_id);
+
try {
$file = $this->taskFileModel->getById($file_id);
@@ -39,6 +46,8 @@ class TaskFileApi extends BaseApi
public function createTaskFile($project_id, $task_id, $filename, $blob)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskFile', $project_id);
+
try {
return $this->taskFileModel->uploadContent($task_id, $filename, $blob);
} catch (ObjectStorageException $e) {
@@ -49,11 +58,13 @@ class TaskFileApi extends BaseApi
public function removeTaskFile($file_id)
{
+ TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskFile', $file_id);
return $this->taskFileModel->remove($file_id);
}
public function removeAllTaskFiles($task_id)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAllTaskFiles', $task_id);
return $this->taskFileModel->removeAll($task_id);
}
}
diff --git a/app/Api/TaskLinkApi.php b/app/Api/Procedure/TaskLinkProcedure.php
index bb809133..375266fb 100644
--- a/app/Api/TaskLinkApi.php
+++ b/app/Api/Procedure/TaskLinkProcedure.php
@@ -1,16 +1,17 @@
<?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
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class TaskLinkApi extends Base
+class TaskLinkProcedure extends BaseProcedure
{
/**
* Get a task link
@@ -21,6 +22,7 @@ class TaskLinkApi extends Base
*/
public function getTaskLinkById($task_link_id)
{
+ TaskLinkAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskLinkById', $task_link_id);
return $this->taskLinkModel->getById($task_link_id);
}
@@ -33,6 +35,7 @@ class TaskLinkApi extends Base
*/
public function getAllTaskLinks($task_id)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTaskLinks', $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)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskLink', $task_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)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTaskLink', $task_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)
{
+ TaskLinkAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskLink', $task_link_id);
return $this->taskLinkModel->remove($task_link_id);
}
}
diff --git a/app/Api/TaskApi.php b/app/Api/Procedure/TaskProcedure.php
index 523bfaa0..2d29a4ef 100644
--- a/app/Api/TaskApi.php
+++ b/app/Api/Procedure/TaskProcedure.php
@@ -1,39 +1,41 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
+use Kanboard\Api\Authorization\ProjectAuthorization;
+use Kanboard\Api\Authorization\TaskAuthorization;
use Kanboard\Filter\TaskProjectFilter;
use Kanboard\Model\TaskModel;
/**
* Task API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class TaskApi extends BaseApi
+class TaskProcedure extends BaseProcedure
{
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();
}
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));
}
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));
}
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));
}
@@ -44,40 +46,43 @@ class TaskApi extends BaseApi
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);
}
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);
}
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);
}
public function removeTask($task_id)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTask', $task_id);
return $this->taskModel->remove($task_id);
}
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);
}
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);
}
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);
}
@@ -86,8 +91,8 @@ class TaskApi extends BaseApi
$recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0,
$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)) {
return false;
}
@@ -127,8 +132,7 @@ class TaskApi extends BaseApi
$recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = 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);
if ($project_id === 0) {
diff --git a/app/Api/UserApi.php b/app/Api/Procedure/UserProcedure.php
index 6cb9df1c..145f85bf 100644
--- a/app/Api/UserApi.php
+++ b/app/Api/Procedure/UserProcedure.php
@@ -1,6 +1,6 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
use LogicException;
use Kanboard\Core\Security\Role;
@@ -11,10 +11,10 @@ use Kanboard\Core\Ldap\User as LdapUser;
/**
* User API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class UserApi extends BaseApi
+class UserProcedure extends BaseProcedure
{
public function getUser($user_id)
{
diff --git a/app/Api/ProjectApi.php b/app/Api/ProjectApi.php
deleted file mode 100644
index a726d4eb..00000000
--- a/app/Api/ProjectApi.php
+++ /dev/null
@@ -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);
- }
-}
diff --git a/app/Api/SubtaskTimeTrackingApi.php b/app/Api/SubtaskTimeTrackingApi.php
deleted file mode 100644
index 0e700b31..00000000
--- a/app/Api/SubtaskTimeTrackingApi.php
+++ /dev/null
@@ -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);
- }
-}
diff --git a/app/Core/Base.php b/app/Core/Base.php
index e5dd6ad9..eacca65d 100644
--- a/app/Core/Base.php
+++ b/app/Core/Base.php
@@ -35,8 +35,12 @@ use Pimple\Container;
* @property \Kanboard\Core\Security\AuthenticationManager $authenticationManager
* @property \Kanboard\Core\Security\AccessMap $applicationAccessMap
* @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 $projectAuthorization
+ * @property \Kanboard\Core\Security\Authorization $apiAuthorization
+ * @property \Kanboard\Core\Security\Authorization $apiProjectAuthorization
* @property \Kanboard\Core\Security\Role $role
* @property \Kanboard\Core\Security\Token $token
* @property \Kanboard\Core\Session\FlashMessage $flash
diff --git a/app/Model/ActionModel.php b/app/Model/ActionModel.php
index 53393ed5..b5d2bd06 100644
--- a/app/Model/ActionModel.php
+++ b/app/Model/ActionModel.php
@@ -86,6 +86,18 @@ class ActionModel extends Base
}
/**
+ * 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
*
* @access private
diff --git a/app/Model/CategoryModel.php b/app/Model/CategoryModel.php
index 62fb5611..024d0026 100644
--- a/app/Model/CategoryModel.php
+++ b/app/Model/CategoryModel.php
@@ -56,6 +56,18 @@ class CategoryModel extends Base
}
/**
+ * 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
*
* @access public
diff --git a/app/Model/ColumnModel.php b/app/Model/ColumnModel.php
index 1adac0f2..795fe692 100644
--- a/app/Model/ColumnModel.php
+++ b/app/Model/ColumnModel.php
@@ -32,6 +32,18 @@ class ColumnModel extends Base
}
/**
+ * 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
*
* @access public
diff --git a/app/Model/CommentModel.php b/app/Model/CommentModel.php
index 36e1fc48..4231f29d 100644
--- a/app/Model/CommentModel.php
+++ b/app/Model/CommentModel.php
@@ -30,6 +30,22 @@ class CommentModel extends Base
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
*
* @access public
diff --git a/app/Model/SubtaskModel.php b/app/Model/SubtaskModel.php
index 019064ad..a97bddbf 100644
--- a/app/Model/SubtaskModel.php
+++ b/app/Model/SubtaskModel.php
@@ -52,6 +52,22 @@ class SubtaskModel extends Base
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
*
* @access public
diff --git a/app/Model/TaskFileModel.php b/app/Model/TaskFileModel.php
index 24c1ad4b..7603019a 100644
--- a/app/Model/TaskFileModel.php
+++ b/app/Model/TaskFileModel.php
@@ -73,6 +73,22 @@ class TaskFileModel extends FileModel
}
/**
+ * 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
*
* @access public
diff --git a/app/Model/TaskLinkModel.php b/app/Model/TaskLinkModel.php
index 45225e35..09978eae 100644
--- a/app/Model/TaskLinkModel.php
+++ b/app/Model/TaskLinkModel.php
@@ -29,6 +29,22 @@ class TaskLinkModel extends Base
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
*
* @access public
diff --git a/app/ServiceProvider/ApiProvider.php b/app/ServiceProvider/ApiProvider.php
index e0312056..f88d9b4f 100644
--- a/app/ServiceProvider/ApiProvider.php
+++ b/app/ServiceProvider/ApiProvider.php
@@ -3,26 +3,26 @@
namespace Kanboard\ServiceProvider;
use JsonRPC\Server;
-use Kanboard\Api\ActionApi;
-use Kanboard\Api\AppApi;
-use Kanboard\Api\BoardApi;
-use Kanboard\Api\CategoryApi;
-use Kanboard\Api\ColumnApi;
-use Kanboard\Api\CommentApi;
-use Kanboard\Api\TaskFileApi;
-use Kanboard\Api\GroupApi;
-use Kanboard\Api\GroupMemberApi;
-use Kanboard\Api\LinkApi;
-use Kanboard\Api\MeApi;
-use Kanboard\Api\Middleware\AuthenticationApiMiddleware;
-use Kanboard\Api\ProjectApi;
-use Kanboard\Api\ProjectPermissionApi;
-use Kanboard\Api\SubtaskApi;
-use Kanboard\Api\SubtaskTimeTrackingApi;
-use Kanboard\Api\SwimlaneApi;
-use Kanboard\Api\TaskApi;
-use Kanboard\Api\TaskLinkApi;
-use Kanboard\Api\UserApi;
+use Kanboard\Api\Procedure\ActionProcedure;
+use Kanboard\Api\Procedure\AppProcedure;
+use Kanboard\Api\Procedure\BoardProcedure;
+use Kanboard\Api\Procedure\CategoryProcedure;
+use Kanboard\Api\Procedure\ColumnProcedure;
+use Kanboard\Api\Procedure\CommentProcedure;
+use Kanboard\Api\Procedure\TaskFileProcedure;
+use Kanboard\Api\Procedure\GroupProcedure;
+use Kanboard\Api\Procedure\GroupMemberProcedure;
+use Kanboard\Api\Procedure\LinkProcedure;
+use Kanboard\Api\Procedure\MeProcedure;
+use Kanboard\Api\Middleware\AuthenticationMiddleware;
+use Kanboard\Api\Procedure\ProjectProcedure;
+use Kanboard\Api\Procedure\ProjectPermissionProcedure;
+use Kanboard\Api\Procedure\SubtaskProcedure;
+use Kanboard\Api\Procedure\SubtaskTimeTrackingProcedure;
+use Kanboard\Api\Procedure\SwimlaneProcedure;
+use Kanboard\Api\Procedure\TaskProcedure;
+use Kanboard\Api\Procedure\TaskLinkProcedure;
+use Kanboard\Api\Procedure\UserProcedure;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
@@ -45,31 +45,32 @@ class ApiProvider implements ServiceProviderInterface
$server = new Server();
$server->setAuthenticationHeader(API_AUTHENTICATION_HEADER);
$server->getMiddlewareHandler()
- ->withMiddleware(new AuthenticationApiMiddleware($container))
+ ->withMiddleware(new AuthenticationMiddleware($container))
;
$server->getProcedureHandler()
- ->withObject(new MeApi($container))
- ->withObject(new ActionApi($container))
- ->withObject(new AppApi($container))
- ->withObject(new BoardApi($container))
- ->withObject(new ColumnApi($container))
- ->withObject(new CategoryApi($container))
- ->withObject(new CommentApi($container))
- ->withObject(new TaskFileApi($container))
- ->withObject(new LinkApi($container))
- ->withObject(new ProjectApi($container))
- ->withObject(new ProjectPermissionApi($container))
- ->withObject(new SubtaskApi($container))
- ->withObject(new SubtaskTimeTrackingApi($container))
- ->withObject(new SwimlaneApi($container))
- ->withObject(new TaskApi($container))
- ->withObject(new TaskLinkApi($container))
- ->withObject(new UserApi($container))
- ->withObject(new GroupApi($container))
- ->withObject(new GroupMemberApi($container))
+ ->withObject(new MeProcedure($container))
+ ->withObject(new ActionProcedure($container))
+ ->withObject(new AppProcedure($container))
+ ->withObject(new BoardProcedure($container))
+ ->withObject(new ColumnProcedure($container))
+ ->withObject(new CategoryProcedure($container))
+ ->withObject(new CommentProcedure($container))
+ ->withObject(new TaskFileProcedure($container))
+ ->withObject(new LinkProcedure($container))
+ ->withObject(new ProjectProcedure($container))
+ ->withObject(new ProjectPermissionProcedure($container))
+ ->withObject(new SubtaskProcedure($container))
+ ->withObject(new SubtaskTimeTrackingProcedure($container))
+ ->withObject(new SwimlaneProcedure($container))
+ ->withObject(new TaskProcedure($container))
+ ->withObject(new TaskLinkProcedure($container))
+ ->withObject(new UserProcedure($container))
+ ->withObject(new GroupProcedure($container))
+ ->withObject(new GroupMemberProcedure($container))
+ ->withBeforeMethod('beforeProcedure')
;
-
+
$container['api'] = $server;
return $container;
}
diff --git a/app/ServiceProvider/AuthenticationProvider.php b/app/ServiceProvider/AuthenticationProvider.php
index 84e4354d..751fe514 100644
--- a/app/ServiceProvider/AuthenticationProvider.php
+++ b/app/ServiceProvider/AuthenticationProvider.php
@@ -46,9 +46,13 @@ class AuthenticationProvider implements ServiceProviderInterface
$container['projectAccessMap'] = $this->getProjectAccessMap();
$container['applicationAccessMap'] = $this->getApplicationAccessMap();
+ $container['apiAccessMap'] = $this->getApiAccessMap();
+ $container['apiProjectAccessMap'] = $this->getApiProjectAccessMap();
$container['projectAuthorization'] = new Authorization($container['projectAccessMap']);
$container['applicationAuthorization'] = new Authorization($container['applicationAccessMap']);
+ $container['apiAuthorization'] = new Authorization($container['apiAccessMap']);
+ $container['apiProjectAuthorization'] = new Authorization($container['apiProjectAccessMap']);
return $container;
}
@@ -151,4 +155,57 @@ class AuthenticationProvider implements ServiceProviderInterface
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;
+ }
}
diff --git a/composer.json b/composer.json
index 85fdb5ad..bcac020e 100644
--- a/composer.json
+++ b/composer.json
@@ -26,7 +26,7 @@
"christian-riesen/otp" : "1.4",
"eluceo/ical": "0.8.0",
"erusev/parsedown" : "1.6.0",
- "fguillot/json-rpc" : "1.2.0",
+ "fguillot/json-rpc" : "1.2.1",
"fguillot/picodb" : "1.0.12",
"fguillot/simpleLogger" : "1.0.1",
"fguillot/simple-validator" : "1.0.0",
diff --git a/composer.lock b/composer.lock
index e0177ed5..e6a72582 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "2de2026649db7bc41653bef80f974c6a",
- "content-hash": "ea8ef74f1f1cf53b9f96b7609d756873",
+ "hash": "283af0b856598f5bc3d8ee0b226959e5",
+ "content-hash": "18c0bbff5406ceb8b567d9655de26746",
"packages": [
{
"name": "christian-riesen/base32",
@@ -203,16 +203,16 @@
},
{
"name": "fguillot/json-rpc",
- "version": "v1.2.0",
+ "version": "v1.2.1",
"source": {
"type": "git",
"url": "https://github.com/fguillot/JsonRPC.git",
- "reference": "b002320b10aa1eeb7aee83f7b703cd6a6e99ff78"
+ "reference": "d491bb549bfa11aff4c37abcea2ffb28c9523f69"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/b002320b10aa1eeb7aee83f7b703cd6a6e99ff78",
- "reference": "b002320b10aa1eeb7aee83f7b703cd6a6e99ff78",
+ "url": "https://api.github.com/repos/fguillot/JsonRPC/zipball/d491bb549bfa11aff4c37abcea2ffb28c9523f69",
+ "reference": "d491bb549bfa11aff4c37abcea2ffb28c9523f69",
"shasum": ""
},
"require": {
@@ -238,7 +238,7 @@
],
"description": "Simple Json-RPC client/server library that just works",
"homepage": "https://github.com/fguillot/JsonRPC",
- "time": "2016-05-29 13:06:36"
+ "time": "2016-06-25 23:11:10"
},
{
"name": "fguillot/picodb",
@@ -682,16 +682,16 @@
},
{
"name": "symfony/console",
- "version": "v2.8.6",
+ "version": "v2.8.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "48221d3de4dc22d2cd57c97e8b9361821da86609"
+ "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/48221d3de4dc22d2cd57c97e8b9361821da86609",
- "reference": "48221d3de4dc22d2cd57c97e8b9361821da86609",
+ "url": "https://api.github.com/repos/symfony/console/zipball/5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3",
+ "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3",
"shasum": ""
},
"require": {
@@ -738,20 +738,20 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
- "time": "2016-04-26 12:00:47"
+ "time": "2016-06-06 15:06:25"
},
{
"name": "symfony/event-dispatcher",
- "version": "v2.8.6",
+ "version": "v2.8.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "a158f13992a3147d466af7a23b564ac719a4ddd8"
+ "reference": "2a6b8713f8bdb582058cfda463527f195b066110"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a158f13992a3147d466af7a23b564ac719a4ddd8",
- "reference": "a158f13992a3147d466af7a23b564ac719a4ddd8",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2a6b8713f8bdb582058cfda463527f195b066110",
+ "reference": "2a6b8713f8bdb582058cfda463527f195b066110",
"shasum": ""
},
"require": {
@@ -798,7 +798,7 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
- "time": "2016-05-03 18:59:18"
+ "time": "2016-06-06 11:11:27"
},
{
"name": "symfony/polyfill-mbstring",
@@ -916,38 +916,135 @@
"time": "2015-06-14 21:17:01"
},
{
+ "name": "phpdocumentor/reflection-common",
+ "version": "1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+ "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
+ "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.6"
+ },
+ "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": "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": "2.0.4",
+ "version": "3.1.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
- "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8"
+ "reference": "9270140b940ff02e58ec577c237274e92cd40cdd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8",
- "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9270140b940ff02e58ec577c237274e92cd40cdd",
+ "reference": "9270140b940ff02e58ec577c237274e92cd40cdd",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=5.5",
+ "phpdocumentor/reflection-common": "^1.0@dev",
+ "phpdocumentor/type-resolver": "^0.2.0",
+ "webmozart/assert": "^1.0"
},
"require-dev": {
- "phpunit/phpunit": "~4.0"
+ "mockery/mockery": "^0.9.4",
+ "phpunit/phpunit": "^4.4"
},
- "suggest": {
- "dflydev/markdown": "~1.0",
- "erusev/parsedown": "~1.0"
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": [
+ "src/"
+ ]
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "me@mikevanriel.com"
+ }
+ ],
+ "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": "phpdocumentor/type-resolver",
+ "version": "0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/TypeResolver.git",
+ "reference": "b39c7a5b194f9ed7bd0dd345c751007a41862443"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b39c7a5b194f9ed7bd0dd345c751007a41862443",
+ "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": "2.0.x-dev"
+ "dev-master": "1.0.x-dev"
}
},
"autoload": {
- "psr-0": {
- "phpDocumentor": [
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": [
"src/"
]
}
@@ -959,39 +1056,39 @@
"authors": [
{
"name": "Mike van Riel",
- "email": "mike.vanriel@naenius.com"
+ "email": "me@mikevanriel.com"
}
],
- "time": "2015-02-03 12:10:50"
+ "time": "2016-06-10 07:14:17"
},
{
"name": "phpspec/prophecy",
- "version": "v1.6.0",
+ "version": "v1.6.1",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
- "reference": "3c91bdf81797d725b14cb62906f9a4ce44235972"
+ "reference": "58a8137754bc24b25740d4281399a4a3596058e0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpspec/prophecy/zipball/3c91bdf81797d725b14cb62906f9a4ce44235972",
- "reference": "3c91bdf81797d725b14cb62906f9a4ce44235972",
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0",
+ "reference": "58a8137754bc24b25740d4281399a4a3596058e0",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": "^5.3|^7.0",
- "phpdocumentor/reflection-docblock": "~2.0",
- "sebastian/comparator": "~1.1",
- "sebastian/recursion-context": "~1.0"
+ "phpdocumentor/reflection-docblock": "^2.0|^3.0.2",
+ "sebastian/comparator": "^1.1",
+ "sebastian/recursion-context": "^1.0"
},
"require-dev": {
- "phpspec/phpspec": "~2.0"
+ "phpspec/phpspec": "^2.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.5.x-dev"
+ "dev-master": "1.6.x-dev"
}
},
"autoload": {
@@ -1024,7 +1121,7 @@
"spy",
"stub"
],
- "time": "2016-02-15 07:46:21"
+ "time": "2016-06-07 08:13:47"
},
{
"name": "phpunit/php-code-coverage",
@@ -1629,16 +1726,16 @@
},
{
"name": "sebastian/exporter",
- "version": "1.2.1",
+ "version": "1.2.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "7ae5513327cb536431847bcc0c10edba2701064e"
+ "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e",
- "reference": "7ae5513327cb536431847bcc0c10edba2701064e",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4",
+ "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4",
"shasum": ""
},
"require": {
@@ -1646,12 +1743,13 @@
"sebastian/recursion-context": "~1.0"
},
"require-dev": {
+ "ext-mbstring": "*",
"phpunit/phpunit": "~4.4"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.2.x-dev"
+ "dev-master": "1.3.x-dev"
}
},
"autoload": {
@@ -1691,7 +1789,7 @@
"export",
"exporter"
],
- "time": "2015-06-21 07:55:53"
+ "time": "2016-06-17 09:04:28"
},
{
"name": "sebastian/global-state",
@@ -1834,16 +1932,16 @@
},
{
"name": "symfony/stopwatch",
- "version": "v2.8.6",
+ "version": "v2.8.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/stopwatch.git",
- "reference": "9e24824b2a9a16e17ab997f61d70bc03948e434e"
+ "reference": "5e628055488bcc42dbace3af65be435d094e37e4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/stopwatch/zipball/9e24824b2a9a16e17ab997f61d70bc03948e434e",
- "reference": "9e24824b2a9a16e17ab997f61d70bc03948e434e",
+ "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5e628055488bcc42dbace3af65be435d094e37e4",
+ "reference": "5e628055488bcc42dbace3af65be435d094e37e4",
"shasum": ""
},
"require": {
@@ -1879,7 +1977,7 @@
],
"description": "Symfony Stopwatch Component",
"homepage": "https://symfony.com",
- "time": "2016-03-04 07:54:35"
+ "time": "2016-06-06 11:11:27"
},
{
"name": "symfony/yaml",
@@ -1927,6 +2025,55 @@
"description": "Symfony Yaml Component",
"homepage": "http://symfony.com",
"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": [],
diff --git a/doc/api-authentication.markdown b/doc/api-authentication.markdown
index 962e5b1b..3ba1e8f5 100644
--- a/doc/api-authentication.markdown
+++ b/doc/api-authentication.markdown
@@ -1,48 +1,26 @@
API Authentication
==================
-Default method (HTTP Basic)
----------------------------
+API endpoint
+------------
+
+URL: `https://YOUR_SERVER/jsonrpc.php`
-The API credentials are available on the settings page.
-- API end-point: `https://YOUR_SERVER/jsonrpc.php`
+Default method (HTTP Basic)
+---------------------------
-If you want to use the "application api":
+### Application credentials
- Username: `jsonrpc`
- 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).
-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
------------------
@@ -64,3 +42,14 @@ curl \
-d '{"jsonrpc": "2.0", "method": "getAllProjects", "id": 1}' \
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`.
diff --git a/doc/api-json-rpc.markdown b/doc/api-json-rpc.markdown
index bb14b008..0f922a7c 100644
--- a/doc/api-json-rpc.markdown
+++ b/doc/api-json-rpc.markdown
@@ -8,25 +8,25 @@ There are two types of API access:
### 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
- No permission checked
- 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...
### User API
- Access to the API with the user credentials (username and password)
-- Access to a restricted set of procedures
-- The project permissions are checked
+- Application role and project permissions are checked for each procedure
- 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
--------
-- Always use HTTPS with a valid certificate
-- If you make a mobile application, it's your job to store securely the user credentials on the device
+- Always use HTTPS with a valid certificate (avoid clear text communication)
+- 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
- Two factor authentication is not yet available through the API
diff --git a/doc/api-project-permission-procedures.markdown b/doc/api-project-permission-procedures.markdown
index 2844ae3c..d5e9b066 100644
--- a/doc/api-project-permission-procedures.markdown
+++ b/doc/api-project-permission-procedures.markdown
@@ -272,3 +272,36 @@ Response example:
"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"
+}
+```
diff --git a/doc/api-project-procedures.markdown b/doc/api-project-procedures.markdown
index f8e2cc5e..3f8d33c2 100644
--- a/doc/api-project-procedures.markdown
+++ b/doc/api-project-procedures.markdown
@@ -7,6 +7,8 @@ API Project Procedures
- Parameters:
- **name** (string, required)
- **description** (string, optional)
+ - **owner_id** (integer, optional)
+ - **identifier** (string, optional)
- Result on success: **project_id**
- Result on failure: **false**
@@ -186,6 +188,8 @@ Response example:
- **project_id** (integer, required)
- **name** (string, required)
- **description** (string, optional)
+ - **owner_id** (integer, optional)
+ - **identifier** (string, optional)
- Result on success: **true**
- Result on failure: **false**
diff --git a/tests/integration/ActionTest.php b/tests/integration/ActionProcedureTest.php
index 7a5adc4a..432de3d3 100644
--- a/tests/integration/ActionTest.php
+++ b/tests/integration/ActionProcedureTest.php
@@ -1,8 +1,8 @@
<?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';
diff --git a/tests/integration/AppTest.php b/tests/integration/AppProcedureTest.php
index 287e6299..06135dac 100644
--- a/tests/integration/AppTest.php
+++ b/tests/integration/AppProcedureTest.php
@@ -1,8 +1,8 @@
<?php
-require_once __DIR__.'/BaseIntegrationTest.php';
+require_once __DIR__.'/BaseProcedureTest.php';
-class AppTest extends BaseIntegrationTest
+class AppProcedureTest extends BaseProcedureTest
{
public function testGetTimezone()
{
diff --git a/tests/integration/BaseIntegrationTest.php b/tests/integration/BaseProcedureTest.php
index cd837173..e3382e82 100644
--- a/tests/integration/BaseIntegrationTest.php
+++ b/tests/integration/BaseProcedureTest.php
@@ -2,7 +2,7 @@
require_once __DIR__.'/../../vendor/autoload.php';
-abstract class BaseIntegrationTest extends PHPUnit_Framework_TestCase
+abstract class BaseProcedureTest extends PHPUnit_Framework_TestCase
{
protected $app = null;
protected $admin = null;
diff --git a/tests/integration/BoardTest.php b/tests/integration/BoardProcedureTest.php
index aa0f61ff..273e93c7 100644
--- a/tests/integration/BoardTest.php
+++ b/tests/integration/BoardProcedureTest.php
@@ -1,8 +1,8 @@
<?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';
diff --git a/tests/integration/CategoryTest.php b/tests/integration/CategoryProcedureTest.php
index c1aec0bc..2f5294ba 100644
--- a/tests/integration/CategoryTest.php
+++ b/tests/integration/CategoryProcedureTest.php
@@ -1,8 +1,8 @@
<?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';
private $categoryId = 0;
diff --git a/tests/integration/ColumnTest.php b/tests/integration/ColumnProcedureTest.php
index 5a81badc..fb6a27c3 100644
--- a/tests/integration/ColumnTest.php
+++ b/tests/integration/ColumnProcedureTest.php
@@ -1,8 +1,8 @@
<?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';
private $columns = array();
diff --git a/tests/integration/CommentTest.php b/tests/integration/CommentProcedureTest.php
index 34376838..881d938c 100644
--- a/tests/integration/CommentTest.php
+++ b/tests/integration/CommentProcedureTest.php
@@ -1,8 +1,8 @@
<?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';
private $commentId = 0;
diff --git a/tests/integration/GroupMemberTest.php b/tests/integration/GroupMemberProcedureTest.php
index f79499a4..fe243533 100644
--- a/tests/integration/GroupMemberTest.php
+++ b/tests/integration/GroupMemberProcedureTest.php
@@ -1,8 +1,8 @@
<?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 $groupName1 = 'My group member A';
diff --git a/tests/integration/GroupTest.php b/tests/integration/GroupProcedureTest.php
index ffcd7a71..610c121d 100644
--- a/tests/integration/GroupTest.php
+++ b/tests/integration/GroupProcedureTest.php
@@ -1,8 +1,8 @@
<?php
-require_once __DIR__.'/BaseIntegrationTest.php';
+require_once __DIR__.'/BaseProcedureTest.php';
-class GroupTest extends BaseIntegrationTest
+class GroupProcedureTest extends BaseProcedureTest
{
public function testAll()
{
diff --git a/tests/integration/LinkTest.php b/tests/integration/LinkProcedureTest.php
index 16b16e50..fb07e694 100644
--- a/tests/integration/LinkTest.php
+++ b/tests/integration/LinkProcedureTest.php
@@ -1,8 +1,8 @@
<?php
-require_once __DIR__.'/BaseIntegrationTest.php';
+require_once __DIR__.'/BaseProcedureTest.php';
-class LinkTest extends BaseIntegrationTest
+class LinkProcedureTest extends BaseProcedureTest
{
public function testGetAllLinks()
{
diff --git a/tests/integration/MeTest.php b/tests/integration/MeProcedureTest.php
index 047ebf85..2106419c 100644
--- a/tests/integration/MeTest.php
+++ b/tests/integration/MeProcedureTest.php
@@ -1,8 +1,8 @@
<?php
-require_once __DIR__.'/BaseIntegrationTest.php';
+require_once __DIR__.'/BaseProcedureTest.php';
-class MeTest extends BaseIntegrationTest
+class MeProcedureTest extends BaseProcedureTest
{
protected $projectName = 'My private project';
@@ -41,11 +41,6 @@ class MeTest extends BaseIntegrationTest
{
$projects = $this->user->getMyProjects();
$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()
diff --git a/tests/integration/OverdueTaskTest.php b/tests/integration/OverdueTaskProcedureTest.php
index 1dea5030..65f52301 100644
--- a/tests/integration/OverdueTaskTest.php
+++ b/tests/integration/OverdueTaskProcedureTest.php
@@ -1,8 +1,8 @@
<?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';
diff --git a/tests/integration/ProcedureAuthorizationTest.php b/tests/integration/ProcedureAuthorizationTest.php
new file mode 100644
index 00000000..a63e9d8c
--- /dev/null
+++ b/tests/integration/ProcedureAuthorizationTest.php
@@ -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));
+ }
+}
diff --git a/tests/integration/ProjectPermissionTest.php b/tests/integration/ProjectPermissionProcedureTest.php
index 3ceda07d..74313dc4 100644
--- a/tests/integration/ProjectPermissionTest.php
+++ b/tests/integration/ProjectPermissionProcedureTest.php
@@ -1,8 +1,8 @@
<?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 $username = 'user-project-permission';
diff --git a/tests/integration/ProjectTest.php b/tests/integration/ProjectProcedureTest.php
index 50d4fc53..1ebd48ae 100644
--- a/tests/integration/ProjectTest.php
+++ b/tests/integration/ProjectProcedureTest.php
@@ -1,8 +1,8 @@
<?php
-require_once __DIR__.'/BaseIntegrationTest.php';
+require_once __DIR__.'/BaseProcedureTest.php';
-class ProjectTest extends BaseIntegrationTest
+class ProjectProcedureTest extends BaseProcedureTest
{
protected $projectName = 'My team project';
diff --git a/tests/integration/SubtaskTest.php b/tests/integration/SubtaskProcedureTest.php
index 10082e60..7ab4ef0b 100644
--- a/tests/integration/SubtaskTest.php
+++ b/tests/integration/SubtaskProcedureTest.php
@@ -1,8 +1,8 @@
<?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';
private $subtaskId = 0;
diff --git a/tests/integration/SwimlaneTest.php b/tests/integration/SwimlaneProcedureTest.php
index 4f703414..e64342b4 100644
--- a/tests/integration/SwimlaneTest.php
+++ b/tests/integration/SwimlaneProcedureTest.php
@@ -1,8 +1,8 @@
<?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';
private $swimlaneId = 0;
diff --git a/tests/integration/TaskFileTest.php b/tests/integration/TaskFileProcedureTest.php
index 7e9e943b..61155555 100644
--- a/tests/integration/TaskFileTest.php
+++ b/tests/integration/TaskFileProcedureTest.php
@@ -1,8 +1,8 @@
<?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 $fileId;
diff --git a/tests/integration/TaskLinkTest.php b/tests/integration/TaskLinkProcedureTest.php
index 03bc437b..a25fced5 100644
--- a/tests/integration/TaskLinkTest.php
+++ b/tests/integration/TaskLinkProcedureTest.php
@@ -1,8 +1,8 @@
<?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 $taskLinkId;
diff --git a/tests/integration/TaskTest.php b/tests/integration/TaskProcedureTest.php
index 6f1d9d62..f456ae52 100644
--- a/tests/integration/TaskTest.php
+++ b/tests/integration/TaskProcedureTest.php
@@ -1,8 +1,8 @@
<?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';
diff --git a/tests/integration/UserTest.php b/tests/integration/UserProcedureTest.php
index c407c918..290f87fb 100644
--- a/tests/integration/UserTest.php
+++ b/tests/integration/UserProcedureTest.php
@@ -1,8 +1,8 @@
<?php
-require_once __DIR__.'/BaseIntegrationTest.php';
+require_once __DIR__.'/BaseProcedureTest.php';
-class UserTest extends BaseIntegrationTest
+class UserProcedureTest extends BaseProcedureTest
{
public function testAll()
{
diff --git a/tests/units/Model/ActionTest.php b/tests/units/Model/ActionModelTest.php
index 5db18983..4e21a999 100644
--- a/tests/units/Model/ActionTest.php
+++ b/tests/units/Model/ActionModelTest.php
@@ -11,7 +11,7 @@ use Kanboard\Model\CategoryModel;
use Kanboard\Model\ProjectUserRoleModel;
use Kanboard\Core\Security\Role;
-class ActionTest extends Base
+class ActionModelTest extends Base
{
public function testCreate()
{
@@ -69,6 +69,24 @@ class ActionTest extends Base
$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()
{
$projectModel = new ProjectModel($this->container);
diff --git a/tests/units/Model/CategoryTest.php b/tests/units/Model/CategoryModelTest.php
index 1fdc51f6..80a20af6 100644
--- a/tests/units/Model/CategoryTest.php
+++ b/tests/units/Model/CategoryModelTest.php
@@ -8,7 +8,7 @@ use Kanboard\Model\TaskFinderModel;
use Kanboard\Model\ProjectModel;
use Kanboard\Model\CategoryModel;
-class CategoryTest extends Base
+class CategoryModelTest extends Base
{
public function testCreation()
{
@@ -81,6 +81,18 @@ class CategoryTest extends Base
$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()
{
$projectModel = new ProjectModel($this->container);
diff --git a/tests/units/Model/CommentTest.php b/tests/units/Model/CommentTest.php
index 7250ae0b..574b5a87 100644
--- a/tests/units/Model/CommentTest.php
+++ b/tests/units/Model/CommentTest.php
@@ -10,16 +10,16 @@ class CommentTest extends Base
{
public function testCreate()
{
- $c = new CommentModel($this->container);
- $tc = new TaskCreationModel($this->container);
- $p = new ProjectModel($this->container);
+ $commentModel = new CommentModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $projectModel = new ProjectModel($this->container);
- $this->assertEquals(1, $p->create(array('name' => 'test1')));
- $this->assertEquals(1, $tc->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(2, $c->create(array('task_id' => 1, 'comment' => 'bla bla')));
+ $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' => 'bla bla', 'user_id' => 1)));
+ $this->assertEquals(2, $commentModel->create(array('task_id' => 1, 'comment' => 'bla bla')));
- $comment = $c->getById(1);
+ $comment = $commentModel->getById(1);
$this->assertNotEmpty($comment);
$this->assertEquals('bla bla', $comment['comment']);
$this->assertEquals(1, $comment['task_id']);
@@ -27,7 +27,7 @@ class CommentTest extends Base
$this->assertEquals('admin', $comment['username']);
$this->assertEquals(time(), $comment['date_creation'], '', 3);
- $comment = $c->getById(2);
+ $comment = $commentModel->getById(2);
$this->assertNotEmpty($comment);
$this->assertEquals('bla bla', $comment['comment']);
$this->assertEquals(1, $comment['task_id']);
@@ -38,17 +38,17 @@ class CommentTest extends Base
public function testGetAll()
{
- $c = new CommentModel($this->container);
- $tc = new TaskCreationModel($this->container);
- $p = new ProjectModel($this->container);
+ $commentModel = new CommentModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $projectModel = new ProjectModel($this->container);
- $this->assertEquals(1, $p->create(array('name' => 'test1')));
- $this->assertEquals(1, $tc->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->assertNotFalse($c->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(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(2, $commentModel->create(array('task_id' => 1, 'comment' => 'c2', '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->assertEquals(3, count($comments));
@@ -56,37 +56,51 @@ class CommentTest extends Base
$this->assertEquals(2, $comments[1]['id']);
$this->assertEquals(3, $comments[2]['id']);
- $this->assertEquals(3, $c->count(1));
+ $this->assertEquals(3, $commentModel->count(1));
}
public function testUpdate()
{
- $c = new CommentModel($this->container);
- $tc = new TaskCreationModel($this->container);
- $p = new ProjectModel($this->container);
+ $commentModel = new CommentModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $projectModel = new ProjectModel($this->container);
- $this->assertEquals(1, $p->create(array('name' => 'test1')));
- $this->assertEquals(1, $tc->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->assertTrue($c->update(array('id' => 1, 'comment' => 'bla')));
+ $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->assertTrue($commentModel->update(array('id' => 1, 'comment' => 'bla')));
- $comment = $c->getById(1);
+ $comment = $commentModel->getById(1);
$this->assertNotEmpty($comment);
$this->assertEquals('bla', $comment['comment']);
}
public function validateRemove()
{
- $c = new CommentModel($this->container);
- $tc = new TaskCreationModel($this->container);
- $p = new ProjectModel($this->container);
+ $commentModel = new CommentModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $projectModel = new ProjectModel($this->container);
- $this->assertEquals(1, $p->create(array('name' => 'test1')));
- $this->assertEquals(1, $tc->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, $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->assertTrue($c->remove(1));
- $this->assertFalse($c->remove(1));
- $this->assertFalse($c->remove(1111));
+ $this->assertTrue($commentModel->remove(1));
+ $this->assertFalse($commentModel->remove(1));
+ $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));
}
}
diff --git a/tests/units/Model/SubtaskTest.php b/tests/units/Model/SubtaskModelTest.php
index b65ee609..6451189d 100644
--- a/tests/units/Model/SubtaskTest.php
+++ b/tests/units/Model/SubtaskModelTest.php
@@ -5,10 +5,9 @@ require_once __DIR__.'/../Base.php';
use Kanboard\Model\TaskCreationModel;
use Kanboard\Model\SubtaskModel;
use Kanboard\Model\ProjectModel;
-use Kanboard\Core\User\UserSession;
use Kanboard\Model\TaskFinderModel;
-class SubtaskTest extends Base
+class SubtaskModelTest extends Base
{
public function onSubtaskCreated($event)
{
@@ -70,18 +69,18 @@ class SubtaskTest extends Base
public function testCreation()
{
- $tc = new TaskCreationModel($this->container);
- $s = new SubtaskModel($this->container);
- $p = new ProjectModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $subtaskModel = new SubtaskModel($this->container);
+ $projectModel = new ProjectModel($this->container);
- $this->assertEquals(1, $p->create(array('name' => 'test')));
- $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1)));
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test')));
+ $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1)));
$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->assertEquals(1, $subtask['id']);
$this->assertEquals(1, $subtask['task_id']);
@@ -95,19 +94,19 @@ class SubtaskTest extends Base
public function testModification()
{
- $tc = new TaskCreationModel($this->container);
- $s = new SubtaskModel($this->container);
- $p = new ProjectModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $subtaskModel = new SubtaskModel($this->container);
+ $projectModel = new ProjectModel($this->container);
- $this->assertEquals(1, $p->create(array('name' => 'test')));
- $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1)));
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test')));
+ $this->assertEquals(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1)));
$this->container['dispatcher']->addListener(SubtaskModel::EVENT_UPDATE, array($this, 'onSubtaskUpdated'));
- $this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1)));
- $this->assertTrue($s->update(array('id' => 1, 'user_id' => 1, 'status' => SubtaskModel::STATUS_INPROGRESS)));
+ $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', 'task_id' => 1)));
+ $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->assertEquals(1, $subtask['id']);
$this->assertEquals(1, $subtask['task_id']);
@@ -121,61 +120,61 @@ class SubtaskTest extends Base
public function testRemove()
{
- $tc = new TaskCreationModel($this->container);
- $s = new SubtaskModel($this->container);
- $p = new ProjectModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $subtaskModel = new SubtaskModel($this->container);
+ $projectModel = new ProjectModel($this->container);
- $this->assertEquals(1, $p->create(array('name' => 'test')));
- $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1)));
- $this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1)));
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test')));
+ $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->container['dispatcher']->addListener(SubtaskModel::EVENT_DELETE, array($this, 'onSubtaskDeleted'));
- $subtask = $s->getById(1);
+ $subtask = $subtaskModel->getById(1);
$this->assertNotEmpty($subtask);
- $this->assertTrue($s->remove(1));
+ $this->assertTrue($subtaskModel->remove(1));
- $subtask = $s->getById(1);
+ $subtask = $subtaskModel->getById(1);
$this->assertEmpty($subtask);
}
public function testToggleStatusWithoutSession()
{
- $tc = new TaskCreationModel($this->container);
- $s = new SubtaskModel($this->container);
- $p = new ProjectModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $subtaskModel = new SubtaskModel($this->container);
+ $projectModel = new ProjectModel($this->container);
- $this->assertEquals(1, $p->create(array('name' => 'test1')));
- $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1)));
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
+ $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->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']);
$this->assertEquals(0, $subtask['user_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->assertEquals(SubtaskModel::STATUS_INPROGRESS, $subtask['status']);
$this->assertEquals(0, $subtask['user_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->assertEquals(SubtaskModel::STATUS_DONE, $subtask['status']);
$this->assertEquals(0, $subtask['user_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->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']);
$this->assertEquals(0, $subtask['user_id']);
@@ -184,17 +183,16 @@ class SubtaskTest extends Base
public function testToggleStatusWithSession()
{
- $tc = new TaskCreationModel($this->container);
- $s = new SubtaskModel($this->container);
- $p = new ProjectModel($this->container);
- $us = new UserSession($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $subtaskModel = new SubtaskModel($this->container);
+ $projectModel = new ProjectModel($this->container);
- $this->assertEquals(1, $p->create(array('name' => 'test1')));
- $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1)));
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
+ $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->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']);
$this->assertEquals(0, $subtask['user_id']);
@@ -203,25 +201,25 @@ class SubtaskTest extends Base
// Set the current logged user
$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->assertEquals(SubtaskModel::STATUS_INPROGRESS, $subtask['status']);
$this->assertEquals(1, $subtask['user_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->assertEquals(SubtaskModel::STATUS_DONE, $subtask['status']);
$this->assertEquals(1, $subtask['user_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->assertEquals(SubtaskModel::STATUS_TODO, $subtask['status']);
$this->assertEquals(1, $subtask['user_id']);
@@ -230,19 +228,19 @@ class SubtaskTest extends Base
public function testCloseAll()
{
- $tc = new TaskCreationModel($this->container);
- $s = new SubtaskModel($this->container);
- $p = new ProjectModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $subtaskModel = new SubtaskModel($this->container);
+ $projectModel = new ProjectModel($this->container);
- $this->assertEquals(1, $p->create(array('name' => 'test1')));
- $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1)));
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
+ $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(2, $s->create(array('title' => 'subtask #2', 'task_id' => 1)));
+ $this->assertEquals(1, $subtaskModel->create(array('title' => 'subtask #1', '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);
foreach ($subtasks as $subtask) {
@@ -252,24 +250,24 @@ class SubtaskTest extends Base
public function testDuplicate()
{
- $tc = new TaskCreationModel($this->container);
- $s = new SubtaskModel($this->container);
- $p = new ProjectModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $subtaskModel = new SubtaskModel($this->container);
+ $projectModel = new ProjectModel($this->container);
// We create a project
- $this->assertEquals(1, $p->create(array('name' => 'test1')));
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
// We create 2 tasks
- $this->assertEquals(1, $tc->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(1, $taskCreationModel->create(array('title' => 'test 1', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 1)));
+ $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
- $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(2, $s->create(array('title' => 'subtask #2', 'task_id' => 1, 'time_estimated' => '', 'time_spent' => '', 'status' => 2, 'user_id' => 1)));
+ $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, $subtaskModel->create(array('title' => 'subtask #2', 'task_id' => 1, 'time_estimated' => '', 'time_spent' => '', 'status' => 2, 'user_id' => 1)));
// We duplicate our subtasks
- $this->assertTrue($s->duplicate(1, 2));
- $subtasks = $s->getAll(2);
+ $this->assertTrue($subtaskModel->duplicate(1, 2));
+ $subtasks = $subtaskModel->getAll(2);
$this->assertNotFalse($subtasks);
$this->assertNotEmpty($subtasks);
@@ -385,4 +383,18 @@ class SubtaskTest extends Base
$this->assertEquals(2, $task['time_spent']);
$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));
+ }
}
diff --git a/tests/units/Model/TaskFileTest.php b/tests/units/Model/TaskFileModelTest.php
index 2faee95c..de12553f 100644
--- a/tests/units/Model/TaskFileTest.php
+++ b/tests/units/Model/TaskFileModelTest.php
@@ -6,7 +6,7 @@ use Kanboard\Model\TaskFileModel;
use Kanboard\Model\TaskCreationModel;
use Kanboard\Model\ProjectModel;
-class TaskFileTest extends Base
+class TaskFileModelTest extends Base
{
public function testCreation()
{
@@ -442,4 +442,17 @@ class TaskFileTest extends Base
$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));
+ }
}
diff --git a/tests/units/Model/TaskLinkModelTest.php b/tests/units/Model/TaskLinkModelTest.php
new file mode 100644
index 00000000..78590891
--- /dev/null
+++ b/tests/units/Model/TaskLinkModelTest.php
@@ -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));
+ }
+}
diff --git a/tests/units/Model/TaskLinkTest.php b/tests/units/Model/TaskLinkTest.php
deleted file mode 100644
index bc574731..00000000
--- a/tests/units/Model/TaskLinkTest.php
+++ /dev/null
@@ -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);
- }
-}