Rewrite of the authentication and authorization system

This commit is contained in:
Frederic Guillot
2015-12-05 20:31:27 -05:00
parent 346b8312e5
commit e9fedf3e5c
255 changed files with 14114 additions and 9820 deletions

View File

@@ -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']),

View File

@@ -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'])

View File

@@ -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)
{

View File

@@ -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());
}
}

View File

@@ -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'));
}
}

View File

@@ -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);
}
}
@@ -69,34 +55,14 @@ 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
*/
public function handleAuthorization($controller, $action)
private function checkApplicationAuthorization($controller, $action)
{
if (! $this->helper->user->hasAccess($controller, $action)) {
$this->forbidden();
}
}
/**
* Check project authorization
*
* @access private
*/
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'].' &gt; '.$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'].' &gt; '.$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(

View File

@@ -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'],
@@ -142,195 +142,6 @@ class Board extends Base
$this->response->html($this->renderBoard($project_id));
}
/**
* 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
*
@@ -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)
{

View File

@@ -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',
)));
}
}

View File

@@ -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)));
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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()),
)));
}
}

View File

@@ -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,
)));
}

View File

@@ -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),

View File

@@ -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,

View File

@@ -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());
}
}

View File

@@ -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);

View File

@@ -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(

View File

@@ -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);
}
}
@@ -183,120 +183,6 @@ class Project extends Base
$this->edit($values, $errors);
}
/**
* 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
*
@@ -413,17 +299,28 @@ 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'),
)));
}
/**
* 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
*

View File

@@ -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'));
}
}
}

View File

@@ -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');
}
/**

View File

@@ -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;

View File

@@ -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,

View File

@@ -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'].' &gt; '.$task['title'],

View File

@@ -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());
}
}

View File

@@ -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,

View File

@@ -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;

View File

@@ -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'),

View File

@@ -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 {

View File

@@ -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(),
)));
}

View File

@@ -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);
}
}