mirror of
https://github.com/sismics/docs.git
synced 2024-11-22 05:57:57 +01:00
Closes #167: disable users
This commit is contained in:
parent
fb75bafe96
commit
d786862a60
@ -10,6 +10,7 @@ import com.sismics.docs.core.util.jpa.QueryParam;
|
||||
import com.sismics.docs.core.util.jpa.QueryUtil;
|
||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||
import com.sismics.util.context.ThreadLocalContext;
|
||||
import org.joda.time.DateTime;
|
||||
import org.mindrot.jbcrypt.BCrypt;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
@ -37,7 +38,7 @@ public class UserDao {
|
||||
q.setParameter("username", username);
|
||||
try {
|
||||
User user = (User) q.getSingleResult();
|
||||
if (!BCrypt.checkpw(password, user.getPassword())) {
|
||||
if (!BCrypt.checkpw(password, user.getPassword()) || user.getDisableDate() != null) {
|
||||
return null;
|
||||
}
|
||||
return user;
|
||||
@ -98,6 +99,7 @@ public class UserDao {
|
||||
userFromDb.setStorageQuota(user.getStorageQuota());
|
||||
userFromDb.setStorageCurrent(user.getStorageCurrent());
|
||||
userFromDb.setTotpKey(user.getTotpKey());
|
||||
userFromDb.setDisableDate(user.getDisableDate());
|
||||
|
||||
// Create audit log
|
||||
AuditLogUtil.create(userFromDb, AuditLogType.UPDATE, userId);
|
||||
@ -246,7 +248,7 @@ public class UserDao {
|
||||
Map<String, Object> parameterMap = new HashMap<>();
|
||||
List<String> criteriaList = new ArrayList<>();
|
||||
|
||||
StringBuilder sb = new StringBuilder("select u.USE_ID_C as c0, u.USE_USERNAME_C as c1, u.USE_EMAIL_C as c2, u.USE_CREATEDATE_D as c3, u.USE_STORAGECURRENT_N as c4, u.USE_STORAGEQUOTA_N as c5, u.USE_TOTPKEY_C as c6");
|
||||
StringBuilder sb = new StringBuilder("select u.USE_ID_C as c0, u.USE_USERNAME_C as c1, u.USE_EMAIL_C as c2, u.USE_CREATEDATE_D as c3, u.USE_STORAGECURRENT_N as c4, u.USE_STORAGEQUOTA_N as c5, u.USE_TOTPKEY_C as c6, u.USE_DISABLEDATE_D as c7");
|
||||
sb.append(" from T_USER u ");
|
||||
|
||||
// Add search criterias
|
||||
@ -283,7 +285,10 @@ public class UserDao {
|
||||
userDto.setCreateTimestamp(((Timestamp) o[i++]).getTime());
|
||||
userDto.setStorageCurrent(((Number) o[i++]).longValue());
|
||||
userDto.setStorageQuota(((Number) o[i++]).longValue());
|
||||
userDto.setTotpKey((String) o[i]);
|
||||
userDto.setTotpKey((String) o[i++]);
|
||||
if (o[i] != null) {
|
||||
userDto.setDisableTimestamp(((Timestamp) o[i]).getTime());
|
||||
}
|
||||
userDtoList.add(userDto);
|
||||
}
|
||||
return userDtoList;
|
||||
@ -299,4 +304,16 @@ public class UserDao {
|
||||
Query query = em.createNativeQuery("select sum(u.USE_STORAGECURRENT_N) from T_USER u where u.USE_DELETEDATE_D is null");
|
||||
return ((Number) query.getSingleResult()).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of active users.
|
||||
*
|
||||
* @return Number of active users
|
||||
*/
|
||||
public long getActiveUserCount() {
|
||||
EntityManager em = ThreadLocalContext.get().getEntityManager();
|
||||
Query query = em.createNativeQuery("select count(u.USE_ID_C) from T_USER u where u.USE_DELETEDATE_D is null and (u.USE_DISABLEDATE_D is null or u.USE_DISABLEDATE_D > :date)");
|
||||
query.setParameter("date", DateTime.now().minusMonths(1).toDate());
|
||||
return ((Number) query.getSingleResult()).longValue();
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,11 @@ public class UserDto {
|
||||
*/
|
||||
private Long createTimestamp;
|
||||
|
||||
/**
|
||||
* Disable date of this user.
|
||||
*/
|
||||
private Long disableTimestamp;
|
||||
|
||||
/**
|
||||
* Storage quota.
|
||||
*/
|
||||
@ -73,6 +78,15 @@ public class UserDto {
|
||||
this.createTimestamp = createTimestamp;
|
||||
}
|
||||
|
||||
public Long getDisableTimestamp() {
|
||||
return disableTimestamp;
|
||||
}
|
||||
|
||||
public UserDto setDisableTimestamp(Long disableTimestamp) {
|
||||
this.disableTimestamp = disableTimestamp;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Long getStorageQuota() {
|
||||
return storageQuota;
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
package com.sismics.docs.core.model.jpa;
|
||||
|
||||
import java.util.Date;
|
||||
import com.google.common.base.MoreObjects;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* User entity.
|
||||
@ -84,6 +83,12 @@ public class User implements Loggable {
|
||||
@Column(name = "USE_DELETEDATE_D")
|
||||
private Date deleteDate;
|
||||
|
||||
/**
|
||||
* Disable date.
|
||||
*/
|
||||
@Column(name = "USE_DISABLEDATE_D")
|
||||
private Date disableDate;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
@ -148,6 +153,15 @@ public class User implements Loggable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Date getDisableDate() {
|
||||
return disableDate;
|
||||
}
|
||||
|
||||
public User setDisableDate(Date disableDate) {
|
||||
this.disableDate = disableDate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPrivateKey() {
|
||||
return privateKey;
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
db.version=13
|
||||
db.version=14
|
@ -0,0 +1,2 @@
|
||||
alter table T_USER add column USE_DISABLEDATE_D datetime;
|
||||
update T_CONFIG set CFG_VALUE_C = '14' where CFG_ID_C = 'DB_VERSION';
|
@ -57,7 +57,7 @@ public abstract class SecurityFilter implements Filter {
|
||||
*/
|
||||
private void injectUser(HttpServletRequest request, User user) {
|
||||
// Check if the user is still valid
|
||||
if (user != null && user.getDeleteDate() == null) {
|
||||
if (user != null && user.getDeleteDate() == null && user.getDisableDate() == null) {
|
||||
injectAuthenticatedUser(request, user);
|
||||
} else {
|
||||
injectAnonymousUser(request);
|
||||
|
@ -1,3 +1,3 @@
|
||||
api.current_version=${project.version}
|
||||
api.min_version=1.0
|
||||
db.version=13
|
||||
db.version=14
|
@ -63,6 +63,7 @@ public class AppResource extends BaseResource {
|
||||
* @apiSuccess {Boolean} guest_login True if guest login is enabled
|
||||
* @apiSuccess {String} total_memory Allocated JVM memory (in bytes)
|
||||
* @apiSuccess {String} free_memory Free JVM memory (in bytes)
|
||||
* @apiSuccess {String} active_user_count Number of active users
|
||||
* @apiSuccess {String} global_storage_current Global storage currently used (in bytes)
|
||||
* @apiSuccess {String} global_storage_quota Maximum global storage (in bytes)
|
||||
* @apiPermission none
|
||||
@ -89,6 +90,7 @@ public class AppResource extends BaseResource {
|
||||
.add("guest_login", guestLogin)
|
||||
.add("total_memory", Runtime.getRuntime().totalMemory())
|
||||
.add("free_memory", Runtime.getRuntime().freeMemory())
|
||||
.add("active_user_count", userDao.getActiveUserCount())
|
||||
.add("global_storage_current", userDao.getGlobalStorageCurrent());
|
||||
if (globalQuota > 0) {
|
||||
response.add("global_storage_quota", globalQuota);
|
||||
|
@ -186,6 +186,7 @@ public class UserResource extends BaseResource {
|
||||
* @apiParam {String{8..50}} password Password
|
||||
* @apiParam {String{1..100}} email E-mail
|
||||
* @apiParam {Number} storage_quota Storage quota (in bytes)
|
||||
* @apiParam {Boolean} disabled Disabled status
|
||||
* @apiSuccess {String} status Status OK
|
||||
* @apiError (client) ForbiddenError Access denied
|
||||
* @apiError (client) ValidationError Validation error
|
||||
@ -204,7 +205,8 @@ public class UserResource extends BaseResource {
|
||||
@PathParam("username") String username,
|
||||
@FormParam("password") String password,
|
||||
@FormParam("email") String email,
|
||||
@FormParam("storage_quota") String storageQuotaStr) {
|
||||
@FormParam("storage_quota") String storageQuotaStr,
|
||||
@FormParam("disabled") Boolean disabled) {
|
||||
if (!authenticate()) {
|
||||
throw new ForbiddenClientException();
|
||||
}
|
||||
@ -218,7 +220,7 @@ public class UserResource extends BaseResource {
|
||||
UserDao userDao = new UserDao();
|
||||
User user = userDao.getActiveByUsername(username);
|
||||
if (user == null) {
|
||||
throw new ClientException("UserNotFound", "The user doesn't exist");
|
||||
throw new ClientException("UserNotFound", "The user does not exist");
|
||||
}
|
||||
|
||||
// Update the user
|
||||
@ -229,6 +231,22 @@ public class UserResource extends BaseResource {
|
||||
Long storageQuota = ValidationUtil.validateLong(storageQuotaStr, "storage_quota");
|
||||
user.setStorageQuota(storageQuota);
|
||||
}
|
||||
if (disabled != null) {
|
||||
// Cannot disable the admin user or the guest user
|
||||
RoleBaseFunctionDao userBaseFuction = new RoleBaseFunctionDao();
|
||||
Set<String> baseFunctionSet = userBaseFuction.findByRoleId(Sets.newHashSet(user.getRoleId()));
|
||||
if (Constants.GUEST_USER_ID.equals(username) || baseFunctionSet.contains(BaseFunction.ADMIN.name())) {
|
||||
disabled = false;
|
||||
}
|
||||
|
||||
if (disabled && user.getDisableDate() == null) {
|
||||
// Recording the disabled date
|
||||
user.setDisableDate(new Date());
|
||||
} else if (!disabled && user.getDisableDate() != null) {
|
||||
// Emptying the disabled date
|
||||
user.setDisableDate(null);
|
||||
}
|
||||
}
|
||||
user = userDao.update(user, principal.getId());
|
||||
|
||||
// Change the password
|
||||
@ -631,6 +649,7 @@ public class UserResource extends BaseResource {
|
||||
* @apiSuccess {Number} storage_quota Storage quota (in bytes)
|
||||
* @apiSuccess {Number} storage_current Quota used (in bytes)
|
||||
* @apiSuccess {String[]} groups Groups
|
||||
* @apiSuccess {Boolean} disabled True if the user is disabled
|
||||
* @apiError (client) ForbiddenError Access denied
|
||||
* @apiError (client) UserNotFound The user does not exist
|
||||
* @apiPermission user
|
||||
@ -668,7 +687,8 @@ public class UserResource extends BaseResource {
|
||||
.add("groups", groups)
|
||||
.add("email", user.getEmail())
|
||||
.add("storage_quota", user.getStorageQuota())
|
||||
.add("storage_current", user.getStorageCurrent());
|
||||
.add("storage_current", user.getStorageCurrent())
|
||||
.add("disabled", user.getDisableDate() != null);
|
||||
return Response.ok().entity(response.build()).build();
|
||||
}
|
||||
|
||||
@ -688,6 +708,7 @@ public class UserResource extends BaseResource {
|
||||
* @apiSuccess {Number} users.storage_quota Storage quota (in bytes)
|
||||
* @apiSuccess {Number} users.storage_current Quota used (in bytes)
|
||||
* @apiSuccess {Number} users.create_date Create date (timestamp)
|
||||
* @apiSuccess {Number} users.disabled True if the user is disabled
|
||||
* @apiError (client) ForbiddenError Access denied
|
||||
* @apiPermission user
|
||||
* @apiVersion 1.5.0
|
||||
@ -730,7 +751,8 @@ public class UserResource extends BaseResource {
|
||||
.add("email", userDto.getEmail())
|
||||
.add("storage_quota", userDto.getStorageQuota())
|
||||
.add("storage_current", userDto.getStorageCurrent())
|
||||
.add("create_date", userDto.getCreateTimestamp()));
|
||||
.add("create_date", userDto.getCreateTimestamp())
|
||||
.add("disabled", userDto.getDisableTimestamp() != null));
|
||||
}
|
||||
|
||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||
|
@ -245,7 +245,8 @@
|
||||
"storage_quota": "Storage quota",
|
||||
"storage_quota_placeholder": "Storage quota (in MB)",
|
||||
"password": "Password",
|
||||
"password_confirm": "Password (confirm)"
|
||||
"password_confirm": "Password (confirm)",
|
||||
"disabled": "Disabled user"
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
|
@ -88,12 +88,23 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="isEdit() && user.username != 'admin' && user.username != 'guest'">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="checkbox text-danger">
|
||||
<label>
|
||||
<input name="disabled" type="checkbox" ng-model="user.disabled" />
|
||||
<strong>{{ 'settings.user.edit.disabled' | translate }}</strong>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button type="submit" class="btn btn-primary" ng-click="edit()" ng-disabled="!editUserForm.$valid">
|
||||
<span class="glyphicon glyphicon-pencil"></span> {{ isEdit() ? 'edit' : 'add' | translate }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" ng-click="remove()" ng-show="isEdit() && user.username != 'guest'">
|
||||
<button type="button" class="btn btn-danger" ng-click="remove()" ng-show="isEdit() && user.username != 'admin' && user.username != 'guest'">
|
||||
<span class="glyphicon glyphicon-trash"></span> {{ 'delete' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
@ -16,7 +16,8 @@
|
||||
<tr ng-repeat="user in users | orderBy: 'username'" ng-click="editUser(user)"
|
||||
ng-class="{ active: $stateParams.username == user.username }">
|
||||
<td>
|
||||
{{ user.username }}
|
||||
<span ng-if="!user.disabled">{{ user.username }}</span>
|
||||
<s ng-if="user.disabled">{{ user.username }}</s>
|
||||
<span class="glyphicon glyphicon-lock" ng-show="user.totp_enabled" uib-tooltip="{{ 'settings.user.totp_enabled' | translate }}"></span>
|
||||
</td>
|
||||
<td>{{ user.create_date | date: dateFormat }}</td>
|
||||
|
@ -1,3 +1,3 @@
|
||||
api.current_version=${project.version}
|
||||
api.min_version=1.0
|
||||
db.version=13
|
||||
db.version=14
|
@ -1,3 +1,3 @@
|
||||
api.current_version=${project.version}
|
||||
api.min_version=1.0
|
||||
db.version=13
|
||||
db.version=14
|
@ -37,6 +37,7 @@ public class TestAppResource extends BaseJerseyTest {
|
||||
Assert.assertTrue(totalMemory > 0 && totalMemory > freeMemory);
|
||||
Assert.assertFalse(json.getBoolean("guest_login"));
|
||||
Assert.assertTrue(json.containsKey("global_storage_current"));
|
||||
Assert.assertTrue(json.getJsonNumber("active_user_count").longValue() > 0);
|
||||
|
||||
// Rebuild Lucene index
|
||||
Response response = target().path("/app/batch/reindex").request()
|
||||
|
@ -57,6 +57,7 @@ public class TestUserResource extends BaseJerseyTest {
|
||||
Assert.assertNotNull(user.getJsonNumber("storage_current"));
|
||||
Assert.assertNotNull(user.getJsonNumber("create_date"));
|
||||
Assert.assertFalse(user.getBoolean("totp_enabled"));
|
||||
Assert.assertFalse(user.getBoolean("disabled"));
|
||||
|
||||
// Create a user KO (login length validation)
|
||||
Response response = target().path("/user").request()
|
||||
@ -262,7 +263,7 @@ public class TestUserResource extends BaseJerseyTest {
|
||||
Assert.assertEquals("newadminemail@docs.com", json.getString("email"));
|
||||
|
||||
// User admin update admin_user1 information
|
||||
json = target().path("/user").request()
|
||||
json = target().path("/user/admin_user1").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
.post(Entity.form(new Form()
|
||||
.param("email", " alice2@docs.com ")), JsonObject.class);
|
||||
@ -276,6 +277,36 @@ public class TestUserResource extends BaseJerseyTest {
|
||||
json = response.readEntity(JsonObject.class);
|
||||
Assert.assertEquals("ForbiddenError", json.getString("type"));
|
||||
|
||||
// User admin disable admin_user1
|
||||
json = target().path("/user/admin_user1").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
.post(Entity.form(new Form()
|
||||
.param("disabled", "true")), JsonObject.class);
|
||||
Assert.assertEquals("ok", json.getString("status"));
|
||||
|
||||
// User admin_user1 tries to authenticate
|
||||
response = target().path("/user/login").request()
|
||||
.post(Entity.form(new Form()
|
||||
.param("username", "admin_user1")
|
||||
.param("password", "12345678")
|
||||
.param("remember", "false")));
|
||||
Assert.assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus());
|
||||
|
||||
// User admin enable admin_user1
|
||||
json = target().path("/user/admin_user1").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
.post(Entity.form(new Form()
|
||||
.param("disabled", "false")), JsonObject.class);
|
||||
Assert.assertEquals("ok", json.getString("status"));
|
||||
|
||||
// User admin_user1 tries to authenticate
|
||||
response = target().path("/user/login").request()
|
||||
.post(Entity.form(new Form()
|
||||
.param("username", "admin_user1")
|
||||
.param("password", "12345678")
|
||||
.param("remember", "false")));
|
||||
Assert.assertEquals(Status.OK.getStatusCode(), response.getStatus());
|
||||
|
||||
// User admin deletes user admin_user1
|
||||
json = target().path("/user/admin_user1").request()
|
||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||
|
Loading…
Reference in New Issue
Block a user