de.svenkubiak.ninja.auth.services.Authentications Maven / Gradle / Ivy
package de.svenkubiak.ninja.auth.services;
import java.time.LocalDateTime;
import ninja.Context;
import ninja.Cookie;
import ninja.Cookie.Builder;
import ninja.utils.NinjaConstant;
import ninja.utils.NinjaProperties;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.mindrot.jbcrypt.BCrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import de.svenkubiak.ninja.auth.enums.Constants;
import de.svenkubiak.ninja.auth.utils.CryptoUtils;
/**
*
* @author svenkubiak
*
*/
@Singleton
public class Authentications {
private static final Logger LOG = LoggerFactory.getLogger(Authentications.class);
private static final String SEPARATOR = "####";
private static final int INDEX_0 = 0;
private static final int INDEX_1 = 1;
private static final int INDEX_2 = 2;
private static final int COOKIE_VARS = 3;
private static final int TWO_WEEKS_SECONDS = 1209600;
private static final int THIRTY_MINUTES = 30;
private static final int LOG_ROUNDS = 12;
private NinjaProperties ninjaProperties;
private CryptoUtils cryptoUtils;
@Inject
public Authentications(CryptoUtils cryptoUtils, NinjaProperties ninjaProperties) {
this.cryptoUtils = cryptoUtils;
this.ninjaProperties = ninjaProperties;
}
/**
* Retrieves the username of the current authenticated user from the
* authentication cookie.
*
* @param context The current ninja context
*
* @return The username of the authenticated user or null if none is found
*/
public String getAuthenticatedUser(Context context) {
Preconditions.checkNotNull(context, "Valid context is required for getAuthenticatedUser");
Cookie cookie = context.getCookie(getCookieName());
if (cookie != null && StringUtils.isNotBlank(cookie.getValue())) {
String value = cookie.getValue();
if (ninjaProperties.getBooleanWithDefault(Constants.AUTH_COOKIE_ENCRYPT.get(), false)) {
value = cryptoUtils.decrypt(value);
}
String [] cookieValue = value.split(SEPARATOR);
if (cookieValue.length == COOKIE_VARS) {
String hash = cookieValue[INDEX_0];
String username = cookieValue[INDEX_1];
String expires = cookieValue[INDEX_2];
if (StringUtils.isNotBlank(hash) && LocalDateTime.now().isBefore(LocalDateTime.parse(expires)) && getHash(username, expires).equals(hash)) {
return username;
}
}
}
return null;
}
/**
* Generates a hash value for a given clear text password using JBCrypt
*
* @param password The clear text password to hash
*
* @return The hashed value
*/
public String getHashedPassword(String password) {
Preconditions.checkNotNull(password, "Password is required for getHashedPassword");
return BCrypt.hashpw(password, BCrypt.gensalt(LOG_ROUNDS));
}
/**
* Checks a clear text password against a previously hashed JBCrypt one. Use
* this method to check if a user has entered the correct password.
*
* @param password The clear text password
* @param hash The with {@link #getHashedPassword(String)} created hash value
*
* @return True if the password is valid, false otherwise
*/
public boolean authenticate(String password, String hash) {
Preconditions.checkNotNull(password, "Password is required for authenticate");
Preconditions.checkNotNull(password, "Hashed password is required for authenticate");
boolean authenticated = false;
try {
authenticated = BCrypt.checkpw(password, hash);
} catch (IllegalArgumentException e) {
LOG.error("Failed to check password against hash", e);
}
return authenticated;
}
/**
* Performs a logout in the current context by removing the authentication cookie
*
* @param context The current ninja context
*/
public void logout(Context context) {
Preconditions.checkNotNull(context, "Valid context is required for logout");
if (context.hasCookie(getCookieName())) {
context.unsetCookie(Cookie.builder(context.getCookie(getCookieName())).build());
}
}
/**
* Return the name of the authentication cookie
*
* @return The name of the authentication cookie
*/
private String getCookieName() {
String prefix = ninjaProperties.get("application.cookie.prefix") + "-";
return prefix + ninjaProperties.getWithDefault(Constants.AUTH_COOKIE_NAME.get(), Constants.DEFAULT_COOKIE_SUFFIX.get());
}
/**
* Performs a login by putting a given username in the authentication cookie. By default
* the cookie is valid until the browser is closed or 30 minutes. If remember is passed as true, the
* cookie will get an expiration date which is by default two weeks. This can be customized
* via application.conf.
*
* VERY IMPORTANT: Only call this method if you are certain that the user is correctly authenticated!
*
* @param context The current ninja context
* @param username The username to login in
* @param remember If the user should stay logged in (default: two weeks)
*/
public void login(Context context, String username, boolean remember) {
Preconditions.checkNotNull(context, "Valid context is required for login");
Preconditions.checkNotNull(username, "Username is required for login");
if (StringUtils.isNotBlank(StringUtils.trimToNull(username))) {
String expires = getExpires(remember);
StringBuilder buffer = new StringBuilder();
buffer.append(getHash(username, expires))
.append(SEPARATOR)
.append(username)
.append(SEPARATOR)
.append(expires);
Builder builder = Cookie.builder(getCookieName(), getCookieValue(buffer.toString()))
.setDomain(ninjaProperties.get(NinjaConstant.applicationCookieDomain))
.setSecure(ninjaProperties.getBooleanWithDefault(NinjaConstant.sessionTransferredOverHttpsOnly, true))
.setHttpOnly(true);
if (remember) {
builder.setMaxAge(ninjaProperties.getIntegerWithDefault(Constants.AUTH_COOKIE_EXPIRES.get(), TWO_WEEKS_SECONDS));
}
context.addCookie(builder.build());
}
}
/**
* Returns the encrypted cookie value if encryption is enabled
*
* @param buffer The cookie value
* @return The encrypted cookie value of the cleartext value of the cookie
*/
private String getCookieValue(String buffer) {
Preconditions.checkNotNull(buffer, "Cookie value is required for getCookieValue");
if (ninjaProperties.getBooleanWithDefault(Constants.AUTH_COOKIE_ENCRYPT.get(), false)) {
return cryptoUtils.encrypt(buffer);
}
return buffer;
}
/**
* Return the string representation of a LocalDateTime of the expiration date of the
* authentication
*
* @param remember If the user wants to stay logged in, even if the browser is closed
* @return The configured AUTH_COOKIE_EXPIRES value or default TWO_WEEKS_SECONDS or 30 minutes in the future if remember = false
*/
private String getExpires(boolean remember) {
if (remember) {
return LocalDateTime.now().plusSeconds(ninjaProperties.getIntegerWithDefault(Constants.AUTH_COOKIE_EXPIRES.get(), TWO_WEEKS_SECONDS)).toString();
}
return LocalDateTime.now().plusMinutes(THIRTY_MINUTES).toString();
}
/**
* Creates a SHA-512 hash for a given username by hashing it with a given timestamp
* and the ninja application secret.
*
* @param username The username to create the hash
* @param timestamp The timestamp to create the hash
*
* @return The hashed value
*/
private String getHash(String username, String timestamp) {
Preconditions.checkNotNull(username, "Username is required for getSignature");
Preconditions.checkNotNull(timestamp, "Timestamp is required for getSignature");
return DigestUtils.sha512Hex(username + timestamp + ninjaProperties.get(Constants.APPLICATION_SECRET.get()));
}
/**
* Convenient function to check if there is an authenticated user in the
* authentication cookie
*
* @param context The current ninja context
* @return True if there is an authenticated user, false otherwise
*/
public boolean hasAuthenticatedUser(Context context) {
Preconditions.checkNotNull(context, "Valid context is required for hasAuthenticatedUser");
return StringUtils.isNotBlank(getAuthenticatedUser(context));
}
/**
* Convenient function to check if a given username is the authenticated user in the
* authentication cookie
*
* @param context The current ninja context
* @return True if there is an authenticated user with the given username, false otherwise
*/
public boolean isAuthenticated(Context context, String username) {
Preconditions.checkNotNull(context, "Valid context is required for isAuthenticated");
Preconditions.checkNotNull(username, "Username is required for isAuthenticated");
return username.equals(getAuthenticatedUser(context));
}
} © 2015 - 2025 Weber Informatics LLC | Privacy Policy