diff --git a/data/config.ini b/data/config.ini
index 7eaf1174..7aafbecd 100644
--- a/data/config.ini
+++ b/data/config.ini
@@ -7,6 +7,13 @@ name = booru-dev
secret = change
minPasswordLength = 5
+[security.privileges]
+anonymous = register, viewUser
+regularUser = listUsers, viewUser
+powerUser = listUsers, viewUser
+moderator = listUsers, viewUser
+administrator = listUsers, viewUser
+
[users]
minUserNameLength = 1
maxUserNameLength = 32
diff --git a/public_html/js/Auth.js b/public_html/js/Auth.js
index 432192c3..0c867da4 100644
--- a/public_html/js/Auth.js
+++ b/public_html/js/Auth.js
@@ -44,10 +44,8 @@ App.Auth = function(jQuery, util, api, appState, promise) {
function logout() {
return promise.make(function(resolve, reject) {
- appState.set('loggedIn', false);
- appState.set('loginToken', null);
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) {
+ appState.set('privileges', response.json.privileges || []);
appState.set('loginToken', response.json.token && response.json.token.name);
appState.set('loggedInUser', response.json.user);
appState.set('loggedIn', response.json.user && !!response.json.user.id);
diff --git a/public_html/js/Presenters/PagedCollectionPresenter.js b/public_html/js/Presenters/PagedCollectionPresenter.js
index a870511c..85a36f92 100644
--- a/public_html/js/Presenters/PagedCollectionPresenter.js
+++ b/public_html/js/Presenters/PagedCollectionPresenter.js
@@ -9,6 +9,7 @@ App.Presenters.PagedCollectionPresenter = function(util, promise, api) {
var baseUri;
var backendUri;
var renderCallback;
+ var failCallback;
var template;
var pageSize;
@@ -19,11 +20,10 @@ App.Presenters.PagedCollectionPresenter = function(util, promise, api) {
baseUri = args.baseUri;
backendUri = args.backendUri;
renderCallback = args.renderCallback;
+ failCallback = args.failCallback;
promise.wait(util.promiseTemplate('pager')).then(function(html) {
template = _.template(html);
- //renderCallback({entities: [], totalRecords: 0});
-
changePage(pageNumber);
});
}
@@ -44,7 +44,11 @@ App.Presenters.PagedCollectionPresenter = function(util, promise, api) {
entities: response.json.data,
totalRecords: response.json.totalRecords});
}).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));
+ }
});
}
diff --git a/public_html/js/Presenters/TopNavigationPresenter.js b/public_html/js/Presenters/TopNavigationPresenter.js
index 7596cf25..ef720393 100644
--- a/public_html/js/Presenters/TopNavigationPresenter.js
+++ b/public_html/js/Presenters/TopNavigationPresenter.js
@@ -33,6 +33,7 @@ App.Presenters.TopNavigationPresenter = function(
$el.html(template({
loggedIn: appState.get('loggedIn'),
user: appState.get('loggedInUser'),
+ privileges: appState.get('privileges'),
}));
$el.find('li.' + selectedElement).addClass('active');
};
diff --git a/public_html/js/Presenters/UserListPresenter.js b/public_html/js/Presenters/UserListPresenter.js
index b1892dbb..756e88a7 100644
--- a/public_html/js/Presenters/UserListPresenter.js
+++ b/public_html/js/Presenters/UserListPresenter.js
@@ -7,7 +7,8 @@ App.Presenters.UserListPresenter = function(
promise,
router,
pagedCollectionPresenter,
- topNavigationPresenter) {
+ topNavigationPresenter,
+ messagePresenter) {
var $el = jQuery('#content');
var template;
@@ -28,6 +29,10 @@ App.Presenters.UserListPresenter = function(
renderCallback: function updateCollection(data) {
userList = data.entities;
render();
+ },
+ failCallback: function(response) {
+ $el.empty();
+ messagePresenter.showError($el, response.json && response.json.error || response);
}});
});
}
diff --git a/public_html/js/Router.js b/public_html/js/Router.js
index fafea7e6..3d843eff 100644
--- a/public_html/js/Router.js
+++ b/public_html/js/Router.js
@@ -1,6 +1,6 @@
var App = App || {};
-App.Router = function(jQuery, util) {
+App.Router = function(jQuery, util, appState) {
var root = '#/';
diff --git a/public_html/templates/top-navigation.tpl b/public_html/templates/top-navigation.tpl
index 4f14628d..8914e6a5 100644
--- a/public_html/templates/top-navigation.tpl
+++ b/public_html/templates/top-navigation.tpl
@@ -1,8 +1,10 @@
- -
- Users
-
+ <% if (_.contains(privileges, 'listUsers')) { %>
+ -
+ Users
+
+ <% } %>
<% if (!loggedIn) { %>
-
Login
diff --git a/src/Controllers/AuthController.php b/src/Controllers/AuthController.php
index 9ad45e1c..0a14f615 100644
--- a/src/Controllers/AuthController.php
+++ b/src/Controllers/AuthController.php
@@ -45,6 +45,7 @@ final class AuthController extends AbstractController
[
'token' => new \Szurubooru\ViewProxies\Token($this->authService->getLoginToken()),
'user' => new \Szurubooru\ViewProxies\User($this->authService->getLoggedInUser()),
+ 'privileges' => $this->authService->getCurrentPrivileges(),
];
}
}
diff --git a/src/Controllers/UserController.php b/src/Controllers/UserController.php
index 528c81ca..3497c8c1 100644
--- a/src/Controllers/UserController.php
+++ b/src/Controllers/UserController.php
@@ -3,15 +3,18 @@ namespace Szurubooru\Controllers;
final class UserController extends AbstractController
{
- private $inputReader;
+ private $authService;
private $userService;
+ private $inputReader;
public function __construct(
+ \Szurubooru\Services\AuthService $authService,
\Szurubooru\Services\UserService $userService,
\Szurubooru\Helpers\InputReader $inputReader)
{
- $this->inputReader = $inputReader;
+ $this->authService = $authService;
$this->userService = $userService;
+ $this->inputReader = $inputReader;
}
public function registerRoutes(\Szurubooru\Router $router)
@@ -25,7 +28,8 @@ final class UserController extends AbstractController
public function getFiltered()
{
- //todo: privilege checking
+ $this->authService->assertPrivilege(\Szurubooru\Privilege::PRIVILEGE_LIST_USERS);
+
//todo: move this to form data constructor
$searchFormData = new \Szurubooru\FormData\SearchFormData;
$searchFormData->query = $this->inputReader->query;
@@ -41,7 +45,8 @@ final class UserController extends AbstractController
public function getByName($name)
{
- //todo: privilege checking
+ $this->authService->assertPrivilege(\Szurubooru\Privilege::PRIVILEGE_VIEW_USER);
+
$user = $this->userService->getByName($name);
if (!$user)
throw new \DomainException('User with name "' . $name . '" was not found.');
@@ -50,7 +55,8 @@ final class UserController extends AbstractController
public function register()
{
- //todo: privilege checking
+ $this->authService->assertPrivilege(\Szurubooru\Privilege::PRIVILEGE_REGISTER);
+
$input = new \Szurubooru\FormData\RegistrationFormData;
//todo: move this to form data constructor
$input->name = $this->inputReader->userName;
diff --git a/src/Privilege.php b/src/Privilege.php
new file mode 100644
index 00000000..6649fc7f
--- /dev/null
+++ b/src/Privilege.php
@@ -0,0 +1,9 @@
+loggedInUser = $this->getAnonymousUser();
$this->validator = $validator;
+ $this->config = $config;
$this->passwordService = $passwordService;
$this->timeService = $timeService;
$this->tokenDao = $tokenDao;
@@ -103,6 +106,27 @@ class AuthService
$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)
{
$loginToken = new \Szurubooru\Entities\Token();
diff --git a/tests/Services/AuthServiceTest.php b/tests/Services/AuthServiceTest.php
index c3da4094..461c78c0 100644
--- a/tests/Services/AuthServiceTest.php
+++ b/tests/Services/AuthServiceTest.php
@@ -4,6 +4,7 @@ namespace Szurubooru\Tests\Services;
class AuthServiceTest extends \Szurubooru\Tests\AbstractTestCase
{
private $validatorMock;
+ private $configMock;
private $passwordServiceMock;
private $timeServiceMock;
private $tokenDaoMock;
@@ -12,6 +13,7 @@ class AuthServiceTest extends \Szurubooru\Tests\AbstractTestCase
public function setUp()
{
$this->validatorMock = $this->mock(\Szurubooru\Validator::class);
+ $this->configMock = $this->mock(\Szurubooru\Config::class);
$this->passwordServiceMock = $this->mock(\Szurubooru\Services\PasswordService::class);
$this->timeServiceMock = $this->mock(\Szurubooru\Services\TimeService::class);
$this->tokenDaoMock = $this->mock(\Szurubooru\Dao\TokenDao::class);
@@ -94,6 +96,7 @@ class AuthServiceTest extends \Szurubooru\Tests\AbstractTestCase
{
return new \Szurubooru\Services\AuthService(
$this->validatorMock,
+ $this->configMock,
$this->passwordServiceMock,
$this->timeServiceMock,
$this->tokenDaoMock,