Added basic privilege system
This commit is contained in:
parent
2ecb79a2fa
commit
de31770c87
12 changed files with 77 additions and 16 deletions
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
if (typeof(failCallback) !== 'undefined') {
|
||||||
|
failCallback(response);
|
||||||
|
} else {
|
||||||
console.log(Error(response.json && response.json.error || response));
|
console.log(Error(response.json && response.json.error || response));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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');
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
}});
|
}});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = '#/';
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
<ul>
|
<ul>
|
||||||
<!-- todo: check privileges -->
|
<!-- todo: check privileges -->
|
||||||
|
<% if (_.contains(privileges, 'listUsers')) { %>
|
||||||
<li class="users">
|
<li class="users">
|
||||||
<a href="#/users">Users</a>
|
<a href="#/users">Users</a>
|
||||||
</li>
|
</li>
|
||||||
|
<% } %>
|
||||||
<% if (!loggedIn) { %>
|
<% if (!loggedIn) { %>
|
||||||
<li class="login">
|
<li class="login">
|
||||||
<a href="#/login">Login</a>
|
<a href="#/login">Login</a>
|
||||||
|
|
|
@ -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(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
9
src/Privilege.php
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
namespace Szurubooru;
|
||||||
|
|
||||||
|
class Privilege
|
||||||
|
{
|
||||||
|
const PRIVILEGE_LIST_USERS = 'listUsers';
|
||||||
|
const PRIVILEGE_VIEW_USER = 'viewUser';
|
||||||
|
const PRIVILEGE_REGISTER = 'register';
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue