mirror of
https://github.com/sismics/docs.git
synced 2024-11-25 15:17:57 +01:00
Users administration (client)
This commit is contained in:
parent
990884137b
commit
c48eb7a0fe
@ -39,5 +39,5 @@ public class Constants {
|
|||||||
/**
|
/**
|
||||||
* Default generic user role.
|
* Default generic user role.
|
||||||
*/
|
*/
|
||||||
public static final String DEFAULT_USER_ROLE = "admin";
|
public static final String DEFAULT_USER_ROLE = "user";
|
||||||
}
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
db.version=2
|
db.version=3
|
@ -0,0 +1 @@
|
|||||||
|
insert into T_ROLE(ROL_ID_C, ROL_NAME_C, ROL_CREATEDATE_D) values('user', 'User', NOW());
|
@ -1 +1 @@
|
|||||||
- Users administration (client)
|
- Users deletion with confirmation (client)
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
api.current_version=${project.version}
|
api.current_version=${project.version}
|
||||||
api.min_version=1.0
|
api.min_version=1.0
|
||||||
db.version=2
|
db.version=3
|
@ -3,6 +3,7 @@ package com.sismics.docs.rest.resource;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.servlet.http.Cookie;
|
import javax.servlet.http.Cookie;
|
||||||
import javax.ws.rs.DELETE;
|
import javax.ws.rs.DELETE;
|
||||||
@ -25,6 +26,7 @@ import org.codehaus.jettison.json.JSONObject;
|
|||||||
|
|
||||||
import com.sismics.docs.core.constant.Constants;
|
import com.sismics.docs.core.constant.Constants;
|
||||||
import com.sismics.docs.core.dao.jpa.AuthenticationTokenDao;
|
import com.sismics.docs.core.dao.jpa.AuthenticationTokenDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.RoleBaseFunctionDao;
|
||||||
import com.sismics.docs.core.dao.jpa.UserDao;
|
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||||
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
import com.sismics.docs.core.dao.jpa.dto.UserDto;
|
||||||
import com.sismics.docs.core.model.jpa.AuthenticationToken;
|
import com.sismics.docs.core.model.jpa.AuthenticationToken;
|
||||||
@ -371,7 +373,9 @@ public class UserResource extends BaseResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the admin user is not deleted
|
// Ensure that the admin user is not deleted
|
||||||
// TODO Ensure it exists at least one user left
|
if (hasBaseFunction(BaseFunction.ADMIN)) {
|
||||||
|
throw new ClientException("ForbiddenError", "The admin user cannot be deleted");
|
||||||
|
}
|
||||||
|
|
||||||
// Delete the user
|
// Delete the user
|
||||||
UserDao userDao = new UserDao();
|
UserDao userDao = new UserDao();
|
||||||
@ -407,7 +411,11 @@ public class UserResource extends BaseResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the admin user is not deleted
|
// Ensure that the admin user is not deleted
|
||||||
// TODO Ensure it exists at least one user left
|
RoleBaseFunctionDao userBaseFuction = new RoleBaseFunctionDao();
|
||||||
|
Set<String> baseFunctionSet = userBaseFuction.findByRoleId(user.getRoleId());
|
||||||
|
if (baseFunctionSet.contains(BaseFunction.ADMIN.name())) {
|
||||||
|
throw new ClientException("ForbiddenError", "The admin user cannot be deleted");
|
||||||
|
}
|
||||||
|
|
||||||
// Delete the user
|
// Delete the user
|
||||||
userDao.delete(user.getUsername());
|
userDao.delete(user.getUsername());
|
||||||
|
@ -85,7 +85,16 @@ var App = angular.module('docs', ['ui.state', 'ui.bootstrap', 'ui.route', 'ui.ke
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.state('settings.user.edit', {
|
.state('settings.user.edit', {
|
||||||
url: '/:username',
|
url: '/edit/:username',
|
||||||
|
views: {
|
||||||
|
'user': {
|
||||||
|
templateUrl: 'partial/settings.user.edit.html',
|
||||||
|
controller: 'SettingsUserEdit'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.state('settings.user.add', {
|
||||||
|
url: '/add',
|
||||||
views: {
|
views: {
|
||||||
'user': {
|
'user': {
|
||||||
templateUrl: 'partial/settings.user.edit.html',
|
templateUrl: 'partial/settings.user.edit.html',
|
||||||
|
@ -4,10 +4,16 @@
|
|||||||
* Settings user page controller.
|
* Settings user page controller.
|
||||||
*/
|
*/
|
||||||
App.controller('SettingsUser', function($scope, $state, Restangular) {
|
App.controller('SettingsUser', function($scope, $state, Restangular) {
|
||||||
// Load users from server
|
/**
|
||||||
Restangular.one('user/list').get({ limit: 100 }).then(function(data) {
|
* Load users from server.
|
||||||
$scope.users = data.users;
|
*/
|
||||||
});
|
$scope.loadUsers = function() {
|
||||||
|
Restangular.one('user/list').get({ limit: 100 }).then(function(data) {
|
||||||
|
$scope.users = data.users;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.loadUsers();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edit a user.
|
* Edit a user.
|
||||||
|
@ -3,6 +3,39 @@
|
|||||||
/**
|
/**
|
||||||
* Settings user edition page controller.
|
* Settings user edition page controller.
|
||||||
*/
|
*/
|
||||||
App.controller('SettingsUserEdit', function($scope, $stateParams, Restangular) {
|
App.controller('SettingsUserEdit', function($scope, $state, $stateParams, Restangular) {
|
||||||
$scope.user = Restangular.one('user', $stateParams.username).get();
|
/**
|
||||||
|
* Returns true if in edit mode (false in add mode).
|
||||||
|
*/
|
||||||
|
$scope.isEdit = function() {
|
||||||
|
return $stateParams.username;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In edit mode, load the current user.
|
||||||
|
*/
|
||||||
|
if ($scope.isEdit()) {
|
||||||
|
Restangular.one('user', $stateParams.username).get().then(function(data) {
|
||||||
|
$scope.user = data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.edit = function() {
|
||||||
|
var promise = null;
|
||||||
|
|
||||||
|
if ($scope.isEdit()) {
|
||||||
|
promise = Restangular
|
||||||
|
.one('user', $stateParams.username)
|
||||||
|
.post('', $scope.user);
|
||||||
|
} else {
|
||||||
|
promise = Restangular
|
||||||
|
.one('user')
|
||||||
|
.put($scope.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.then(function() {
|
||||||
|
$scope.loadUsers();
|
||||||
|
$state.transitionTo('settings.user');
|
||||||
|
});
|
||||||
|
};
|
||||||
});
|
});
|
@ -1,13 +1,50 @@
|
|||||||
<h2>{{ user.username }}</h2>
|
<h2 ng-show="isEdit()">Edit <small>"{{ user.username }}"</small></h2>
|
||||||
<dl class="dl-horizontal">
|
<h2 ng-show="!isEdit()">Add <small>user</small></h2>
|
||||||
<dt>E-mail</dt>
|
<form class="form-horizontal" name="editUserForm" novalidate>
|
||||||
<dd>{{ user.email }}</dd>
|
<div class="control-group" ng-class="{ error: !editUserForm.username.$valid, success: editUserForm.username.$valid }">
|
||||||
</dl>
|
<label class="control-label" for="inputUsername">Username</label>
|
||||||
<dl class="dl-horizontal">
|
<div class="controls">
|
||||||
<dt>Theme</dt>
|
<input name="username" type="text" id="inputUsername" required ng-disabled="isEdit()"
|
||||||
<dd>{{ user.theme }}</dd>
|
ng-minlength="3" ng-maxlength="50" placeholder="Username" ng-model="user.username" />
|
||||||
</dl>
|
<span class="help-inline" ng-show="editUserForm.username.$error.required">Required</span>
|
||||||
<dl class="dl-horizontal">
|
<span class="help-inline" ng-show="editUserForm.username.$error.minlength">Too short</span>
|
||||||
<dt>Locale</dt>
|
<span class="help-inline" ng-show="editUserForm.username.$error.maxlength">Too long</span>
|
||||||
<dd>{{ user.locale }}</dd>
|
</div>
|
||||||
</dl>
|
</div>
|
||||||
|
<div class="control-group" ng-class="{ error: !editUserForm.email.$valid, success: editUserForm.email.$valid }">
|
||||||
|
<label class="control-label" for="inputEmail">E-mail</label>
|
||||||
|
<div class="controls">
|
||||||
|
<input name="email" type="email" id="inputEmail" required
|
||||||
|
ng-minlength="3" ng-maxlength="50" placeholder="E-mail" ng-model="user.email" />
|
||||||
|
<span class="help-inline" ng-show="editUserForm.email.$error.required">Required</span>
|
||||||
|
<span class="help-inline" ng-show="editUserForm.email.$error.email">Must be a valid e-mail</span>
|
||||||
|
<span class="help-inline" ng-show="editUserForm.email.$error.minlength">Too short</span>
|
||||||
|
<span class="help-inline" ng-show="editUserForm.email.$error.maxlength">Too long</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="control-group" ng-class="{ error: !editUserForm.password.$valid, success: editUserForm.password.$valid }">
|
||||||
|
<label class="control-label" for="inputPassword">Password</label>
|
||||||
|
<div class="controls">
|
||||||
|
<input name="password" type="password" id="inputPassword" ng-required="!isEdit()"
|
||||||
|
ng-minlength="8" ng-maxlength="50" placeholder="Password" ng-model="user.password" />
|
||||||
|
<span class="help-inline" ng-show="editUserForm.password.$error.required">Required</span>
|
||||||
|
<span class="help-inline" ng-show="editUserForm.password.$error.minlength">Too short</span>
|
||||||
|
<span class="help-inline" ng-show="editUserForm.password.$error.maxlength">Too long</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="control-group" ng-class="{ error: !editUserForm.passwordconfirm.$valid, success: editUserForm.passwordconfirm.$valid }">
|
||||||
|
<label class="control-label" for="inputPasswordConfirm">Password (confirm)</label>
|
||||||
|
<div class="controls">
|
||||||
|
<input name="passwordconfirm" type="password" id="inputPasswordConfirm" ng-required="!isEdit()"
|
||||||
|
ui-validate="'$value == user.password'" ui-validate-watch="'user.password'"
|
||||||
|
placeholder="Password (confirm)" ng-model="user.passwordconfirm" />
|
||||||
|
<span class="help-inline" ng-show="editUserForm.passwordconfirm.$error.validator">Password and password confirmation must match</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-actions">
|
||||||
|
<button type="submit" class="btn btn-primary" ng-click="edit()" ng-disabled="!editUserForm.$valid">
|
||||||
|
<span class="icon-pencil icon-white"></span> {{ isEdit() ? 'Edit' : 'Add' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<alert ng-repeat="alert in alerts" type="alert.type" close="closeAlert($index)">{{ alert.msg }}</alert>
|
@ -1,9 +1,9 @@
|
|||||||
<h1>Users <small>management</small></h1>
|
<h1>Users <small>management</small> <a class="btn btn-primary" href="#/settings/user/add">Add</a></h1>
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span4 well">
|
<div class="span4 well">
|
||||||
<table class="table table-striped table-hover">
|
<table class="table table-striped table-hover table-users">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Username</th>
|
<th>Username</th>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="user in users" ng-click="editUser(user)">
|
<tr ng-repeat="user in users | orderBy: 'username'" ng-click="editUser(user)">
|
||||||
<td>{{ user.username }}</td>
|
<td>{{ user.username }}</td>
|
||||||
<td>{{ user.create_date | date: 'yyyy-MM-dd' }}</td>
|
<td>{{ user.create_date | date: 'yyyy-MM-dd' }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -50,6 +50,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Users list
|
||||||
|
.table-users {
|
||||||
|
tbody tr {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Logs list
|
// Logs list
|
||||||
.table-logs {
|
.table-logs {
|
||||||
tbody tr td {
|
tbody tr td {
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
api.current_version=${project.version}
|
api.current_version=${project.version}
|
||||||
api.min_version=1.0
|
api.min_version=1.0
|
||||||
db.version=2
|
db.version=3
|
@ -238,7 +238,7 @@ public class TestUserResource extends BaseJerseyTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testUserResourceAdmin() throws JSONException {
|
public void testUserResourceAdmin() throws JSONException {
|
||||||
// Create admin_user1 user
|
// Create admin_user1 user
|
||||||
clientUtil.createUser("admin_user1");
|
clientUtil.createUser("admin_user1");
|
||||||
|
|
||||||
// Login admin
|
// Login admin
|
||||||
@ -275,14 +275,34 @@ public class TestUserResource extends BaseJerseyTest {
|
|||||||
userResource = resource().path("/user");
|
userResource = resource().path("/user");
|
||||||
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||||
postParams = new MultivaluedMapImpl();
|
postParams = new MultivaluedMapImpl();
|
||||||
postParams.add("email", " alice2@docs.com ");
|
postParams.add("email", " alice2@reader.com ");
|
||||||
postParams.add("theme", " default.less");
|
postParams.add("theme", " default.less");
|
||||||
postParams.add("locale", " en ");
|
postParams.add("locale", " en ");
|
||||||
|
postParams.add("display_title_web", true);
|
||||||
|
postParams.add("display_title_mobile", false);
|
||||||
|
postParams.add("display_unread_web", false);
|
||||||
|
postParams.add("display_unread_mobile", false);
|
||||||
response = userResource.post(ClientResponse.class, postParams);
|
response = userResource.post(ClientResponse.class, postParams);
|
||||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||||
json = response.getEntity(JSONObject.class);
|
json = response.getEntity(JSONObject.class);
|
||||||
Assert.assertEquals("ok", json.getString("status"));
|
Assert.assertEquals("ok", json.getString("status"));
|
||||||
|
|
||||||
|
// User admin deletes himself: forbidden
|
||||||
|
userResource = resource().path("/user");
|
||||||
|
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||||
|
response = userResource.delete(ClientResponse.class);
|
||||||
|
Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus()));
|
||||||
|
json = response.getEntity(JSONObject.class);
|
||||||
|
Assert.assertEquals("ForbiddenError", json.getString("type"));
|
||||||
|
|
||||||
|
// User admin deletes himself: forbidden
|
||||||
|
userResource = resource().path("/user/admin");
|
||||||
|
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||||
|
response = userResource.delete(ClientResponse.class);
|
||||||
|
Assert.assertEquals(Status.BAD_REQUEST, Status.fromStatusCode(response.getStatus()));
|
||||||
|
json = response.getEntity(JSONObject.class);
|
||||||
|
Assert.assertEquals("ForbiddenError", json.getString("type"));
|
||||||
|
|
||||||
// User admin deletes user admin_user1
|
// User admin deletes user admin_user1
|
||||||
userResource = resource().path("/user/admin_user1");
|
userResource = resource().path("/user/admin_user1");
|
||||||
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
userResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||||
|
Loading…
Reference in New Issue
Block a user