Added basic privilege system

This commit is contained in:
Marcin Kurczewski 2014-09-04 19:57:06 +02:00
parent 2ecb79a2fa
commit de31770c87
12 changed files with 77 additions and 16 deletions

View file

@ -7,6 +7,13 @@ name = booru-dev
secret = change secret = change
minPasswordLength = 5 minPasswordLength = 5
[security.privileges]
anonymous = register, viewUser
regularUser = listUsers, viewUser
powerUser = listUsers, viewUser
moderator = listUsers, viewUser
administrator = listUsers, viewUser
[users] [users]
minUserNameLength = 1 minUserNameLength = 1
maxUserNameLength = 32 maxUserNameLength = 32

View file

@ -44,10 +44,8 @@ App.Auth = function(jQuery, util, api, appState, promise) {
function logout() { function logout() {
return promise.make(function(resolve, reject) { return promise.make(function(resolve, reject) {
appState.set('loggedIn', false);
appState.set('loginToken', null);
jQuery.removeCookie('auth'); jQuery.removeCookie('auth');
resolve(); return loginAnonymous().then(resolve).fail(reject);
}); });
}; };
@ -75,6 +73,7 @@ App.Auth = function(jQuery, util, api, appState, promise) {
}; };
function updateAppState(response) { function updateAppState(response) {
appState.set('privileges', response.json.privileges || []);
appState.set('loginToken', response.json.token && response.json.token.name); appState.set('loginToken', response.json.token && response.json.token.name);
appState.set('loggedInUser', response.json.user); appState.set('loggedInUser', response.json.user);
appState.set('loggedIn', response.json.user && !!response.json.user.id); appState.set('loggedIn', response.json.user && !!response.json.user.id);

View file

@ -9,6 +9,7 @@ App.Presenters.PagedCollectionPresenter = function(util, promise, api) {
var baseUri; var baseUri;
var backendUri; var backendUri;
var renderCallback; var renderCallback;
var failCallback;
var template; var template;
var pageSize; var pageSize;
@ -19,11 +20,10 @@ App.Presenters.PagedCollectionPresenter = function(util, promise, api) {
baseUri = args.baseUri; baseUri = args.baseUri;
backendUri = args.backendUri; backendUri = args.backendUri;
renderCallback = args.renderCallback; renderCallback = args.renderCallback;
failCallback = args.failCallback;
promise.wait(util.promiseTemplate('pager')).then(function(html) { promise.wait(util.promiseTemplate('pager')).then(function(html) {
template = _.template(html); template = _.template(html);
//renderCallback({entities: [], totalRecords: 0});
changePage(pageNumber); changePage(pageNumber);
}); });
} }
@ -44,7 +44,11 @@ App.Presenters.PagedCollectionPresenter = function(util, promise, api) {
entities: response.json.data, entities: response.json.data,
totalRecords: response.json.totalRecords}); totalRecords: response.json.totalRecords});
}).fail(function(response) { }).fail(function(response) {
console.log(Error(response.json && response.json.error || response)); if (typeof(failCallback) !== 'undefined') {
failCallback(response);
} else {
console.log(Error(response.json && response.json.error || response));
}
}); });
} }

View file

@ -33,6 +33,7 @@ App.Presenters.TopNavigationPresenter = function(
$el.html(template({ $el.html(template({
loggedIn: appState.get('loggedIn'), loggedIn: appState.get('loggedIn'),
user: appState.get('loggedInUser'), user: appState.get('loggedInUser'),
privileges: appState.get('privileges'),
})); }));
$el.find('li.' + selectedElement).addClass('active'); $el.find('li.' + selectedElement).addClass('active');
}; };

View file

@ -7,7 +7,8 @@ App.Presenters.UserListPresenter = function(
promise, promise,
router, router,
pagedCollectionPresenter, pagedCollectionPresenter,
topNavigationPresenter) { topNavigationPresenter,
messagePresenter) {
var $el = jQuery('#content'); var $el = jQuery('#content');
var template; var template;
@ -28,6 +29,10 @@ App.Presenters.UserListPresenter = function(
renderCallback: function updateCollection(data) { renderCallback: function updateCollection(data) {
userList = data.entities; userList = data.entities;
render(); render();
},
failCallback: function(response) {
$el.empty();
messagePresenter.showError($el, response.json && response.json.error || response);
}}); }});
}); });
} }

View file

@ -1,6 +1,6 @@
var App = App || {}; var App = App || {};
App.Router = function(jQuery, util) { App.Router = function(jQuery, util, appState) {
var root = '#/'; var root = '#/';

View file

@ -1,8 +1,10 @@
<ul> <ul>
<!-- todo: check privileges --> <!-- todo: check privileges -->
<li class="users"> <% if (_.contains(privileges, 'listUsers')) { %>
<a href="#/users">Users</a> <li class="users">
</li> <a href="#/users">Users</a>
</li>
<% } %>
<% if (!loggedIn) { %> <% if (!loggedIn) { %>
<li class="login"> <li class="login">
<a href="#/login">Login</a> <a href="#/login">Login</a>

View file

@ -45,6 +45,7 @@ final class AuthController extends AbstractController
[ [
'token' => new \Szurubooru\ViewProxies\Token($this->authService->getLoginToken()), 'token' => new \Szurubooru\ViewProxies\Token($this->authService->getLoginToken()),
'user' => new \Szurubooru\ViewProxies\User($this->authService->getLoggedInUser()), 'user' => new \Szurubooru\ViewProxies\User($this->authService->getLoggedInUser()),
'privileges' => $this->authService->getCurrentPrivileges(),
]; ];
} }
} }

View file

@ -3,15 +3,18 @@ namespace Szurubooru\Controllers;
final class UserController extends AbstractController final class UserController extends AbstractController
{ {
private $inputReader; private $authService;
private $userService; private $userService;
private $inputReader;
public function __construct( public function __construct(
\Szurubooru\Services\AuthService $authService,
\Szurubooru\Services\UserService $userService, \Szurubooru\Services\UserService $userService,
\Szurubooru\Helpers\InputReader $inputReader) \Szurubooru\Helpers\InputReader $inputReader)
{ {
$this->inputReader = $inputReader; $this->authService = $authService;
$this->userService = $userService; $this->userService = $userService;
$this->inputReader = $inputReader;
} }
public function registerRoutes(\Szurubooru\Router $router) public function registerRoutes(\Szurubooru\Router $router)
@ -25,7 +28,8 @@ final class UserController extends AbstractController
public function getFiltered() public function getFiltered()
{ {
//todo: privilege checking $this->authService->assertPrivilege(\Szurubooru\Privilege::PRIVILEGE_LIST_USERS);
//todo: move this to form data constructor //todo: move this to form data constructor
$searchFormData = new \Szurubooru\FormData\SearchFormData; $searchFormData = new \Szurubooru\FormData\SearchFormData;
$searchFormData->query = $this->inputReader->query; $searchFormData->query = $this->inputReader->query;
@ -41,7 +45,8 @@ final class UserController extends AbstractController
public function getByName($name) public function getByName($name)
{ {
//todo: privilege checking $this->authService->assertPrivilege(\Szurubooru\Privilege::PRIVILEGE_VIEW_USER);
$user = $this->userService->getByName($name); $user = $this->userService->getByName($name);
if (!$user) if (!$user)
throw new \DomainException('User with name "' . $name . '" was not found.'); throw new \DomainException('User with name "' . $name . '" was not found.');
@ -50,7 +55,8 @@ final class UserController extends AbstractController
public function register() public function register()
{ {
//todo: privilege checking $this->authService->assertPrivilege(\Szurubooru\Privilege::PRIVILEGE_REGISTER);
$input = new \Szurubooru\FormData\RegistrationFormData; $input = new \Szurubooru\FormData\RegistrationFormData;
//todo: move this to form data constructor //todo: move this to form data constructor
$input->name = $this->inputReader->userName; $input->name = $this->inputReader->userName;

9
src/Privilege.php Normal file
View file

@ -0,0 +1,9 @@
<?php
namespace Szurubooru;
class Privilege
{
const PRIVILEGE_LIST_USERS = 'listUsers';
const PRIVILEGE_VIEW_USER = 'viewUser';
const PRIVILEGE_REGISTER = 'register';
}

View file

@ -7,6 +7,7 @@ class AuthService
private $loginToken = null; private $loginToken = null;
private $validator; private $validator;
private $config;
private $passwordService; private $passwordService;
private $timeService; private $timeService;
private $userDao; private $userDao;
@ -14,6 +15,7 @@ class AuthService
public function __construct( public function __construct(
\Szurubooru\Validator $validator, \Szurubooru\Validator $validator,
\Szurubooru\Config $config,
\Szurubooru\Services\PasswordService $passwordService, \Szurubooru\Services\PasswordService $passwordService,
\Szurubooru\Services\TimeService $timeService, \Szurubooru\Services\TimeService $timeService,
\Szurubooru\Dao\TokenDao $tokenDao, \Szurubooru\Dao\TokenDao $tokenDao,
@ -22,6 +24,7 @@ class AuthService
$this->loggedInUser = $this->getAnonymousUser(); $this->loggedInUser = $this->getAnonymousUser();
$this->validator = $validator; $this->validator = $validator;
$this->config = $config;
$this->passwordService = $passwordService; $this->passwordService = $passwordService;
$this->timeService = $timeService; $this->timeService = $timeService;
$this->tokenDao = $tokenDao; $this->tokenDao = $tokenDao;
@ -103,6 +106,27 @@ class AuthService
$this->loginToken = null; $this->loginToken = null;
} }
public function getCurrentPrivileges()
{
switch ($this->getLoggedInUser()->accessRank)
{
case \Szurubooru\Entities\User::ACCESS_RANK_ANONYMOUS: $keyName = 'anonymous'; break;
case \Szurubooru\Entities\User::ACCESS_RANK_REGULAR_USER: $keyName = 'regularUser'; break;
case \Szurubooru\Entities\User::ACCESS_RANK_POWER_USER: $keyName = 'powerUser'; break;
case \Szurubooru\Entities\User::ACCESS_RANK_MODERATOR: $keyName = 'moderator'; break;
case \Szurubooru\Entities\User::ACCESS_RANK_ADMINISTRATOR: $keyName = 'administrator'; break;
default:
throw new \DomainException('Invalid access rank!');
}
return array_filter(preg_split('/[;,\s]+/', $this->config->security->privileges[$keyName]));
}
public function assertPrivilege($privilege)
{
if (!in_array($privilege, $this->getCurrentPrivileges()))
throw new \DomainException('Unprivileged operation');
}
private function createAndSaveLoginToken(\Szurubooru\Entities\User $user) private function createAndSaveLoginToken(\Szurubooru\Entities\User $user)
{ {
$loginToken = new \Szurubooru\Entities\Token(); $loginToken = new \Szurubooru\Entities\Token();

View file

@ -4,6 +4,7 @@ namespace Szurubooru\Tests\Services;
class AuthServiceTest extends \Szurubooru\Tests\AbstractTestCase class AuthServiceTest extends \Szurubooru\Tests\AbstractTestCase
{ {
private $validatorMock; private $validatorMock;
private $configMock;
private $passwordServiceMock; private $passwordServiceMock;
private $timeServiceMock; private $timeServiceMock;
private $tokenDaoMock; private $tokenDaoMock;
@ -12,6 +13,7 @@ class AuthServiceTest extends \Szurubooru\Tests\AbstractTestCase
public function setUp() public function setUp()
{ {
$this->validatorMock = $this->mock(\Szurubooru\Validator::class); $this->validatorMock = $this->mock(\Szurubooru\Validator::class);
$this->configMock = $this->mock(\Szurubooru\Config::class);
$this->passwordServiceMock = $this->mock(\Szurubooru\Services\PasswordService::class); $this->passwordServiceMock = $this->mock(\Szurubooru\Services\PasswordService::class);
$this->timeServiceMock = $this->mock(\Szurubooru\Services\TimeService::class); $this->timeServiceMock = $this->mock(\Szurubooru\Services\TimeService::class);
$this->tokenDaoMock = $this->mock(\Szurubooru\Dao\TokenDao::class); $this->tokenDaoMock = $this->mock(\Szurubooru\Dao\TokenDao::class);
@ -94,6 +96,7 @@ class AuthServiceTest extends \Szurubooru\Tests\AbstractTestCase
{ {
return new \Szurubooru\Services\AuthService( return new \Szurubooru\Services\AuthService(
$this->validatorMock, $this->validatorMock,
$this->configMock,
$this->passwordServiceMock, $this->passwordServiceMock,
$this->timeServiceMock, $this->timeServiceMock,
$this->tokenDaoMock, $this->tokenDaoMock,