diff options
Diffstat (limited to 'app/Controller')
32 files changed, 686 insertions, 520 deletions
diff --git a/app/Controller/Action.php b/app/Controller/Action.php index ad136067..3caea45c 100644 --- a/app/Controller/Action.php +++ b/app/Controller/Action.php @@ -27,7 +27,7 @@ class Action extends Base 'available_events' => $this->action->getAvailableEvents(), 'available_params' => $this->action->getAllActionParameters(), 'columns_list' => $this->board->getColumnsList($project['id']), - 'users_list' => $this->projectPermission->getMemberList($project['id']), + 'users_list' => $this->projectUserRole->getAssignableUsersList($project['id']), 'projects_list' => $this->project->getList(false), 'colors_list' => $this->color->getList(), 'categories_list' => $this->category->getList($project['id']), @@ -86,7 +86,7 @@ class Action extends Base 'values' => $values, 'action_params' => $action_params, 'columns_list' => $this->board->getColumnsList($project['id']), - 'users_list' => $this->projectPermission->getMemberList($project['id']), + 'users_list' => $this->projectUserRole->getAssignableUsersList($project['id']), 'projects_list' => $projects_list, 'colors_list' => $this->color->getList(), 'categories_list' => $this->category->getList($project['id']), diff --git a/app/Controller/Activity.php b/app/Controller/Activity.php index 24327c23..71d5e94f 100644 --- a/app/Controller/Activity.php +++ b/app/Controller/Activity.php @@ -20,7 +20,7 @@ class Activity extends Base $project = $this->getProject(); $this->response->html($this->template->layout('activity/project', array( - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), + 'board_selector' => $this->projectUserRole->getProjectsByUser($this->userSession->getId()), 'events' => $this->projectActivity->getProject($project['id']), 'project' => $project, 'title' => t('%s\'s activity', $project['name']) diff --git a/app/Controller/Analytic.php b/app/Controller/Analytic.php index 1082b462..e03d8cab 100644 --- a/app/Controller/Analytic.php +++ b/app/Controller/Analytic.php @@ -20,7 +20,7 @@ class Analytic extends Base */ private function layout($template, array $params) { - $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId()); + $params['board_selector'] = $this->projectUserRole->getProjectsByUser($this->userSession->getId()); $params['content_for_sublayout'] = $this->template->render($template, $params); return $this->template->layout('analytic/layout', $params); @@ -132,6 +132,9 @@ class Analytic extends Base * Common method for CFD and Burdown chart * * @access private + * @param string $template + * @param string $column + * @param string $title */ private function commonAggregateMetrics($template, $column, $title) { diff --git a/app/Controller/App.php b/app/Controller/App.php index 2fae004c..c596b4a8 100644 --- a/app/Controller/App.php +++ b/app/Controller/App.php @@ -22,7 +22,7 @@ class App extends Base */ private function layout($template, array $params) { - $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId()); + $params['board_selector'] = $this->projectUserRole->getProjectsByUser($this->userSession->getId()); $params['content_for_sublayout'] = $this->template->render($template, $params); return $this->template->layout('app/layout', $params); @@ -42,7 +42,7 @@ class App extends Base ->setUrl('app', $action, array('pagination' => 'projects', 'user_id' => $user_id)) ->setMax($max) ->setOrder('name') - ->setQuery($this->project->getQueryColumnStats($this->projectPermission->getActiveMemberProjectIds($user_id))) + ->setQuery($this->project->getQueryColumnStats($this->projectPermission->getActiveProjectIds($user_id))) ->calculateOnlyIf($this->request->getStringParam('pagination') === 'projects'); } @@ -169,7 +169,7 @@ class App extends Base $this->response->html($this->layout('app/activity', array( 'title' => t('My activity stream'), - 'events' => $this->projectActivity->getProjects($this->projectPermission->getActiveMemberProjectIds($user['id']), 100), + 'events' => $this->projectActivity->getProjects($this->projectPermission->getActiveProjectIds($user['id']), 100), 'user' => $user, ))); } @@ -202,49 +202,4 @@ class App extends Base 'user' => $user, ))); } - - /** - * Render Markdown text and reply with the HTML Code - * - * @access public - */ - public function preview() - { - $payload = $this->request->getJson(); - - if (empty($payload['text'])) { - $this->response->html('<p>'.t('Nothing to preview...').'</p>'); - } - - $this->response->html($this->helper->text->markdown($payload['text'])); - } - - /** - * Task autocompletion (Ajax) - * - * @access public - */ - public function autocomplete() - { - $search = $this->request->getStringParam('term'); - $projects = $this->projectPermission->getActiveMemberProjectIds($this->userSession->getId()); - - if (empty($projects)) { - $this->response->json(array()); - } - - $filter = $this->taskFilterAutoCompleteFormatter - ->create() - ->filterByProjects($projects) - ->excludeTasks(array($this->request->getIntegerParam('exclude_task_id'))); - - // Search by task id or by title - if (ctype_digit($search)) { - $filter->filterById($search); - } else { - $filter->filterByTitle($search); - } - - $this->response->json($filter->format()); - } } diff --git a/app/Controller/Auth.php b/app/Controller/Auth.php index b90e756d..cd1dd167 100644 --- a/app/Controller/Auth.php +++ b/app/Controller/Auth.php @@ -24,7 +24,7 @@ class Auth extends Base } $this->response->html($this->template->layout('auth/index', array( - 'captcha' => isset($values['username']) && $this->authentication->hasCaptcha($values['username']), + 'captcha' => ! empty($values['username']) && $this->userLocking->hasCaptcha($values['username']), 'errors' => $errors, 'values' => $values, 'no_layout' => true, @@ -40,18 +40,11 @@ class Auth extends Base public function check() { $values = $this->request->getValues(); + $this->sessionStorage->hasRememberMe = ! empty($values['remember_me']); list($valid, $errors) = $this->authentication->validateForm($values); if ($valid) { - if (isset($this->sessionStorage->redirectAfterLogin) - && ! empty($this->sessionStorage->redirectAfterLogin) - && ! filter_var($this->sessionStorage->redirectAfterLogin, FILTER_VALIDATE_URL)) { - $redirect = $this->sessionStorage->redirectAfterLogin; - unset($this->sessionStorage->redirectAfterLogin); - $this->response->redirect($redirect); - } - - $this->response->redirect($this->helper->url->to('app', 'index')); + $this->redirectAfterLogin(); } $this->login($values, $errors); @@ -64,7 +57,6 @@ class Auth extends Base */ public function logout() { - $this->authentication->backend('rememberMe')->destroy($this->userSession->getId()); $this->sessionManager->close(); $this->response->redirect($this->helper->url->to('auth', 'login')); } @@ -83,4 +75,20 @@ class Auth extends Base $this->sessionStorage->captcha = $builder->getPhrase(); $builder->output(); } + + /** + * Redirect the user after the authentication + * + * @access private + */ + private function redirectAfterLogin() + { + if (isset($this->sessionStorage->redirectAfterLogin) && ! empty($this->sessionStorage->redirectAfterLogin) && ! filter_var($this->sessionStorage->redirectAfterLogin, FILTER_VALIDATE_URL)) { + $redirect = $this->sessionStorage->redirectAfterLogin; + unset($this->sessionStorage->redirectAfterLogin); + $this->response->redirect($redirect); + } + + $this->response->redirect($this->helper->url->to('app', 'index')); + } } diff --git a/app/Controller/Base.php b/app/Controller/Base.php index 8630f00c..76948a0f 100644 --- a/app/Controller/Base.php +++ b/app/Controller/Base.php @@ -3,7 +3,7 @@ namespace Kanboard\Controller; use Pimple\Container; -use Symfony\Component\EventDispatcher\Event; +use Kanboard\Core\Security\Role; /** * Base controller @@ -14,36 +14,22 @@ use Symfony\Component\EventDispatcher\Event; abstract class Base extends \Kanboard\Core\Base { /** - * Constructor - * - * @access public - * @param \Pimple\Container $container - */ - public function __construct(Container $container) - { - $this->container = $container; - - if (DEBUG) { - $this->logger->debug('START_REQUEST='.$_SERVER['REQUEST_URI']); - } - } - - /** - * Destructor + * Method executed before each action * * @access public */ - public function __destruct() + public function beforeAction($controller, $action) { - if (DEBUG) { - foreach ($this->db->getLogMessages() as $message) { - $this->logger->debug($message); - } + $this->sessionManager->open(); + $this->dispatcher->dispatch('app.bootstrap'); + $this->sendHeaders($action); + $this->authenticationManager->checkCurrentSession(); - $this->logger->debug('SQL_QUERIES={nb}', array('nb' => $this->container['db']->nbQueries)); - $this->logger->debug('RENDERING={time}', array('time' => microtime(true) - @$_SERVER['REQUEST_TIME_FLOAT'])); - $this->logger->debug('MEMORY='.$this->helper->text->bytes(memory_get_usage())); - $this->logger->debug('END_REQUEST='.$_SERVER['REQUEST_URI']); + if (! $this->applicationAuthorization->isAllowed($controller, $action, Role::APP_PUBLIC)) { + $this->handleAuthentication(); + $this->handlePostAuthentication($controller, $action); + $this->checkApplicationAuthorization($controller, $action); + $this->checkProjectAuthorization($controller, $action); } } @@ -70,33 +56,13 @@ abstract class Base extends \Kanboard\Core\Base } /** - * Method executed before each action - * - * @access public - */ - public function beforeAction($controller, $action) - { - $this->sessionManager->open(); - $this->sendHeaders($action); - $this->container['dispatcher']->dispatch('session.bootstrap', new Event); - - if (! $this->acl->isPublicAction($controller, $action)) { - $this->handleAuthentication(); - $this->handle2FA($controller, $action); - $this->handleAuthorization($controller, $action); - - $this->sessionStorage->hasSubtaskInProgress = $this->subtask->hasSubtaskInProgress($this->userSession->getId()); - } - } - - /** * Check authentication * - * @access public + * @access private */ - public function handleAuthentication() + private function handleAuthentication() { - if (! $this->authentication->isAuthenticated()) { + if (! $this->userSession->isLogged() && ! $this->authenticationManager->preAuthentication()) { if ($this->request->isAjax()) { $this->response->text('Not Authorized', 401); } @@ -107,15 +73,15 @@ abstract class Base extends \Kanboard\Core\Base } /** - * Check 2FA + * Handle Post-Authentication (2FA) * - * @access public + * @access private */ - public function handle2FA($controller, $action) + private function handlePostAuthentication($controller, $action) { $ignore = ($controller === 'twofactor' && in_array($action, array('code', 'check'))) || ($controller === 'auth' && $action === 'logout'); - if ($ignore === false && $this->userSession->has2FA() && ! $this->userSession->check2FA()) { + if ($ignore === false && $this->userSession->hasPostAuthentication() && ! $this->userSession->isPostAuthenticationValidated()) { if ($this->request->isAjax()) { $this->response->text('Not Authorized', 401); } @@ -125,11 +91,23 @@ abstract class Base extends \Kanboard\Core\Base } /** - * Check page access and authorization + * Check application authorization * - * @access public + * @access private + */ + private function checkApplicationAuthorization($controller, $action) + { + if (! $this->helper->user->hasAccess($controller, $action)) { + $this->forbidden(); + } + } + + /** + * Check project authorization + * + * @access private */ - public function handleAuthorization($controller, $action) + private function checkProjectAuthorization($controller, $action) { $project_id = $this->request->getIntegerParam('project_id'); $task_id = $this->request->getIntegerParam('task_id'); @@ -139,7 +117,7 @@ abstract class Base extends \Kanboard\Core\Base $project_id = $this->taskFinder->getProjectId($task_id); } - if (! $this->acl->isAllowed($controller, $action, $project_id)) { + if ($project_id > 0 && ! $this->helper->user->hasProjectAccess($controller, $action, $project_id)) { $this->forbidden(); } } @@ -147,10 +125,10 @@ abstract class Base extends \Kanboard\Core\Base /** * Application not found page (404 error) * - * @access public + * @access protected * @param boolean $no_layout Display the layout or not */ - public function notfound($no_layout = false) + protected function notfound($no_layout = false) { $this->response->html($this->template->layout('app/notfound', array( 'title' => t('Page not found'), @@ -161,11 +139,15 @@ abstract class Base extends \Kanboard\Core\Base /** * Application forbidden page * - * @access public + * @access protected * @param boolean $no_layout Display the layout or not */ - public function forbidden($no_layout = false) + protected function forbidden($no_layout = false) { + if ($this->request->isAjax()) { + $this->response->text('Not Authorized', 401); + } + $this->response->html($this->template->layout('app/forbidden', array( 'title' => t('Access Forbidden'), 'no_layout' => $no_layout, @@ -209,7 +191,7 @@ abstract class Base extends \Kanboard\Core\Base $content = $this->template->render($template, $params); $params['task_content_for_layout'] = $content; $params['title'] = $params['task']['project_name'].' > '.$params['task']['title']; - $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId()); + $params['board_selector'] = $this->projectUserRole->getProjectsByUser($this->userSession->getId()); return $this->template->layout('task/layout', $params); } @@ -227,7 +209,7 @@ abstract class Base extends \Kanboard\Core\Base $content = $this->template->render($template, $params); $params['project_content_for_layout'] = $content; $params['title'] = $params['project']['name'] === $params['title'] ? $params['title'] : $params['project']['name'].' > '.$params['title']; - $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId()); + $params['board_selector'] = $this->projectUserRole->getProjectsByUser($this->userSession->getId()); $params['sidebar_template'] = $sidebar_template; return $this->template->layout('project/layout', $params); @@ -300,12 +282,15 @@ abstract class Base extends \Kanboard\Core\Base * Common method to get project filters * * @access protected + * @param string $controller + * @param string $action + * @return array */ protected function getProjectFilters($controller, $action) { $project = $this->getProject(); $search = $this->request->getStringParam('search', $this->userSession->getFilters($project['id'])); - $board_selector = $this->projectPermission->getAllowedProjects($this->userSession->getId()); + $board_selector = $this->projectUserRole->getProjectsByUser($this->userSession->getId()); unset($board_selector[$project['id']]); $filters = array( diff --git a/app/Controller/Board.php b/app/Controller/Board.php index 7442ff22..a75fea33 100644 --- a/app/Controller/Board.php +++ b/app/Controller/Board.php @@ -51,7 +51,7 @@ class Board extends Base $this->response->html($this->template->layout('board/view_private', array( 'categories_list' => $this->category->getList($params['project']['id'], false), - 'users_list' => $this->projectPermission->getMemberList($params['project']['id'], false), + 'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false), 'custom_filters_list' => $this->customFilter->getAll($params['project']['id'], $this->userSession->getId()), 'swimlanes' => $this->taskFilter->search($params['filters']['search'])->getBoard($params['project']['id']), 'description' => $params['project']['description'], @@ -143,195 +143,6 @@ class Board extends Base } /** - * Get links on mouseover - * - * @access public - */ - public function tasklinks() - { - $task = $this->getTask(); - $this->response->html($this->template->render('board/tooltip_tasklinks', array( - 'links' => $this->taskLink->getAll($task['id']), - 'task' => $task, - ))); - } - - /** - * Get subtasks on mouseover - * - * @access public - */ - public function subtasks() - { - $task = $this->getTask(); - $this->response->html($this->template->render('board/tooltip_subtasks', array( - 'subtasks' => $this->subtask->getAll($task['id']), - 'task' => $task, - ))); - } - - /** - * Display all attachments during the task mouseover - * - * @access public - */ - public function attachments() - { - $task = $this->getTask(); - - $this->response->html($this->template->render('board/tooltip_files', array( - 'files' => $this->file->getAll($task['id']), - 'task' => $task, - ))); - } - - /** - * Display comments during a task mouseover - * - * @access public - */ - public function comments() - { - $task = $this->getTask(); - - $this->response->html($this->template->render('board/tooltip_comments', array( - 'comments' => $this->comment->getAll($task['id'], $this->userSession->getCommentSorting()) - ))); - } - - /** - * Display task description - * - * @access public - */ - public function description() - { - $task = $this->getTask(); - - $this->response->html($this->template->render('board/tooltip_description', array( - 'task' => $task - ))); - } - - /** - * Change a task assignee directly from the board - * - * @access public - */ - public function changeAssignee() - { - $task = $this->getTask(); - $project = $this->project->getById($task['project_id']); - - $this->response->html($this->template->render('board/popover_assignee', array( - 'values' => $task, - 'users_list' => $this->projectPermission->getMemberList($project['id']), - 'project' => $project, - ))); - } - - /** - * Validate an assignee modification - * - * @access public - */ - public function updateAssignee() - { - $values = $this->request->getValues(); - - list($valid, ) = $this->taskValidator->validateAssigneeModification($values); - - if ($valid && $this->taskModification->update($values)) { - $this->flash->success(t('Task updated successfully.')); - } else { - $this->flash->failure(t('Unable to update your task.')); - } - - $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $values['project_id']))); - } - - /** - * Change a task category directly from the board - * - * @access public - */ - public function changeCategory() - { - $task = $this->getTask(); - $project = $this->project->getById($task['project_id']); - - $this->response->html($this->template->render('board/popover_category', array( - 'values' => $task, - 'categories_list' => $this->category->getList($project['id']), - 'project' => $project, - ))); - } - - /** - * Validate a category modification - * - * @access public - */ - public function updateCategory() - { - $values = $this->request->getValues(); - - list($valid, ) = $this->taskValidator->validateCategoryModification($values); - - if ($valid && $this->taskModification->update($values)) { - $this->flash->success(t('Task updated successfully.')); - } else { - $this->flash->failure(t('Unable to update your task.')); - } - - $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $values['project_id']))); - } - - /** - * Screenshot popover - * - * @access public - */ - public function screenshot() - { - $task = $this->getTask(); - - $this->response->html($this->template->render('file/screenshot', array( - 'task' => $task, - 'redirect' => 'board', - ))); - } - - /** - * Get recurrence information on mouseover - * - * @access public - */ - public function recurrence() - { - $task = $this->getTask(); - - $this->response->html($this->template->render('task/recurring_info', array( - 'task' => $task, - 'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(), - 'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(), - 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(), - ))); - } - - /** - * Display swimlane description in tooltip - * - * @access public - */ - public function swimlane() - { - $this->getProject(); - $swimlane = $this->swimlane->getById($this->request->getIntegerParam('swimlane_id')); - $this->response->html($this->template->render('board/tooltip_description', array('task' => $swimlane))); - } - - /** * Enable collapsed mode * * @access public @@ -355,6 +166,7 @@ class Board extends Base * Change display mode * * @access private + * @param boolean $mode */ private function changeDisplayMode($mode) { @@ -372,6 +184,7 @@ class Board extends Base * Render board * * @access private + * @param integer $project_id */ private function renderBoard($project_id) { diff --git a/app/Controller/BoardPopover.php b/app/Controller/BoardPopover.php new file mode 100644 index 00000000..51ec9bc4 --- /dev/null +++ b/app/Controller/BoardPopover.php @@ -0,0 +1,101 @@ +<?php + +namespace Kanboard\Controller; + +/** + * Board Popover + * + * @package controller + * @author Frederic Guillot + */ +class BoardPopover extends Base +{ + /** + * Change a task assignee directly from the board + * + * @access public + */ + public function changeAssignee() + { + $task = $this->getTask(); + $project = $this->project->getById($task['project_id']); + + $this->response->html($this->template->render('board/popover_assignee', array( + 'values' => $task, + 'users_list' => $this->projectUserRole->getAssignableUsersList($project['id']), + 'project' => $project, + ))); + } + + /** + * Validate an assignee modification + * + * @access public + */ + public function updateAssignee() + { + $values = $this->request->getValues(); + + list($valid, ) = $this->taskValidator->validateAssigneeModification($values); + + if ($valid && $this->taskModification->update($values)) { + $this->flash->success(t('Task updated successfully.')); + } else { + $this->flash->failure(t('Unable to update your task.')); + } + + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $values['project_id']))); + } + + /** + * Change a task category directly from the board + * + * @access public + */ + public function changeCategory() + { + $task = $this->getTask(); + $project = $this->project->getById($task['project_id']); + + $this->response->html($this->template->render('board/popover_category', array( + 'values' => $task, + 'categories_list' => $this->category->getList($project['id']), + 'project' => $project, + ))); + } + + /** + * Validate a category modification + * + * @access public + */ + public function updateCategory() + { + $values = $this->request->getValues(); + + list($valid, ) = $this->taskValidator->validateCategoryModification($values); + + if ($valid && $this->taskModification->update($values)) { + $this->flash->success(t('Task updated successfully.')); + } else { + $this->flash->failure(t('Unable to update your task.')); + } + + $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $values['project_id']))); + } + + /** + * Screenshot popover + * + * @access public + */ + public function screenshot() + { + $task = $this->getTask(); + + $this->response->html($this->template->render('file/screenshot', array( + 'task' => $task, + 'redirect' => 'board', + ))); + } +} diff --git a/app/Controller/BoardTooltip.php b/app/Controller/BoardTooltip.php new file mode 100644 index 00000000..ed58a2f2 --- /dev/null +++ b/app/Controller/BoardTooltip.php @@ -0,0 +1,112 @@ +<?php + +namespace Kanboard\Controller; + +/** + * Board Tooltip + * + * @package controller + * @author Frederic Guillot + */ +class BoardTooltip extends Base +{ + /** + * Get links on mouseover + * + * @access public + */ + public function tasklinks() + { + $task = $this->getTask(); + $this->response->html($this->template->render('board/tooltip_tasklinks', array( + 'links' => $this->taskLink->getAll($task['id']), + 'task' => $task, + ))); + } + + /** + * Get subtasks on mouseover + * + * @access public + */ + public function subtasks() + { + $task = $this->getTask(); + $this->response->html($this->template->render('board/tooltip_subtasks', array( + 'subtasks' => $this->subtask->getAll($task['id']), + 'task' => $task, + ))); + } + + /** + * Display all attachments during the task mouseover + * + * @access public + */ + public function attachments() + { + $task = $this->getTask(); + + $this->response->html($this->template->render('board/tooltip_files', array( + 'files' => $this->file->getAll($task['id']), + 'task' => $task, + ))); + } + + /** + * Display comments during a task mouseover + * + * @access public + */ + public function comments() + { + $task = $this->getTask(); + + $this->response->html($this->template->render('board/tooltip_comments', array( + 'comments' => $this->comment->getAll($task['id'], $this->userSession->getCommentSorting()) + ))); + } + + /** + * Display task description + * + * @access public + */ + public function description() + { + $task = $this->getTask(); + + $this->response->html($this->template->render('board/tooltip_description', array( + 'task' => $task + ))); + } + + /** + * Get recurrence information on mouseover + * + * @access public + */ + public function recurrence() + { + $task = $this->getTask(); + + $this->response->html($this->template->render('task/recurring_info', array( + 'task' => $task, + 'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(), + 'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(), + 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(), + ))); + } + + /** + * Display swimlane description in tooltip + * + * @access public + */ + public function swimlane() + { + $this->getProject(); + $swimlane = $this->swimlane->getById($this->request->getIntegerParam('swimlane_id')); + $this->response->html($this->template->render('board/tooltip_description', array('task' => $swimlane))); + } +} diff --git a/app/Controller/Config.php b/app/Controller/Config.php index 49806144..c813c795 100644 --- a/app/Controller/Config.php +++ b/app/Controller/Config.php @@ -20,7 +20,7 @@ class Config extends Base */ private function layout($template, array $params) { - $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId()); + $params['board_selector'] = $this->projectUserRole->getProjectsByUser($this->userSession->getId()); $params['values'] = $this->config->getAll(); $params['errors'] = array(); $params['config_content_for_layout'] = $this->template->render($template, $params); diff --git a/app/Controller/Currency.php b/app/Controller/Currency.php index 118b2c41..89e38569 100644 --- a/app/Controller/Currency.php +++ b/app/Controller/Currency.php @@ -20,7 +20,7 @@ class Currency extends Base */ private function layout($template, array $params) { - $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId()); + $params['board_selector'] = $this->projectUserRole->getProjectsByUser($this->userSession->getId()); $params['config_content_for_layout'] = $this->template->render($template, $params); return $this->template->layout('config/layout', $params); diff --git a/app/Controller/Customfilter.php b/app/Controller/Customfilter.php index d6863103..ef75a837 100644 --- a/app/Controller/Customfilter.php +++ b/app/Controller/Customfilter.php @@ -137,7 +137,7 @@ class Customfilter extends Base { $user_id = $this->userSession->getId(); - if ($filter['user_id'] != $user_id && (! $this->projectPermission->isManager($project['id'], $user_id) || ! $this->userSession->isAdmin())) { + if ($filter['user_id'] != $user_id && ($this->projectUserRole->getUserRole($project['id'], $user_id) === Role::PROJECT_MANAGER || ! $this->userSession->isAdmin())) { $this->forbidden(); } } diff --git a/app/Controller/Doc.php b/app/Controller/Doc.php index 32413048..08561aa1 100644 --- a/app/Controller/Doc.php +++ b/app/Controller/Doc.php @@ -53,7 +53,7 @@ class Doc extends Base } $this->response->html($this->template->layout('doc/show', $this->readFile($filename) + array( - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), + 'board_selector' => $this->projectUserRole->getProjectsByUser($this->userSession->getId()), ))); } } diff --git a/app/Controller/Feed.php b/app/Controller/Feed.php index 95b81fb8..8457c383 100644 --- a/app/Controller/Feed.php +++ b/app/Controller/Feed.php @@ -25,10 +25,8 @@ class Feed extends Base $this->forbidden(true); } - $projects = $this->projectPermission->getActiveMemberProjects($user['id']); - $this->response->xml($this->template->render('feed/user', array( - 'events' => $this->projectActivity->getProjects(array_keys($projects)), + 'events' => $this->projectActivity->getProjects($this->projectPermission->getActiveProjectIds($user['id'])), 'user' => $user, ))); } diff --git a/app/Controller/Gantt.php b/app/Controller/Gantt.php index bd3d92f7..f3954a25 100644 --- a/app/Controller/Gantt.php +++ b/app/Controller/Gantt.php @@ -20,13 +20,13 @@ class Gantt extends Base if ($this->userSession->isAdmin()) { $project_ids = $this->project->getAllIds(); } else { - $project_ids = $this->projectPermission->getMemberProjectIds($this->userSession->getId()); + $project_ids = $this->projectPermission->getActiveProjectIds($this->userSession->getId()); } $this->response->html($this->template->layout('gantt/projects', array( 'projects' => $this->projectGanttFormatter->filter($project_ids)->format(), 'title' => t('Gantt chart for all projects'), - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), + 'board_selector' => $this->projectUserRole->getProjectsByUser($this->userSession->getId()), ))); } @@ -66,7 +66,7 @@ class Gantt extends Base } $this->response->html($this->template->layout('gantt/project', $params + array( - 'users_list' => $this->projectPermission->getMemberList($params['project']['id'], false), + 'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false), 'sorting' => $sorting, 'tasks' => $filter->format(), ))); @@ -109,7 +109,7 @@ class Gantt extends Base 'column_id' => $this->board->getFirstColumn($project['id']), 'position' => 1 ), - 'users_list' => $this->projectPermission->getMemberList($project['id'], true, false, true), + 'users_list' => $this->projectUserRole->getAssignableUsersList($project['id'], true, false, true), 'colors_list' => $this->color->getList(), 'categories_list' => $this->category->getList($project['id']), 'swimlanes_list' => $this->swimlane->getList($project['id'], false, true), diff --git a/app/Controller/Group.php b/app/Controller/Group.php index 4e81f6c1..22d49e61 100644 --- a/app/Controller/Group.php +++ b/app/Controller/Group.php @@ -25,7 +25,7 @@ class Group extends Base ->calculate(); $this->response->html($this->template->layout('group/index', array( - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), + 'board_selector' => $this->projectUserRole->getProjectsByUser($this->userSession->getId()), 'title' => t('Groups').' ('.$paginator->getTotal().')', 'paginator' => $paginator, ))); @@ -49,7 +49,7 @@ class Group extends Base ->calculate(); $this->response->html($this->template->layout('group/users', array( - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), + 'board_selector' => $this->projectUserRole->getProjectsByUser($this->userSession->getId()), 'title' => t('Members of %s', $group['name']).' ('.$paginator->getTotal().')', 'paginator' => $paginator, 'group' => $group, @@ -64,7 +64,7 @@ class Group extends Base public function create(array $values = array(), array $errors = array()) { $this->response->html($this->template->layout('group/create', array( - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), + 'board_selector' => $this->projectUserRole->getProjectsByUser($this->userSession->getId()), 'errors' => $errors, 'values' => $values, 'title' => t('New group') @@ -105,7 +105,7 @@ class Group extends Base } $this->response->html($this->template->layout('group/edit', array( - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), + 'board_selector' => $this->projectUserRole->getProjectsByUser($this->userSession->getId()), 'errors' => $errors, 'values' => $values, 'title' => t('Edit group') @@ -149,7 +149,7 @@ class Group extends Base } $this->response->html($this->template->layout('group/associate', array( - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), + 'board_selector' => $this->projectUserRole->getProjectsByUser($this->userSession->getId()), 'users' => $this->user->prepareList($this->groupMember->getNotMembers($group_id)), 'group' => $group, 'errors' => $errors, diff --git a/app/Controller/GroupHelper.php b/app/Controller/GroupHelper.php new file mode 100644 index 00000000..34f522a6 --- /dev/null +++ b/app/Controller/GroupHelper.php @@ -0,0 +1,24 @@ +<?php + +namespace Kanboard\Controller; + +/** + * Group Helper + * + * @package controller + * @author Frederic Guillot + */ +class GroupHelper extends Base +{ + /** + * Group autocompletion (Ajax) + * + * @access public + */ + public function autocomplete() + { + $search = $this->request->getStringParam('term'); + $groups = $this->groupManager->find($search); + $this->response->json($this->groupAutoCompleteFormatter->setGroups($groups)->format()); + } +} diff --git a/app/Controller/Link.php b/app/Controller/Link.php index c7f18230..33ec6688 100644 --- a/app/Controller/Link.php +++ b/app/Controller/Link.php @@ -21,7 +21,7 @@ class Link extends Base */ private function layout($template, array $params) { - $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId()); + $params['board_selector'] = $this->projectUserRole->getProjectsByUser($this->userSession->getId()); $params['config_content_for_layout'] = $this->template->render($template, $params); return $this->template->layout('config/layout', $params); diff --git a/app/Controller/Oauth.php b/app/Controller/Oauth.php index 39546148..ed901def 100644 --- a/app/Controller/Oauth.php +++ b/app/Controller/Oauth.php @@ -17,7 +17,7 @@ class Oauth extends Base */ public function google() { - $this->step1('google'); + $this->step1('Google'); } /** @@ -27,7 +27,7 @@ class Oauth extends Base */ public function github() { - $this->step1('github'); + $this->step1('Github'); } /** @@ -37,7 +37,7 @@ class Oauth extends Base */ public function gitlab() { - $this->step1('gitlab'); + $this->step1('Gitlab'); } /** @@ -45,12 +45,12 @@ class Oauth extends Base * * @access public */ - public function unlink($backend = '') + public function unlink() { - $backend = $this->request->getStringParam('backend', $backend); + $backend = $this->request->getStringParam('backend'); $this->checkCSRFParam(); - if ($this->authentication->backend($backend)->unlink($this->userSession->getId())) { + if ($this->authenticationManager->getProvider($backend)->unlink($this->userSession->getId())) { $this->flash->success(t('Your external account is not linked anymore to your profile.')); } else { $this->flash->failure(t('Unable to unlink your external account.')); @@ -63,15 +63,16 @@ class Oauth extends Base * Redirect to the provider if no code received * * @access private + * @param string $provider */ - private function step1($backend) + private function step1($provider) { $code = $this->request->getStringParam('code'); if (! empty($code)) { - $this->step2($backend, $code); + $this->step2($provider, $code); } else { - $this->response->redirect($this->authentication->backend($backend)->getService()->getAuthorizationUrl()); + $this->response->redirect($this->authenticationManager->getProvider($provider)->getService()->getAuthorizationUrl()); } } @@ -79,30 +80,35 @@ class Oauth extends Base * Link or authenticate the user * * @access private + * @param string $provider + * @param string $code */ - private function step2($backend, $code) + private function step2($provider, $code) { - $profile = $this->authentication->backend($backend)->getProfile($code); + $this->authenticationManager->getProvider($provider)->setCode($code); if ($this->userSession->isLogged()) { - $this->link($backend, $profile); + $this->link($provider); } - $this->authenticate($backend, $profile); + $this->authenticate($provider); } /** * Link the account * * @access private + * @param string $provider */ - private function link($backend, $profile) + private function link($provider) { - if (empty($profile)) { + $authProvider = $this->authenticationManager->getProvider($provider); + + if (! $authProvider->authenticate()) { $this->flash->failure(t('External authentication failed')); } else { + $this->userProfile->assign($this->userSession->getId(), $authProvider->getUser()); $this->flash->success(t('Your external account is linked to your profile successfully.')); - $this->authentication->backend($backend)->updateUser($this->userSession->getId(), $profile); } $this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId()))); @@ -112,10 +118,11 @@ class Oauth extends Base * Authenticate the account * * @access private + * @param string $provider */ - private function authenticate($backend, $profile) + private function authenticate($provider) { - if (! empty($profile) && $this->authentication->backend($backend)->authenticate($profile['id'])) { + if ($this->authenticationManager->oauthAuthentication($provider)) { $this->response->redirect($this->helper->url->to('app', 'index')); } else { $this->response->html($this->template->layout('auth/index', array( diff --git a/app/Controller/Project.php b/app/Controller/Project.php index 2d9c25de..80c95aa2 100644 --- a/app/Controller/Project.php +++ b/app/Controller/Project.php @@ -20,7 +20,7 @@ class Project extends Base if ($this->userSession->isAdmin()) { $project_ids = $this->project->getAllIds(); } else { - $project_ids = $this->projectPermission->getMemberProjectIds($this->userSession->getId()); + $project_ids = $this->projectPermission->getActiveProjectIds($this->userSession->getId()); } $nb_projects = count($project_ids); @@ -33,7 +33,7 @@ class Project extends Base ->calculate(); $this->response->html($this->template->layout('project/index', array( - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), + 'board_selector' => $this->projectUserRole->getProjectsByUser($this->userSession->getId()), 'paginator' => $paginator, 'nb_projects' => $nb_projects, 'title' => t('Projects').' ('.$nb_projects.')' @@ -160,11 +160,11 @@ class Project extends Base $values = $this->request->getValues(); if (isset($values['is_private'])) { - if (! $this->helper->user->isProjectAdministrationAllowed($project['id'])) { + if (! $this->helper->user->hasProjectAccess('project', 'create', $project['id'])) { unset($values['is_private']); } } elseif ($project['is_private'] == 1 && ! isset($values['is_private'])) { - if ($this->helper->user->isProjectAdministrationAllowed($project['id'])) { + if ($this->helper->user->hasProjectAccess('project', 'create', $project['id'])) { $values += array('is_private' => 0); } } @@ -184,120 +184,6 @@ class Project extends Base } /** - * Users list for the selected project - * - * @access public - */ - public function users() - { - $project = $this->getProject(); - - $this->response->html($this->projectLayout('project/users', array( - 'project' => $project, - 'users' => $this->projectPermission->getAllUsers($project['id']), - 'title' => t('Edit project access list') - ))); - } - - /** - * Allow everybody - * - * @access public - */ - public function allowEverybody() - { - $project = $this->getProject(); - $values = $this->request->getValues() + array('is_everybody_allowed' => 0); - list($valid, ) = $this->projectPermission->validateProjectModification($values); - - if ($valid) { - if ($this->project->update($values)) { - $this->flash->success(t('Project updated successfully.')); - } else { - $this->flash->failure(t('Unable to update this project.')); - } - } - - $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $project['id']))); - } - - /** - * Allow a specific user (admin only) - * - * @access public - */ - public function allow() - { - $values = $this->request->getValues(); - list($valid, ) = $this->projectPermission->validateUserModification($values); - - if ($valid) { - if ($this->projectPermission->addMember($values['project_id'], $values['user_id'])) { - $this->flash->success(t('Project updated successfully.')); - } else { - $this->flash->failure(t('Unable to update this project.')); - } - } - - $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $values['project_id']))); - } - - /** - * Change the role of a project member - * - * @access public - */ - public function role() - { - $this->checkCSRFParam(); - - $values = array( - 'project_id' => $this->request->getIntegerParam('project_id'), - 'user_id' => $this->request->getIntegerParam('user_id'), - 'is_owner' => $this->request->getIntegerParam('is_owner'), - ); - - list($valid, ) = $this->projectPermission->validateUserModification($values); - - if ($valid) { - if ($this->projectPermission->changeRole($values['project_id'], $values['user_id'], $values['is_owner'])) { - $this->flash->success(t('Project updated successfully.')); - } else { - $this->flash->failure(t('Unable to update this project.')); - } - } - - $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $values['project_id']))); - } - - /** - * Revoke user access (admin only) - * - * @access public - */ - public function revoke() - { - $this->checkCSRFParam(); - - $values = array( - 'project_id' => $this->request->getIntegerParam('project_id'), - 'user_id' => $this->request->getIntegerParam('user_id'), - ); - - list($valid, ) = $this->projectPermission->validateUserModification($values); - - if ($valid) { - if ($this->projectPermission->revokeMember($values['project_id'], $values['user_id'])) { - $this->flash->success(t('Project updated successfully.')); - } else { - $this->flash->failure(t('Unable to update this project.')); - } - } - - $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $values['project_id']))); - } - - /** * Remove a project * * @access public @@ -413,11 +299,11 @@ class Project extends Base */ public function create(array $values = array(), array $errors = array()) { - $is_private = $this->request->getIntegerParam('private', $this->userSession->isAdmin() || $this->userSession->isProjectAdmin() ? 0 : 1); + $is_private = isset($values['is_private']) && $values['is_private'] == 1; $this->response->html($this->template->layout('project/new', array( - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), - 'values' => empty($values) ? array('is_private' => $is_private) : $values, + 'board_selector' => $this->projectUserRole->getProjectsByUser($this->userSession->getId()), + 'values' => $values, 'errors' => $errors, 'is_private' => $is_private, 'title' => $is_private ? t('New private project') : t('New project'), @@ -425,6 +311,17 @@ class Project extends Base } /** + * Display a form to create a private project + * + * @access public + */ + public function createPrivate(array $values = array(), array $errors = array()) + { + $values['is_private'] = 1; + $this->create($values, $errors); + } + + /** * Validate and save a new project * * @access public diff --git a/app/Controller/ProjectPermission.php b/app/Controller/ProjectPermission.php new file mode 100644 index 00000000..4434d017 --- /dev/null +++ b/app/Controller/ProjectPermission.php @@ -0,0 +1,177 @@ +<?php + +namespace Kanboard\Controller; + +use Kanboard\Core\Security\Role; + +/** + * Project Permission + * + * @package controller + * @author Frederic Guillot + */ +class ProjectPermission extends Base +{ + /** + * Show all permissions + * + * @access public + */ + public function index(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + + if (empty($values)) { + $values['role'] = Role::PROJECT_MEMBER; + } + + $this->response->html($this->projectLayout('project_permission/index', array( + 'project' => $project, + 'users' => $this->projectUserRole->getUsers($project['id']), + 'groups' => $this->projectGroupRole->getGroups($project['id']), + 'roles' => $this->role->getProjectRoles(), + 'values' => $values, + 'errors' => $errors, + 'title' => t('Project Permissions'), + ))); + } + + /** + * Allow everybody + * + * @access public + */ + public function allowEverybody() + { + $project = $this->getProject(); + $values = $this->request->getValues() + array('is_everybody_allowed' => 0); + + if ($this->project->update($values)) { + $this->flash->success(t('Project updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this project.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id']))); + } + + /** + * Add user to the project + * + * @access public + */ + public function addUser() + { + $values = $this->request->getValues(); + + if ($this->projectUserRole->addUser($values['project_id'], $values['user_id'], $values['role'])) { + $this->flash->success(t('Project updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this project.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id']))); + } + + /** + * Revoke user access + * + * @access public + */ + public function removeUser() + { + $this->checkCSRFParam(); + + $values = array( + 'project_id' => $this->request->getIntegerParam('project_id'), + 'user_id' => $this->request->getIntegerParam('user_id'), + ); + + if ($this->projectUserRole->removeUser($values['project_id'], $values['user_id'])) { + $this->flash->success(t('Project updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this project.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id']))); + } + + /** + * Change user role + * + * @access public + */ + public function changeUserRole() + { + $project_id = $this->request->getIntegerParam('project_id'); + $values = $this->request->getJson(); + + if (! empty($project_id) && ! empty($values) && $this->projectUserRole->changeUserRole($project_id, $values['id'], $values['role'])) { + $this->response->json(array('status' => 'ok')); + } else { + $this->response->json(array('status' => 'error')); + } + } + + /** + * Add group to the project + * + * @access public + */ + public function addGroup() + { + $values = $this->request->getValues(); + + if (empty($values['group_id']) && ! empty($values['external_id'])) { + $values['group_id'] = $this->group->create($values['name'], $values['external_id']); + } + + if ($this->projectGroupRole->addGroup($values['project_id'], $values['group_id'], $values['role'])) { + $this->flash->success(t('Project updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this project.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id']))); + } + + /** + * Revoke group access + * + * @access public + */ + public function removeGroup() + { + $this->checkCSRFParam(); + + $values = array( + 'project_id' => $this->request->getIntegerParam('project_id'), + 'group_id' => $this->request->getIntegerParam('group_id'), + ); + + if ($this->projectGroupRole->removeGroup($values['project_id'], $values['group_id'])) { + $this->flash->success(t('Project updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this project.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id']))); + } + + /** + * Change group role + * + * @access public + */ + public function changeGroupRole() + { + $project_id = $this->request->getIntegerParam('project_id'); + $values = $this->request->getJson(); + + if (! empty($project_id) && ! empty($values) && $this->projectGroupRole->changeGroupRole($project_id, $values['id'], $values['role'])) { + $this->response->json(array('status' => 'ok')); + } else { + $this->response->json(array('status' => 'error')); + } + } +} diff --git a/app/Controller/Projectuser.php b/app/Controller/Projectuser.php index 18829b3c..34595764 100644 --- a/app/Controller/Projectuser.php +++ b/app/Controller/Projectuser.php @@ -4,6 +4,7 @@ namespace Kanboard\Controller; use Kanboard\Model\User as UserModel; use Kanboard\Model\Task as TaskModel; +use Kanboard\Core\Security\Role; /** * Project User overview @@ -23,7 +24,7 @@ class Projectuser extends Base */ private function layout($template, array $params) { - $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId()); + $params['board_selector'] = $this->projectUserRole->getProjectsByUser($this->userSession->getId()); $params['content_for_sublayout'] = $this->template->render($template, $params); $params['filter'] = array('user_id' => $params['user_id']); @@ -37,17 +38,17 @@ class Projectuser extends Base if ($this->userSession->isAdmin()) { $project_ids = $this->project->getAllIds(); } else { - $project_ids = $this->projectPermission->getMemberProjectIds($this->userSession->getId()); + $project_ids = $this->projectPermission->getActiveProjectIds($this->userSession->getId()); } return array($user_id, $project_ids, $this->user->getList(true)); } - private function role($is_owner, $action, $title, $title_user) + private function role($role, $action, $title, $title_user) { list($user_id, $project_ids, $users) = $this->common(); - $query = $this->projectPermission->getQueryByRole($project_ids, $is_owner)->callback(array($this->project, 'applyColumnStats')); + $query = $this->projectPermission->getQueryByRole($project_ids, $role)->callback(array($this->project, 'applyColumnStats')); if ($user_id !== UserModel::EVERYBODY_ID) { $query->eq(UserModel::TABLE.'.id', $user_id); @@ -101,7 +102,7 @@ class Projectuser extends Base */ public function managers() { - $this->role(1, 'managers', t('People who are project managers'), 'Projects where "%s" is manager'); + $this->role(Role::PROJECT_MANAGER, 'managers', t('People who are project managers'), 'Projects where "%s" is manager'); } /** @@ -110,7 +111,7 @@ class Projectuser extends Base */ public function members() { - $this->role(0, 'members', t('People who are project members'), 'Projects where "%s" is member'); + $this->role(ROLE::PROJECT_MEMBER, 'members', t('People who are project members'), 'Projects where "%s" is member'); } /** diff --git a/app/Controller/Search.php b/app/Controller/Search.php index 0aff9073..390210c0 100644 --- a/app/Controller/Search.php +++ b/app/Controller/Search.php @@ -12,7 +12,7 @@ class Search extends Base { public function index() { - $projects = $this->projectPermission->getAllowedProjects($this->userSession->getId()); + $projects = $this->projectUserRole->getProjectsByUser($this->userSession->getId()); $search = urldecode($this->request->getStringParam('search')); $nb_tasks = 0; diff --git a/app/Controller/Subtask.php b/app/Controller/Subtask.php index 30ddc375..c93b637d 100644 --- a/app/Controller/Subtask.php +++ b/app/Controller/Subtask.php @@ -48,7 +48,7 @@ class Subtask extends Base $this->response->html($this->taskLayout('subtask/create', array( 'values' => $values, 'errors' => $errors, - 'users_list' => $this->projectPermission->getMemberList($task['project_id']), + 'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']), 'task' => $task, ))); } @@ -95,7 +95,7 @@ class Subtask extends Base $this->response->html($this->taskLayout('subtask/edit', array( 'values' => empty($values) ? $subtask : $values, 'errors' => $errors, - 'users_list' => $this->projectPermission->getMemberList($task['project_id']), + 'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']), 'status_list' => $this->subtask->getStatusList(), 'subtask' => $subtask, 'task' => $task, diff --git a/app/Controller/Task.php b/app/Controller/Task.php index e71b2017..1811dcb7 100644 --- a/app/Controller/Task.php +++ b/app/Controller/Task.php @@ -76,7 +76,7 @@ class Task extends Base 'link_label_list' => $this->link->getList(0, false), 'columns_list' => $this->board->getColumnsList($task['project_id']), 'colors_list' => $this->color->getList(), - 'users_list' => $this->projectPermission->getMemberList($task['project_id'], true, false, false), + 'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id'], true, false, false), 'date_format' => $this->config->get('application_date_format'), 'date_formats' => $this->dateParser->getAvailableFormats(), 'title' => $task['project_name'].' > '.$task['title'], diff --git a/app/Controller/TaskHelper.php b/app/Controller/TaskHelper.php new file mode 100644 index 00000000..236af33e --- /dev/null +++ b/app/Controller/TaskHelper.php @@ -0,0 +1,57 @@ +<?php + +namespace Kanboard\Controller; + +/** + * Task Ajax Helper + * + * @package controller + * @author Frederic Guillot + */ +class TaskHelper extends Base +{ + /** + * Render Markdown text and reply with the HTML Code + * + * @access public + */ + public function preview() + { + $payload = $this->request->getJson(); + + if (empty($payload['text'])) { + $this->response->html('<p>'.t('Nothing to preview...').'</p>'); + } + + $this->response->html($this->helper->text->markdown($payload['text'])); + } + + /** + * Task autocompletion (Ajax) + * + * @access public + */ + public function autocomplete() + { + $search = $this->request->getStringParam('term'); + $projects = $this->projectPermission->getActiveProjectIds($this->userSession->getId()); + + if (empty($projects)) { + $this->response->json(array()); + } + + $filter = $this->taskFilterAutoCompleteFormatter + ->create() + ->filterByProjects($projects) + ->excludeTasks(array($this->request->getIntegerParam('exclude_task_id'))); + + // Search by task id or by title + if (ctype_digit($search)) { + $filter->filterById($search); + } else { + $filter->filterByTitle($search); + } + + $this->response->json($filter->format()); + } +} diff --git a/app/Controller/Taskcreation.php b/app/Controller/Taskcreation.php index cffa9d74..4d74fac6 100644 --- a/app/Controller/Taskcreation.php +++ b/app/Controller/Taskcreation.php @@ -36,7 +36,7 @@ class Taskcreation extends Base 'errors' => $errors, 'values' => $values + array('project_id' => $project['id']), 'columns_list' => $this->board->getColumnsList($project['id']), - 'users_list' => $this->projectPermission->getMemberList($project['id'], true, false, true), + 'users_list' => $this->projectUserRole->getAssignableUsersList($project['id'], true, false, true), 'colors_list' => $this->color->getList(), 'categories_list' => $this->category->getList($project['id']), 'swimlanes_list' => $swimlanes_list, diff --git a/app/Controller/Taskduplication.php b/app/Controller/Taskduplication.php index 9cd684eb..ae8bfcbc 100644 --- a/app/Controller/Taskduplication.php +++ b/app/Controller/Taskduplication.php @@ -2,6 +2,8 @@ namespace Kanboard\Controller; +use Kanboard\Model\Project as ProjectModel; + /** * Task Duplication controller * @@ -107,7 +109,7 @@ class Taskduplication extends Base private function chooseDestination(array $task, $template) { $values = array(); - $projects_list = $this->projectPermission->getActiveMemberProjects($this->userSession->getId()); + $projects_list = $this->projectUserRole->getProjectsByUser($this->userSession->getId(), array(ProjectModel::ACTIVE)); unset($projects_list[$task['project_id']]); @@ -117,7 +119,7 @@ class Taskduplication extends Base $swimlanes_list = $this->swimlane->getList($dst_project_id, false, true); $columns_list = $this->board->getColumnsList($dst_project_id); $categories_list = $this->category->getList($dst_project_id); - $users_list = $this->projectPermission->getMemberList($dst_project_id); + $users_list = $this->projectUserRole->getAssignableUsersList($dst_project_id); $values = $this->taskDuplication->checkDestinationProjectValues($task); $values['project_id'] = $dst_project_id; diff --git a/app/Controller/Taskmodification.php b/app/Controller/Taskmodification.php index 02b09a36..81cf430f 100644 --- a/app/Controller/Taskmodification.php +++ b/app/Controller/Taskmodification.php @@ -110,7 +110,7 @@ class Taskmodification extends Base 'values' => $values, 'errors' => $errors, 'task' => $task, - 'users_list' => $this->projectPermission->getMemberList($task['project_id']), + 'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']), 'colors_list' => $this->color->getList(), 'categories_list' => $this->category->getList($task['project_id']), 'date_format' => $this->config->get('application_date_format'), diff --git a/app/Controller/Twofactor.php b/app/Controller/Twofactor.php index a7368d6b..aeb13acc 100644 --- a/app/Controller/Twofactor.php +++ b/app/Controller/Twofactor.php @@ -2,10 +2,6 @@ namespace Kanboard\Controller; -use Otp\Otp; -use Otp\GoogleAuthenticator; -use Base32\Base32; - /** * Two Factor Auth controller * @@ -36,12 +32,15 @@ class Twofactor extends User $user = $this->getUser(); $this->checkCurrentUser($user); + $provider = $this->authenticationManager->getPostAuthenticationProvider(); $label = $user['email'] ?: $user['username']; + $provider->setSecret($user['twofactor_secret']); + $this->response->html($this->layout('twofactor/index', array( 'user' => $user, - 'qrcode_url' => $user['twofactor_activated'] == 1 ? GoogleAuthenticator::getQrCodeUrl('totp', $label, $user['twofactor_secret']) : '', - 'key_url' => $user['twofactor_activated'] == 1 ? GoogleAuthenticator::getKeyUri('totp', $label, $user['twofactor_secret']) : '', + 'qrcode_url' => $user['twofactor_activated'] == 1 ? $provider->getQrCodeUrl($label) : '', + 'key_url' => $user['twofactor_activated'] == 1 ? $provider->getKeyUrl($label) : '', ))); } @@ -61,7 +60,7 @@ class Twofactor extends User $this->user->update(array( 'id' => $user['id'], 'twofactor_activated' => 1, - 'twofactor_secret' => GoogleAuthenticator::generateRandom(), + 'twofactor_secret' => $this->authenticationManager->getPostAuthenticationProvider()->getSecret(), )); } else { $this->user->update(array( @@ -72,14 +71,14 @@ class Twofactor extends User } // Allow the user to test or disable the feature - $this->userSession->disable2FA(); + $this->userSession->disablePostAuthentication(); $this->flash->success(t('User updated successfully.')); $this->response->redirect($this->helper->url->to('twofactor', 'index', array('user_id' => $user['id']))); } /** - * Test 2FA + * Test code * * @access public */ @@ -88,10 +87,13 @@ class Twofactor extends User $user = $this->getUser(); $this->checkCurrentUser($user); - $otp = new Otp; $values = $this->request->getValues(); - if (! empty($values['code']) && $otp->checkTotp(Base32::decode($user['twofactor_secret']), $values['code'])) { + $provider = $this->authenticationManager->getPostAuthenticationProvider(); + $provider->setCode(empty($values['code']) ? '' : $values['code']); + $provider->setSecret($user['twofactor_secret']); + + if ($provider->authenticate()) { $this->flash->success(t('The two factor authentication code is valid.')); } else { $this->flash->failure(t('The two factor authentication code is not valid.')); @@ -110,11 +112,14 @@ class Twofactor extends User $user = $this->getUser(); $this->checkCurrentUser($user); - $otp = new Otp; $values = $this->request->getValues(); - if (! empty($values['code']) && $otp->checkTotp(Base32::decode($user['twofactor_secret']), $values['code'])) { - $this->sessionStorage->postAuth['validated'] = true; + $provider = $this->authenticationManager->getPostAuthenticationProvider(); + $provider->setCode(empty($values['code']) ? '' : $values['code']); + $provider->setSecret($user['twofactor_secret']); + + if ($provider->authenticate()) { + $this->userSession->validatePostAuthentication(); $this->flash->success(t('The two factor authentication code is valid.')); $this->response->redirect($this->helper->url->to('app', 'index')); } else { diff --git a/app/Controller/User.php b/app/Controller/User.php index 23e19828..aa548647 100644 --- a/app/Controller/User.php +++ b/app/Controller/User.php @@ -3,6 +3,8 @@ namespace Kanboard\Controller; use Kanboard\Notification\Mail as MailNotification; +use Kanboard\Model\Project as ProjectModel; +use Kanboard\Core\Security\Role; /** * User controller @@ -24,7 +26,7 @@ class User extends Base { $content = $this->template->render($template, $params); $params['user_content_for_layout'] = $content; - $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId()); + $params['board_selector'] = $this->projectUserRole->getProjectsByUser($this->userSession->getId()); if (isset($params['user'])) { $params['title'] = ($params['user']['name'] ?: $params['user']['username']).' (#'.$params['user']['id'].')'; @@ -49,7 +51,7 @@ class User extends Base $this->response->html( $this->template->layout('user/index', array( - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), + 'board_selector' => $this->projectUserRole->getProjectsByUser($this->userSession->getId()), 'title' => t('Users').' ('.$paginator->getTotal().')', 'paginator' => $paginator, ))); @@ -67,10 +69,11 @@ class User extends Base $this->response->html($this->template->layout($is_remote ? 'user/create_remote' : 'user/create_local', array( 'timezones' => $this->config->getTimezones(true), 'languages' => $this->config->getLanguages(true), - 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()), + 'roles' => $this->role->getApplicationRoles(), + 'board_selector' => $this->projectUserRole->getProjectsByUser($this->userSession->getId()), 'projects' => $this->project->getList(), 'errors' => $errors, - 'values' => $values, + 'values' => $values + array('role' => Role::APP_USER), 'title' => t('New user') ))); } @@ -92,7 +95,7 @@ class User extends Base $user_id = $this->user->create($values); if ($user_id !== false) { - $this->projectPermission->addMember($project_id, $user_id); + $this->projectUserRole->addUser($project_id, $user_id, Role::PROJECT_MEMBER); if (! empty($values['notifications_enabled'])) { $this->userNotificationType->saveSelectedTypes($user_id, array(MailNotification::TYPE)); @@ -170,7 +173,7 @@ class User extends Base { $user = $this->getUser(); $this->response->html($this->layout('user/sessions', array( - 'sessions' => $this->authentication->backend('rememberMe')->getAll($user['id']), + 'sessions' => $this->rememberMeSession->getAll($user['id']), 'user' => $user, ))); } @@ -184,8 +187,8 @@ class User extends Base { $this->checkCSRFParam(); $user = $this->getUser(); - $this->authentication->backend('rememberMe')->remove($this->request->getIntegerParam('id')); - $this->response->redirect($this->helper->url->to('user', 'session', array('user_id' => $user['id']))); + $this->rememberMeSession->remove($this->request->getIntegerParam('id')); + $this->response->redirect($this->helper->url->to('user', 'sessions', array('user_id' => $user['id']))); } /** @@ -205,7 +208,7 @@ class User extends Base } $this->response->html($this->layout('user/notifications', array( - 'projects' => $this->projectPermission->getMemberProjects($user['id']), + 'projects' => $this->projectUserRole->getProjectsByUser($user['id'], array(ProjectModel::ACTIVE)), 'notifications' => $this->userNotification->readSettings($user['id']), 'types' => $this->userNotificationType->getTypes(), 'filters' => $this->userNotificationFilter->getFilters(), @@ -326,16 +329,9 @@ class User extends Base if ($this->request->isPost()) { $values = $this->request->getValues(); - if ($this->userSession->isAdmin()) { - $values += array('is_admin' => 0, 'is_project_admin' => 0); - } else { - // Regular users can't be admin - if (isset($values['is_admin'])) { - unset($values['is_admin']); - } - - if (isset($values['is_project_admin'])) { - unset($values['is_project_admin']); + if (! $this->userSession->isAdmin()) { + if (isset($values['role'])) { + unset($values['role']); } } @@ -358,6 +354,7 @@ class User extends Base 'user' => $user, 'timezones' => $this->config->getTimezones(true), 'languages' => $this->config->getLanguages(true), + 'roles' => $this->role->getApplicationRoles(), ))); } diff --git a/app/Controller/UserHelper.php b/app/Controller/UserHelper.php new file mode 100644 index 00000000..f164d0a6 --- /dev/null +++ b/app/Controller/UserHelper.php @@ -0,0 +1,24 @@ +<?php + +namespace Kanboard\Controller; + +/** + * User Helper + * + * @package controller + * @author Frederic Guillot + */ +class UserHelper extends Base +{ + /** + * User autocompletion (Ajax) + * + * @access public + */ + public function autocomplete() + { + $search = $this->request->getStringParam('term'); + $users = $this->userFilterAutoCompleteFormatter->create($search)->filterByUsernameOrByName()->format(); + $this->response->json($users); + } +} |