#85: Login as guest

This commit is contained in:
jendib 2016-05-29 16:37:26 +02:00
parent ead01ce1d0
commit d7865cfaf0
No known key found for this signature in database
GPG Key ID: 06EE7F699579166F
7 changed files with 125 additions and 38 deletions

View File

@ -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.
*/ */

View File

@ -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';

View File

@ -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;
}
} }

View File

@ -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
* *

View File

@ -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);
}
} }

View File

@ -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();
} }

View File

@ -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);
} }
} }