1
0
mirror of https://github.com/sismics/docs.git synced 2025-12-17 19:51:39 +00:00

#84: TOTP key generation and validation code checking on login

This commit is contained in:
jendib
2016-03-22 23:08:49 +01:00
parent 5f84da61c8
commit fb0bb62eaf
11 changed files with 118 additions and 44 deletions

View File

@@ -1,3 +1,3 @@
api.current_version=${project.version}
api.min_version=1.0
db.version=8
db.version=9

View File

@@ -255,6 +255,7 @@ public class UserResource extends BaseResource {
public Response login(
@FormParam("username") String username,
@FormParam("password") String password,
@FormParam("code") String validationCodeStr,
@FormParam("remember") boolean longLasted) {
// Validate the input data
username = StringUtils.strip(username);
@@ -262,10 +263,25 @@ public class UserResource extends BaseResource {
// Get the user
UserDao userDao = new UserDao();
String userId = userDao.authenticate(username, password);
if (userId == null) {
User user = userDao.authenticate(username, password);
if (user == null) {
throw new ForbiddenClientException();
}
// Two factor authentication
if (user.getTotpKey() != null) {
// If TOTP is enabled, ask a validation code
if (Strings.isNullOrEmpty(validationCodeStr)) {
throw new ClientException("ValidationCodeRequired", "An OTP validation code is required");
}
// Check the validation code
int validationCode = ValidationUtil.validateInteger(validationCodeStr, "code");
GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator();
if (!googleAuthenticator.authorize(user.getTotpKey(), validationCode)) {
throw new ForbiddenClientException();
}
}
// Get the remote IP
String ip = request.getHeader("x-forwarded-for");
@@ -275,15 +291,15 @@ public class UserResource extends BaseResource {
// Create a new session token
AuthenticationTokenDao authenticationTokenDao = new AuthenticationTokenDao();
AuthenticationToken authenticationToken = new AuthenticationToken();
authenticationToken.setUserId(userId);
authenticationToken.setLongLasted(longLasted);
authenticationToken.setIp(ip);
authenticationToken.setUserAgent(StringUtils.abbreviate(request.getHeader("user-agent"), 1000));
AuthenticationToken authenticationToken = new AuthenticationToken()
.setUserId(user.getId())
.setLongLasted(longLasted)
.setIp(ip)
.setUserAgent(StringUtils.abbreviate(request.getHeader("user-agent"), 1000));
String token = authenticationTokenDao.create(authenticationToken);
// Cleanup old session tokens
authenticationTokenDao.deleteOldSessionToken(userId);
authenticationTokenDao.deleteOldSessionToken(user.getId());
JsonObjectBuilder response = Json.createObjectBuilder();
int maxAge = longLasted ? TokenBasedSecurityFilter.TOKEN_LONG_LIFETIME : -1;
@@ -648,18 +664,18 @@ public class UserResource extends BaseResource {
throw new ForbiddenClientException();
}
// Create a new TOTP key and scratch codes
// Create a new TOTP key
GoogleAuthenticator gAuth = new GoogleAuthenticator();
final GoogleAuthenticatorKey key = gAuth.createCredentials();
JsonArrayBuilder scratchCodes = Json.createArrayBuilder();
for (int scratchCode : key.getScratchCodes()) {
scratchCodes.add(scratchCode);
}
// Save it
UserDao userDao = new UserDao();
User user = userDao.getActiveByUsername(principal.getName());
user.setTotpKey(key.getKey());
user = userDao.update(user, principal.getId());
JsonObjectBuilder response = Json.createObjectBuilder()
.add("secret", key.getKey())
.add("scratch_codes", scratchCodes);
.add("secret", key.getKey());
return Response.ok().entity(response.build()).build();
}

View File

@@ -1,3 +1,3 @@
api.current_version=${project.version}
api.min_version=1.0
db.version=8
db.version=9

View File

@@ -1,3 +1,3 @@
api.current_version=${project.version}
api.min_version=1.0
db.version=8
db.version=9

View File

@@ -1,5 +1,6 @@
package com.sismics.docs.rest;
import java.util.Date;
import java.util.Locale;
import javax.json.JsonArray;
@@ -13,6 +14,7 @@ import org.junit.Assert;
import org.junit.Test;
import com.sismics.util.filter.TokenBasedSecurityFilter;
import com.sismics.util.totp.GoogleAuthenticator;
/**
* Exhaustive test of the user resource.
@@ -299,5 +301,27 @@ public class TestUserResource extends BaseJerseyTest {
.post(Entity.form(new Form()), JsonObject.class);
String secret = json.getString("secret");
Assert.assertNotNull(secret);
// Try to login with totp1 without a validation code
Response response = target().path("/user/login").request()
.post(Entity.form(new Form()
.param("username", "totp1")
.param("password", "12345678")
.param("remember", "false")));
Assert.assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
json = response.readEntity(JsonObject.class);
Assert.assertEquals("ValidationCodeRequired", json.getString("type"));
// Generate a OTP
GoogleAuthenticator googleAuthenticator = new GoogleAuthenticator();
int validationCode = googleAuthenticator.calculateCode(secret, new Date().getTime() / 30000);
// Login with totp1 with a validation code
json = target().path("/user/login").request()
.post(Entity.form(new Form()
.param("username", "totp1")
.param("password", "12345678")
.param("code", Integer.toString(validationCode))
.param("remember", "false")), JsonObject.class);
}
}