mirror of
https://github.com/sismics/docs.git
synced 2024-11-25 15:17:57 +01:00
#85: Login as guest
This commit is contained in:
parent
ead01ce1d0
commit
d7865cfaf0
@ -29,7 +29,12 @@ public class Constants {
|
|||||||
* File Lucene directory storage.
|
* File Lucene directory storage.
|
||||||
*/
|
*/
|
||||||
public static final String LUCENE_DIRECTORY_STORAGE_FILE = "FILE";
|
public static final String LUCENE_DIRECTORY_STORAGE_FILE = "FILE";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guest user ID.
|
||||||
|
*/
|
||||||
|
public static final String GUEST_USER_ID = "guest";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default generic user role.
|
* Default generic user role.
|
||||||
*/
|
*/
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
insert into T_CONFIG(CFG_ID_C, CFG_VALUE_C) values('GUEST_LOGIN', 'false');
|
insert into T_CONFIG(CFG_ID_C, CFG_VALUE_C) values('GUEST_LOGIN', 'false');
|
||||||
|
insert into T_USER(USE_ID_C, USE_IDROLE_C, USE_USERNAME_C, USE_PASSWORD_C, USE_EMAIL_C, USE_CREATEDATE_D, USE_PRIVATEKEY_C) values('guest', 'user', 'guest', '', 'guest@localhost', NOW(), 'GuestPk');
|
||||||
update T_CONFIG set CFG_VALUE_C = '10' where CFG_ID_C = 'DB_VERSION';
|
update T_CONFIG set CFG_VALUE_C = '10' where CFG_ID_C = 'DB_VERSION';
|
||||||
|
@ -59,4 +59,9 @@ public class AnonymousPrincipal implements IPrincipal {
|
|||||||
public Set<String> getGroupIdSet() {
|
public Set<String> getGroupIdSet() {
|
||||||
return Sets.newHashSet();
|
return Sets.newHashSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGuest() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,13 @@ public interface IPrincipal extends Principal {
|
|||||||
*/
|
*/
|
||||||
boolean isAnonymous();
|
boolean isAnonymous();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the principal is a guest.
|
||||||
|
*
|
||||||
|
* @return True if the principal is a guest
|
||||||
|
*/
|
||||||
|
boolean isGuest();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ID of the connected user, or null if the user is anonymous
|
* Returns the ID of the connected user, or null if the user is anonymous
|
||||||
*
|
*
|
||||||
|
@ -2,6 +2,7 @@ package com.sismics.security;
|
|||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.sismics.docs.core.constant.Constants;
|
||||||
import org.joda.time.DateTimeZone;
|
import org.joda.time.DateTimeZone;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -108,4 +109,9 @@ public class UserPrincipal implements IPrincipal {
|
|||||||
public void setGroupIdSet(Set<String> groupIdSet) {
|
public void setGroupIdSet(Set<String> groupIdSet) {
|
||||||
this.groupIdSet = groupIdSet;
|
this.groupIdSet = groupIdSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGuest() {
|
||||||
|
return Constants.GUEST_USER_ID.equals(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ import javax.ws.rs.core.MediaType;
|
|||||||
import javax.ws.rs.core.NewCookie;
|
import javax.ws.rs.core.NewCookie;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import com.sismics.docs.core.constant.ConfigType;
|
||||||
|
import com.sismics.docs.core.util.ConfigUtil;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
@ -150,7 +152,7 @@ public class UserResource extends BaseResource {
|
|||||||
* @apiParam {String{8..50}} password Password
|
* @apiParam {String{8..50}} password Password
|
||||||
* @apiParam {String{1..100}} email E-mail
|
* @apiParam {String{1..100}} email E-mail
|
||||||
* @apiSuccess {String} status Status OK
|
* @apiSuccess {String} status Status OK
|
||||||
* @apiError (client) ForbiddenError Access denied
|
* @apiError (client) ForbiddenError Access denied or connected as guest
|
||||||
* @apiError (client) ValidationError Validation error
|
* @apiError (client) ValidationError Validation error
|
||||||
* @apiPermission user
|
* @apiPermission user
|
||||||
* @apiVersion 1.5.0
|
* @apiVersion 1.5.0
|
||||||
@ -163,7 +165,7 @@ public class UserResource extends BaseResource {
|
|||||||
public Response update(
|
public Response update(
|
||||||
@FormParam("password") String password,
|
@FormParam("password") String password,
|
||||||
@FormParam("email") String email) {
|
@FormParam("email") String email) {
|
||||||
if (!authenticate()) {
|
if (!authenticate() || principal.isGuest()) {
|
||||||
throw new ForbiddenClientException();
|
throw new ForbiddenClientException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,7 +303,7 @@ public class UserResource extends BaseResource {
|
|||||||
* @apiName PostUserLogin
|
* @apiName PostUserLogin
|
||||||
* @apiGroup User
|
* @apiGroup User
|
||||||
* @apiParam {String} username Username
|
* @apiParam {String} username Username
|
||||||
* @apiParam {String} password Password
|
* @apiParam {String} password Password (optional for guest login)
|
||||||
* @apiParam {String} code TOTP validation code
|
* @apiParam {String} code TOTP validation code
|
||||||
* @apiParam {Boolean} remember If true, create a long lasted token
|
* @apiParam {Boolean} remember If true, create a long lasted token
|
||||||
* @apiSuccess {String} auth_token A cookie named auth_token containing the token ID
|
* @apiSuccess {String} auth_token A cookie named auth_token containing the token ID
|
||||||
@ -328,7 +330,16 @@ public class UserResource extends BaseResource {
|
|||||||
|
|
||||||
// Get the user
|
// Get the user
|
||||||
UserDao userDao = new UserDao();
|
UserDao userDao = new UserDao();
|
||||||
User user = userDao.authenticate(username, password);
|
User user = null;
|
||||||
|
if (username.equals(Constants.GUEST_USER_ID)) {
|
||||||
|
if (ConfigUtil.getConfigBooleanValue(ConfigType.GUEST_LOGIN)) {
|
||||||
|
// Login as guest
|
||||||
|
user = userDao.getActiveByUsername(Constants.GUEST_USER_ID);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Login as a normal user
|
||||||
|
user = userDao.authenticate(username, password);
|
||||||
|
}
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new ForbiddenClientException();
|
throw new ForbiddenClientException();
|
||||||
}
|
}
|
||||||
@ -429,7 +440,7 @@ public class UserResource extends BaseResource {
|
|||||||
* @apiName DeleteUser
|
* @apiName DeleteUser
|
||||||
* @apiGroup User
|
* @apiGroup User
|
||||||
* @apiSuccess {String} status Status OK
|
* @apiSuccess {String} status Status OK
|
||||||
* @apiError (client) ForbiddenError Access denied or the admin user cannot be deleted
|
* @apiError (client) ForbiddenError Access denied or the user cannot be deleted
|
||||||
* @apiPermission user
|
* @apiPermission user
|
||||||
* @apiVersion 1.5.0
|
* @apiVersion 1.5.0
|
||||||
*
|
*
|
||||||
@ -442,8 +453,8 @@ public class UserResource extends BaseResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the admin user is not deleted
|
// Ensure that the admin user is not deleted
|
||||||
if (hasBaseFunction(BaseFunction.ADMIN)) {
|
if (hasBaseFunction(BaseFunction.ADMIN) || principal.isGuest()) {
|
||||||
throw new ClientException("ForbiddenError", "The admin user cannot be deleted");
|
throw new ClientException("ForbiddenError", "This user cannot be deleted");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find linked data
|
// Find linked data
|
||||||
@ -486,7 +497,7 @@ public class UserResource extends BaseResource {
|
|||||||
* @apiName DeleteUserUsername
|
* @apiName DeleteUserUsername
|
||||||
* @apiGroup User
|
* @apiGroup User
|
||||||
* @apiSuccess {String} status Status OK
|
* @apiSuccess {String} status Status OK
|
||||||
* @apiError (client) ForbiddenError Access denied or the admin user cannot be deleted
|
* @apiError (client) ForbiddenError Access denied or the user cannot be deleted
|
||||||
* @apiError (client) UserNotFound The user does not exist
|
* @apiError (client) UserNotFound The user does not exist
|
||||||
* @apiPermission admin
|
* @apiPermission admin
|
||||||
* @apiVersion 1.5.0
|
* @apiVersion 1.5.0
|
||||||
@ -501,7 +512,12 @@ public class UserResource extends BaseResource {
|
|||||||
throw new ForbiddenClientException();
|
throw new ForbiddenClientException();
|
||||||
}
|
}
|
||||||
checkBaseFunction(BaseFunction.ADMIN);
|
checkBaseFunction(BaseFunction.ADMIN);
|
||||||
|
|
||||||
|
// Cannot delete the guest user
|
||||||
|
if (Constants.GUEST_USER_ID.equals(username)) {
|
||||||
|
throw new ClientException("ForbiddenError", "The guest user cannot be deleted");
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the user exists
|
// Check if the user exists
|
||||||
UserDao userDao = new UserDao();
|
UserDao userDao = new UserDao();
|
||||||
User user = userDao.getActiveByUsername(username);
|
User user = userDao.getActiveByUsername(username);
|
||||||
@ -768,18 +784,21 @@ public class UserResource extends BaseResource {
|
|||||||
String authToken = getAuthToken();
|
String authToken = getAuthToken();
|
||||||
|
|
||||||
JsonArrayBuilder sessions = Json.createArrayBuilder();
|
JsonArrayBuilder sessions = Json.createArrayBuilder();
|
||||||
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
|
|
||||||
|
|
||||||
for (AuthenticationToken authenticationToken : authenticationTokenDao.getByUserId(principal.getId())) {
|
// The guest user cannot see other sessions
|
||||||
JsonObjectBuilder session = Json.createObjectBuilder()
|
if (!principal.isGuest()) {
|
||||||
.add("create_date", authenticationToken.getCreationDate().getTime())
|
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
|
||||||
.add("ip", JsonUtil.nullable(authenticationToken.getIp()))
|
for (AuthenticationToken authenticationToken : authenticationTokenDao.getByUserId(principal.getId())) {
|
||||||
.add("user_agent", JsonUtil.nullable(authenticationToken.getUserAgent()));
|
JsonObjectBuilder session = Json.createObjectBuilder()
|
||||||
if (authenticationToken.getLastConnectionDate() != null) {
|
.add("create_date", authenticationToken.getCreationDate().getTime())
|
||||||
session.add("last_connection_date", authenticationToken.getLastConnectionDate().getTime());
|
.add("ip", JsonUtil.nullable(authenticationToken.getIp()))
|
||||||
|
.add("user_agent", JsonUtil.nullable(authenticationToken.getUserAgent()));
|
||||||
|
if (authenticationToken.getLastConnectionDate() != null) {
|
||||||
|
session.add("last_connection_date", authenticationToken.getLastConnectionDate().getTime());
|
||||||
|
}
|
||||||
|
session.add("current", authenticationToken.getId().equals(authToken));
|
||||||
|
sessions.add(session);
|
||||||
}
|
}
|
||||||
session.add("current", authenticationToken.getId().equals(authToken));
|
|
||||||
sessions.add(session);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonObjectBuilder response = Json.createObjectBuilder()
|
JsonObjectBuilder response = Json.createObjectBuilder()
|
||||||
@ -795,7 +814,7 @@ public class UserResource extends BaseResource {
|
|||||||
* @apiName DeleteUserSession
|
* @apiName DeleteUserSession
|
||||||
* @apiGroup User
|
* @apiGroup User
|
||||||
* @apiSuccess {String} status Status OK
|
* @apiSuccess {String} status Status OK
|
||||||
* @apiError (client) ForbiddenError Access denied
|
* @apiError (client) ForbiddenError Access denied or connected as guest
|
||||||
* @apiPermission user
|
* @apiPermission user
|
||||||
* @apiVersion 1.5.0
|
* @apiVersion 1.5.0
|
||||||
*
|
*
|
||||||
@ -804,10 +823,10 @@ public class UserResource extends BaseResource {
|
|||||||
@DELETE
|
@DELETE
|
||||||
@Path("session")
|
@Path("session")
|
||||||
public Response deleteSession() {
|
public Response deleteSession() {
|
||||||
if (!authenticate()) {
|
if (!authenticate() || principal.isGuest()) {
|
||||||
throw new ForbiddenClientException();
|
throw new ForbiddenClientException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the value of the session token
|
// Get the value of the session token
|
||||||
String authToken = getAuthToken();
|
String authToken = getAuthToken();
|
||||||
|
|
||||||
@ -830,7 +849,7 @@ public class UserResource extends BaseResource {
|
|||||||
* @apiName PostUserEnableTotp
|
* @apiName PostUserEnableTotp
|
||||||
* @apiGroup User
|
* @apiGroup User
|
||||||
* @apiSuccess {String} secret Secret TOTP seed to initiate the algorithm
|
* @apiSuccess {String} secret Secret TOTP seed to initiate the algorithm
|
||||||
* @apiError (client) ForbiddenError Access denied
|
* @apiError (client) ForbiddenError Access denied or connected as guest
|
||||||
* @apiPermission user
|
* @apiPermission user
|
||||||
* @apiVersion 1.5.0
|
* @apiVersion 1.5.0
|
||||||
*
|
*
|
||||||
@ -839,7 +858,7 @@ public class UserResource extends BaseResource {
|
|||||||
@POST
|
@POST
|
||||||
@Path("enable_totp")
|
@Path("enable_totp")
|
||||||
public Response enableTotp() {
|
public Response enableTotp() {
|
||||||
if (!authenticate()) {
|
if (!authenticate() || principal.isGuest()) {
|
||||||
throw new ForbiddenClientException();
|
throw new ForbiddenClientException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -866,7 +885,7 @@ public class UserResource extends BaseResource {
|
|||||||
* @apiGroup User
|
* @apiGroup User
|
||||||
* @apiParam {String{1..100}} password Password
|
* @apiParam {String{1..100}} password Password
|
||||||
* @apiSuccess {String} status Status OK
|
* @apiSuccess {String} status Status OK
|
||||||
* @apiError (client) ForbiddenError Access denied
|
* @apiError (client) ForbiddenError Access denied or connected as guest
|
||||||
* @apiError (client) ValidationError Validation error
|
* @apiError (client) ValidationError Validation error
|
||||||
* @apiPermission user
|
* @apiPermission user
|
||||||
* @apiVersion 1.5.0
|
* @apiVersion 1.5.0
|
||||||
@ -877,7 +896,7 @@ public class UserResource extends BaseResource {
|
|||||||
@POST
|
@POST
|
||||||
@Path("disable_totp")
|
@Path("disable_totp")
|
||||||
public Response disableTotp(@FormParam("password") String password) {
|
public Response disableTotp(@FormParam("password") String password) {
|
||||||
if (!authenticate()) {
|
if (!authenticate() || principal.isGuest()) {
|
||||||
throw new ForbiddenClientException();
|
throw new ForbiddenClientException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
package com.sismics.docs.rest;
|
package com.sismics.docs.rest;
|
||||||
|
|
||||||
|
import com.sismics.docs.core.constant.PermType;
|
||||||
|
import com.sismics.docs.core.dao.jpa.AclDao;
|
||||||
|
import com.sismics.util.context.ThreadLocalContext;
|
||||||
|
import com.sismics.util.filter.TokenBasedSecurityFilter;
|
||||||
|
import com.sismics.util.jpa.EMF;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import javax.json.JsonArray;
|
import javax.json.JsonArray;
|
||||||
import javax.json.JsonObject;
|
import javax.json.JsonObject;
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
@ -9,15 +17,6 @@ import javax.ws.rs.core.Form;
|
|||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
|
||||||
import com.sismics.docs.core.constant.PermType;
|
|
||||||
import com.sismics.docs.core.dao.jpa.AclDao;
|
|
||||||
import com.sismics.util.context.ThreadLocalContext;
|
|
||||||
import com.sismics.util.jpa.EMF;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import com.sismics.util.filter.TokenBasedSecurityFilter;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the app resource.
|
* Test the app resource.
|
||||||
@ -134,9 +133,10 @@ public class TestAppResource extends BaseJerseyTest {
|
|||||||
// Login admin
|
// Login admin
|
||||||
String adminToken = clientUtil.login("admin", "admin", false);
|
String adminToken = clientUtil.login("admin", "admin", false);
|
||||||
|
|
||||||
// Try to login without credentials
|
// Try to login as guest
|
||||||
Response response = target().path("/user/login").request()
|
Response response = target().path("/user/login").request()
|
||||||
.post(Entity.form(new Form()));
|
.post(Entity.form(new Form()
|
||||||
|
.param("username", "guest")));
|
||||||
Assert.assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus());
|
Assert.assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus());
|
||||||
|
|
||||||
// Enable guest login
|
// Enable guest login
|
||||||
@ -144,5 +144,49 @@ public class TestAppResource extends BaseJerseyTest {
|
|||||||
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
|
||||||
.post(Entity.form(new Form()
|
.post(Entity.form(new Form()
|
||||||
.param("enabled", "true")), JsonObject.class);
|
.param("enabled", "true")), JsonObject.class);
|
||||||
|
|
||||||
|
// Login as guest
|
||||||
|
String guestToken = clientUtil.login("guest", "", false);
|
||||||
|
|
||||||
|
// Guest cannot delete himself
|
||||||
|
response = target().path("/user").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, guestToken)
|
||||||
|
.delete();
|
||||||
|
Assert.assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
|
||||||
|
|
||||||
|
// Guest cannot see opened sessions
|
||||||
|
JsonObject json = target().path("/user/session").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, guestToken)
|
||||||
|
.get(JsonObject.class);
|
||||||
|
Assert.assertEquals(0, json.getJsonArray("sessions").size());
|
||||||
|
|
||||||
|
// Guest cannot delete opened sessions
|
||||||
|
response = target().path("/user/session").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, guestToken)
|
||||||
|
.delete();
|
||||||
|
Assert.assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus());
|
||||||
|
|
||||||
|
// Guest cannot enable TOTP
|
||||||
|
response = target().path("/user/enable_totp").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, guestToken)
|
||||||
|
.post(Entity.form(new Form()));
|
||||||
|
Assert.assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus());
|
||||||
|
|
||||||
|
// Guest cannot disable TOTP
|
||||||
|
response = target().path("/user/disable_totp").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, guestToken)
|
||||||
|
.post(Entity.form(new Form()));
|
||||||
|
Assert.assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus());
|
||||||
|
|
||||||
|
// Guest cannot update itself
|
||||||
|
response = target().path("/user").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, guestToken)
|
||||||
|
.post(Entity.form(new Form()));
|
||||||
|
Assert.assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus());
|
||||||
|
|
||||||
|
// Guest can see its documents
|
||||||
|
target().path("/document/list").request()
|
||||||
|
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, guestToken)
|
||||||
|
.get(JsonObject.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user