Merge pull request #73 from Asimq/develop

Moved password encryption from MD5 to a salted and secure hash - 1448
This commit is contained in:
Mike Conlon 2018-05-23 13:49:04 -04:00 committed by GitHub
commit 111c0a8ee5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 487 additions and 50 deletions

View file

@ -102,7 +102,7 @@ public class PolicyHelper {
String uri = user.getUri();
log.debug("userAccount is '" + uri + "'");
if (!auth.isCurrentPassword(user, password)) {
if (!auth.isCurrentPasswordArgon2(user, password)) {
log.debug(String.format("UNAUTHORIZED, password not accepted "
+ "for %s, account URI: %s", email, uri));
return false;

View file

@ -67,6 +67,7 @@ public class RootUserPolicy implements PolicyIface {
private ServletContext ctx;
private StartupStatus ss;
private UserAccountsDao uaDao;
private ConfigurationProperties cp;
private String configuredRootUser;
private boolean configuredRootUserExists;
private TreeSet<String> otherRootUsers;
@ -75,6 +76,7 @@ public class RootUserPolicy implements PolicyIface {
public void contextInitialized(ServletContextEvent sce) {
ctx = sce.getServletContext();
ss = StartupStatus.getBean(ctx);
cp = ConfigurationProperties.getBean(ctx);
try {
uaDao = ModelAccess.on(ctx).getWebappDaoFactory()
@ -148,8 +150,9 @@ public class RootUserPolicy implements PolicyIface {
ua.setEmailAddress(configuredRootUser);
ua.setFirstName("root");
ua.setLastName("user");
ua.setMd5Password(Authenticator
.applyMd5Encoding(ROOT_USER_INITIAL_PASSWORD));
ua.setArgon2Password(Authenticator.applyArgon2iEncoding(
ROOT_USER_INITIAL_PASSWORD));
ua.setMd5Password("");
ua.setPasswordChangeRequired(true);
ua.setStatus(Status.ACTIVE);
ua.setRootUser(true);

View file

@ -48,6 +48,7 @@ public class UserAccount {
private String firstName = ""; // Never null.
private String lastName = ""; // Never null.
private String argon2Password = ""; //Never null.
private String md5Password = ""; // Never null.
private String oldPassword = ""; // Never null.
private long passwordLinkExpires = 0L; // Never negative.
@ -104,6 +105,14 @@ public class UserAccount {
this.lastName = nonNull(lastName, "");
}
public String getArgon2Password() {
return argon2Password;
}
public void setArgon2Password(String argo2Password) {
this.argon2Password = nonNull(argo2Password, "");
}
public String getMd5Password() {
return md5Password;
}
@ -125,7 +134,7 @@ public class UserAccount {
}
public String getPasswordLinkExpiresHash() {
return limitStringLength(8, Authenticator.applyMd5Encoding(String
return limitStringLength(8, Authenticator.applyArgon2iEncoding(String
.valueOf(passwordLinkExpires)));
}
@ -236,6 +245,7 @@ public class UserAccount {
+ (", firstName=" + firstName) + (", lastName=" + lastName)
+ (", md5password=" + md5Password)
+ (", oldPassword=" + oldPassword)
+ (", argon2password=" + argon2Password)
+ (", passwordLinkExpires=" + passwordLinkExpires)
+ (", passwordChangeRequired=" + passwordChangeRequired)
+ (", externalAuthOnly=" + externalAuthOnly)

View file

@ -29,6 +29,9 @@ public class ConfigurationPropertiesSmokeTests implements
private static final String PROPERTY_LANGUAGE_SELECTABLE = "languages.selectableLocales";
private static final String PROPERTY_LANGUAGE_FORCE = "languages.forceLocale";
private static final String PROPERTY_LANGUAGE_FILTER = "RDFService.languageFilter";
private static final String PROPERTY_ARGON2_TIME = "argon2.time";
private static final String PROPERTY_ARGON2_MEMORY = "argon2.memory";
private static final String PROPERTY_ARGON2_PARALLELISM = "argon2.parallelism";
@Override
public void contextInitialized(ServletContextEvent sce) {
@ -39,6 +42,7 @@ public class ConfigurationPropertiesSmokeTests implements
checkDefaultNamespace(ctx, props, ss);
checkMultipleRPFs(ctx, props, ss);
checkLanguages(props, ss);
checkEncryptionParameters(props, ss);
}
/**
@ -149,6 +153,26 @@ public class ConfigurationPropertiesSmokeTests implements
}
}
/**
* Fail if there are no config properties for the Argon2 encryption.
*/
private void checkEncryptionParameters(ConfigurationProperties props,
StartupStatus ss) {
failIfNotPresent(props, ss, PROPERTY_ARGON2_TIME);
failIfNotPresent(props, ss, PROPERTY_ARGON2_MEMORY);
failIfNotPresent(props, ss, PROPERTY_ARGON2_PARALLELISM);
}
private void failIfNotPresent(ConfigurationProperties props,
StartupStatus ss, String name) {
String value = props.getProperty(name);
if (value == null || value.isEmpty()) {
ss.fatal(this, "runtime.properties does not contain a value for '"
+ name + "'");
return;
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// nothing to do at shutdown

View file

@ -37,7 +37,7 @@ public class UserAccountsSelector {
+ "PREFIX auth: <http://vitro.mannlib.cornell.edu/ns/vitro/authorization#> \n";
private static final String ALL_VARIABLES = "?uri ?email ?firstName "
+ "?lastName ?pwd ?expire ?count ?lastLogin ?status ?isRoot";
+ "?lastName ?md5pwd ?a2pwd ?expire ?count ?lastLogin ?status ?isRoot";
private static final String COUNT_VARIABLE = "?uri";
@ -158,7 +158,8 @@ public class UserAccountsSelector {
private String optionalClauses() {
return "OPTIONAL { ?uri auth:firstName ?firstName } \n"
+ " OPTIONAL { ?uri auth:lastName ?lastName } \n"
+ " OPTIONAL { ?uri auth:md5password ?pwd } \n"
+ " OPTIONAL { ?uri auth:md5password ?md5pwd } \n"
+ " OPTIONAL { ?uri auth:argon2password ?a2pwd } \n"
+ " OPTIONAL { ?uri auth:passwordChangeExpires ?expire } \n"
+ " OPTIONAL { ?uri auth:loginCount ?count } \n"
+ " OPTIONAL { ?uri auth:lastLoginTime ?lastLogin } \n"
@ -245,7 +246,8 @@ public class UserAccountsSelector {
user.setEmailAddress(solution.getLiteral("email").getString());
user.setFirstName(ifLiteralPresent(solution, "firstName", ""));
user.setLastName(ifLiteralPresent(solution, "lastName", ""));
user.setMd5Password(ifLiteralPresent(solution, "pwd", ""));
user.setMd5Password(ifLiteralPresent(solution, "md5pwd", ""));
user.setArgon2Password(ifLiteralPresent(solution, "a2pwd", ""));
user.setPasswordLinkExpires(ifLongPresent(solution, "expire", 0L));
user.setLoginCount(ifIntPresent(solution, "count", 0));
user.setLastLoginTime(ifLongPresent(solution, "lastLogin", 0));

View file

@ -198,8 +198,8 @@ public abstract class UserAccountsAddPageStrategy extends UserAccountsPage {
@Override
protected void setAdditionalProperties(UserAccount u) {
if (!page.isExternalAuthOnly()) {
u.setMd5Password(Authenticator
.applyMd5Encoding(initialPassword));
u.setArgon2Password(Authenticator.applyArgon2iEncoding(initialPassword));
u.setMd5Password("");
u.setPasswordChangeRequired(true);
}
u.setStatus(Status.ACTIVE);

View file

@ -194,7 +194,8 @@ public abstract class UserAccountsEditPageStrategy extends UserAccountsPage {
@Override
protected void setAdditionalProperties(UserAccount u) {
if (!page.isExternalAuthOnly() && !newPassword.isEmpty()) {
u.setMd5Password(Authenticator.applyMd5Encoding(newPassword));
u.setArgon2Password(Authenticator.applyArgon2iEncoding(newPassword));
u.setMd5Password("");
u.setPasswordChangeRequired(true);
}
}

View file

@ -33,14 +33,14 @@ public class UserAccountsCreatePasswordPage extends
}
public void createPassword() {
userAccount.setMd5Password(Authenticator.applyMd5Encoding(newPassword));
userAccount.setArgon2Password(Authenticator.applyArgon2iEncoding(newPassword));
userAccount.setMd5Password("");
userAccount.setPasswordLinkExpires(0L);
userAccount.setPasswordChangeRequired(false);
userAccount.setStatus(Status.ACTIVE);
userAccountsDao.updateUserAccount(userAccount);
log.debug("Set password on '" + userAccount.getEmailAddress()
+ "' to '" + newPassword + "'");
notifyUser();
}

View file

@ -155,8 +155,8 @@ public abstract class UserAccountsMyAccountPageStrategy extends
@Override
public void setAdditionalProperties(UserAccount userAccount) {
if (!newPassword.isEmpty() && !page.isExternalAuthOnly()) {
userAccount.setMd5Password(Authenticator
.applyMd5Encoding(newPassword));
userAccount.setArgon2Password(Authenticator.applyArgon2iEncoding(newPassword));
userAccount.setMd5Password("");
userAccount.setPasswordChangeRequired(false);
userAccount.setPasswordLinkExpires(0L);
}

View file

@ -33,7 +33,8 @@ public class UserAccountsResetPasswordPage extends UserAccountsPasswordBasePage
}
public void resetPassword() {
userAccount.setMd5Password(Authenticator.applyMd5Encoding(newPassword));
userAccount.setArgon2Password(Authenticator.applyArgon2iEncoding(newPassword));
userAccount.setMd5Password("");
userAccount.setPasswordLinkExpires(0L);
userAccount.setPasswordChangeRequired(false);
userAccount.setStatus(Status.ACTIVE);

View file

@ -54,7 +54,7 @@ public class VitroApiServlet extends HttpServlet {
+ "last names and a valid email address.");
}
if (!auth.isCurrentPassword(account, password)) {
if (!auth.isCurrentPasswordArgon2(account, password)) {
log.debug("Invalid: '" + email + "'/'" + password + "'");
throw new AuthException("email/password combination is not valid");
}

View file

@ -141,8 +141,12 @@ public class AdminLoginController extends FreemarkerHttpServlet {
}
private boolean newPasswordRequired() {
return auth.isCurrentPassword(userAccount, password)
&& (userAccount.isPasswordChangeRequired());
if(auth.md5HashIsNull(userAccount)) {
return auth.isCurrentPasswordArgon2(userAccount, password)
&& userAccount.isPasswordChangeRequired();
}
else
return auth.isCurrentPassword(userAccount, password); // MD5 password should be changed anyway
}
private boolean isPasswordValidLength(String pw) {
@ -151,8 +155,18 @@ public class AdminLoginController extends FreemarkerHttpServlet {
}
private boolean tryToLogin() {
if (!auth.isCurrentPassword(userAccount, password)) {
return false;
if(auth.md5HashIsNull(userAccount)) {
if (!auth.isCurrentPasswordArgon2(userAccount, password))
return false;
}
else {
if (!auth.isCurrentPassword(userAccount, password))
return false;
else {
userAccount.setPasswordChangeRequired(true);
userAccount.setMd5Password("");
}
}
try {

View file

@ -2,21 +2,23 @@
package edu.cornell.mannlib.vitro.webapp.controller.authenticate;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.codec.binary.Hex;
import de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Factory;
import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource;
import edu.cornell.mannlib.vitro.webapp.auth.identifier.ActiveIdentifierBundleFactories;
import edu.cornell.mannlib.vitro.webapp.auth.identifier.IdentifierBundle;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import org.apache.commons.codec.binary.Hex;
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
/**
* The tool that a login process will use to interface with the user records in
@ -55,6 +57,7 @@ public abstract class Authenticator {
*
* If there is no factory, configure a Basic one.
*/
public static Authenticator getInstance(HttpServletRequest request) {
ServletContext ctx = request.getSession().getServletContext();
Object attribute = ctx.getAttribute(FACTORY_ATTRIBUTE_NAME);
@ -112,6 +115,22 @@ public abstract class Authenticator {
public abstract boolean isCurrentPassword(UserAccount userAccount,
String clearTextPassword);
/**
* Does this UserAccount have this Argon2 password? False if the
* userAccount is null.
*/
public abstract boolean isCurrentPasswordArgon2(UserAccount userAccount,
String clearTextPassword);
/**
*
* Checks if the user still has got an MD5 Password
*/
public abstract boolean md5HashIsNull(UserAccount userAccount);
/**
* Internal: record a new password for the user. Takes no action if the
* userAccount is null.
@ -180,6 +199,46 @@ public abstract class Authenticator {
}
}
/**
* Applies Argon2i hashing on a string. Obtains the argon2i parameters
* from the configuration properties specified in the runtime.properties
* through this class "Authenticator".
**/
public static String applyArgon2iEncoding(String raw) {
ServletContext ctx = ApplicationUtils.instance().getServletContext();
ConfigurationProperties configProp = ConfigurationProperties.getBean(ctx);
Argon2 argon2 = Argon2Factory.create();
if (configProp.getProperty("argon2.time") != null
&& configProp.getProperty("argon2.memory") != null
&& configProp.getProperty("argon2.parallelism") != null) {
return argon2.hash(
Integer.parseInt(configProp.getProperty("argon2.time")),
Integer.parseInt(configProp.getProperty("argon2.memory")),
Integer.parseInt(configProp.getProperty("argon2.parallelism")), raw);
} else {
throw new RuntimeException(
"Parameters \"argon2.time\", \"argon2.memory\" and "
+ "\"argon2.parallelism\" are either missing in the "
+ "\"runtime.properties\" file or are not defined correctly");
}
}
/**
Verifies the string against the Argon2i hash stored for a user account
*/
public static boolean verifyArgon2iHash(String hash, String raw)
{
Argon2 argon2 = Argon2Factory.create();
return argon2.verify(hash, raw);
}
/**
* Check whether the form of the emailAddress is syntactically correct. Does
* not allow multiple addresses. Does not allow local addresses (without a

View file

@ -98,6 +98,30 @@ public class BasicAuthenticator extends Authenticator {
return encodedPassword.equals(userAccount.getMd5Password());
}
@Override
public boolean md5HashIsNull(UserAccount userAccount){
if(userAccount.getMd5Password().compareTo("")==0 ||
userAccount.getMd5Password()==null)
return true;
else
return false;
}
@Override
public boolean isCurrentPasswordArgon2(UserAccount userAccount,
String clearTextPassword) {
if (userAccount == null) {
return false;
}
if (clearTextPassword == null) {
return false;
}
return verifyArgon2iHash(userAccount.getArgon2Password(),
clearTextPassword);
}
@Override
public void recordNewPassword(UserAccount userAccount,
String newClearTextPassword) {
@ -105,7 +129,9 @@ public class BasicAuthenticator extends Authenticator {
log.error("Trying to change password on null user.");
return;
}
userAccount.setMd5Password(applyMd5Encoding(newClearTextPassword));
userAccount.setArgon2Password((applyArgon2iEncoding(
newClearTextPassword)));
userAccount.setMd5Password("");
userAccount.setPasswordChangeRequired(false);
userAccount.setPasswordLinkExpires(0L);
getUserAccountsDao().updateUserAccount(userAccount);

View file

@ -5,6 +5,7 @@ package edu.cornell.mannlib.vitro.webapp.controller.authenticate;
import static edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource.INTERNAL;
import static edu.cornell.mannlib.vitro.webapp.beans.UserAccount.MAX_PASSWORD_LENGTH;
import static edu.cornell.mannlib.vitro.webapp.beans.UserAccount.MIN_PASSWORD_LENGTH;
import static edu.cornell.mannlib.vitro.webapp.controller.login.LoginProcessBean.MLevel.ERROR;
import java.io.IOException;
import java.io.PrintWriter;
@ -158,7 +159,19 @@ public class ProgramLogin extends HttpServlet {
}
private boolean usernameAndPasswordAreValid() {
return auth.isCurrentPassword(userAccount, password);
if(auth.md5HashIsNull(userAccount)) {
if (!auth.isCurrentPasswordArgon2(userAccount, password))
return false;
}
else {
if (!auth.isCurrentPassword(userAccount, password))
return false;
else {
userAccount.setPasswordChangeRequired(true);
}
}
return true;
}
private boolean loginDisabled() {

View file

@ -76,6 +76,30 @@ public class RestrictedAuthenticator extends Authenticator {
return auth.getAccountForInternalAuth(emailAddress);
}
@Override
public boolean md5HashIsNull(UserAccount userAccount){
if(userAccount.getMd5Password().compareTo("")==0 ||
userAccount.getMd5Password()==null)
return true;
else
return false;
}
@Override
public boolean isCurrentPasswordArgon2(UserAccount userAccount,
String clearTextPassword) {
if (userAccount == null) {
return false;
}
if (clearTextPassword == null) {
return false;
}
return verifyArgon2iHash(userAccount.getArgon2Password(),
clearTextPassword);
}
@Override
public boolean isCurrentPassword(UserAccount userAccount,
String clearTextPassword) {

View file

@ -331,15 +331,37 @@ public class Authenticate extends VitroHttpServlet {
return;
}
if (!getAuthenticator(request).isUserPermittedToLogin(user)) {
bean.setMessage(request, ERROR, "logins_disabled_for_maintenance");
return;
}
if (!getAuthenticator(request).isCurrentPassword(user, password)) {
bean.setMessage(request, ERROR, "error_incorrect_credentials");
return;
if(getAuthenticator(request).md5HashIsNull(user)) {
if (!getAuthenticator(request)
.isCurrentPasswordArgon2(user, password)) {
bean.setMessage(request, ERROR,
"error_incorrect_credentials");
return;
}
}
else {
if (!getAuthenticator(request)
.isCurrentPassword(user, password)) {
bean.setMessage(request, ERROR,
"error_incorrect_credentials");
return;
}
else {
user.setPasswordChangeRequired(true);
user.setMd5Password("");
bean.setMessage(request, ERROR,
"password_system_has_changed");
}
}
// Username and password are correct. What next?
if (user.isPasswordChangeRequired()) {
@ -401,7 +423,7 @@ public class Authenticate extends VitroHttpServlet {
UserAccount user = getAuthenticator(request).getAccountForInternalAuth(
username);
if (getAuthenticator(request).isCurrentPassword(user, newPassword)) {
if (getAuthenticator(request).isCurrentPasswordArgon2(user, newPassword)) {
bean.setMessage(request, ERROR, "error_previous_password");
return;
}

View file

@ -149,6 +149,7 @@ public class VitroVocabulary {
public static final String USERACCOUNT_EMAIL_ADDRESS = VITRO_AUTH + "emailAddress";
public static final String USERACCOUNT_FIRST_NAME = VITRO_AUTH + "firstName";
public static final String USERACCOUNT_LAST_NAME = VITRO_AUTH + "lastName";
public static final String USERACCOUNT_ARGON2_PASSWORD = VITRO_AUTH + "argon2password";
public static final String USERACCOUNT_MD5_PASSWORD = VITRO_AUTH + "md5password";
public static final String USERACCOUNT_OLD_PASSWORD = VITRO_AUTH + "oldpassword";
public static final String USERACCOUNT_LOGIN_COUNT = VITRO_AUTH + "loginCount";

View file

@ -112,6 +112,7 @@ public class JenaBaseDaoCon {
protected DatatypeProperty USERACCOUNT_EMAIL_ADDRESS = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_EMAIL_ADDRESS);
protected DatatypeProperty USERACCOUNT_FIRST_NAME = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_FIRST_NAME);
protected DatatypeProperty USERACCOUNT_LAST_NAME = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_LAST_NAME);
protected DatatypeProperty USERACCOUNT_ARGON2_PASSWORD = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_ARGON2_PASSWORD);
protected DatatypeProperty USERACCOUNT_MD5_PASSWORD = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_MD5_PASSWORD);
protected DatatypeProperty USERACCOUNT_OLD_PASSWORD = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_OLD_PASSWORD);
protected DatatypeProperty USERACCOUNT_LOGIN_COUNT = _constModel.createDatatypeProperty(VitroVocabulary.USERACCOUNT_LOGIN_COUNT);

View file

@ -93,6 +93,7 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao
USERACCOUNT_EMAIL_ADDRESS));
u.setFirstName(getPropertyStringValue(r, USERACCOUNT_FIRST_NAME));
u.setLastName(getPropertyStringValue(r, USERACCOUNT_LAST_NAME));
u.setArgon2Password(getPropertyStringValue(r, USERACCOUNT_ARGON2_PASSWORD));
u.setMd5Password(getPropertyStringValue(r, USERACCOUNT_MD5_PASSWORD));
u.setOldPassword(getPropertyStringValue(r, USERACCOUNT_OLD_PASSWORD));
u.setPasswordLinkExpires(getPropertyLongValue(r,
@ -225,6 +226,8 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao
userAccount.getLastName(), model);
addPropertyStringValue(res, USERACCOUNT_MD5_PASSWORD,
userAccount.getMd5Password(), model);
addPropertyStringValue(res, USERACCOUNT_ARGON2_PASSWORD,
userAccount.getArgon2Password(), model);
addPropertyStringValue(res, USERACCOUNT_OLD_PASSWORD,
userAccount.getOldPassword(), model);
addPropertyLongValue(res, USERACCOUNT_PASSWORD_LINK_EXPIRES,
@ -288,6 +291,8 @@ public class UserAccountsDaoJena extends JenaBaseDao implements UserAccountsDao
userAccount.getLastName(), model);
updatePropertyStringValue(res, USERACCOUNT_MD5_PASSWORD,
userAccount.getMd5Password(), model);
updatePropertyStringValue(res, USERACCOUNT_ARGON2_PASSWORD,
userAccount.getArgon2Password(), model);
updatePropertyStringValue(res, USERACCOUNT_OLD_PASSWORD,
userAccount.getOldPassword(), model);
updatePropertyLongValue(res, USERACCOUNT_PASSWORD_LINK_EXPIRES,

View file

@ -81,7 +81,8 @@ public class UserAccountsSelectorTest extends AbstractTestClass {
assertEquals("email", "email@jones.edu", acct.getEmailAddress());
assertEquals("firstName", "Bob", acct.getFirstName());
assertEquals("lastName", "Caruso", acct.getLastName());
assertEquals("password", "garbage", acct.getMd5Password());
assertEquals("md5password", "garbage", acct.getMd5Password());
assertEquals("argon2password", "betterGarbage", acct.getArgon2Password());
assertEquals("expires", 1100234965897L, acct.getPasswordLinkExpires());
assertEquals("loginCount", 50, acct.getLoginCount());
assertEquals("lastLogin", 1020304050607080L, acct.getLastLoginTime());
@ -105,7 +106,8 @@ public class UserAccountsSelectorTest extends AbstractTestClass {
assertEquals("email", "email@henry.edu", acct.getEmailAddress());
assertEquals("firstName", "Mary", acct.getFirstName());
assertEquals("lastName", "McInerney", acct.getLastName());
assertEquals("password", "garbage", acct.getMd5Password());
assertEquals("md5password", "garbage", acct.getMd5Password());
assertEquals("argon2password", "betterGarbage", acct.getArgon2Password());
assertEquals("expires", 0L, acct.getPasswordLinkExpires());
assertEquals("loginCount", 7, acct.getLoginCount());
assertEquals("lastLogin", 1122334455667788L, acct.getLastLoginTime());

View file

@ -9,6 +9,8 @@ import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Factory;
import edu.cornell.mannlib.vedit.beans.LoginStatusBean;
import edu.cornell.mannlib.vedit.beans.LoginStatusBean.AuthenticationSource;
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
@ -102,6 +104,49 @@ public class AuthenticatorStub extends Authenticator {
return true;
}
@Override
public boolean md5HashIsNull(UserAccount userAccount){
if(userAccount!=null) {
if (userAccount.getMd5Password().compareTo("") == 0 ||
userAccount.getMd5Password() == null)
return true;
else
return false;
}
return false;
}
/**
* Applies Argon2i hashing on a string.
* Used by tests only with pre-specified values because the configuration
* properties (runtime.properties) is not set at compile time.
**/
public static String applyArgon2iEncodingStub(String raw) {
Argon2 argon2 = Argon2Factory.create();
try {
return argon2.hash(200, 500, 1, raw);
} catch (Exception e) {
// This can't happen with a normal Java runtime.
throw new RuntimeException(e);
}
}
@Override
public boolean isCurrentPasswordArgon2(UserAccount userAccount,
String clearTextPassword) {
if (userAccount == null) {
return false;
}
if (clearTextPassword == null) {
return false;
}
return verifyArgon2iHash(userAccount.getArgon2Password(),
clearTextPassword);
}
@Override
public boolean isCurrentPassword(UserAccount userAccount,
String clearTextPassword) {

View file

@ -98,7 +98,8 @@ public class ProgramLoginTest extends AbstractTestClass {
user.setUri(uri);
user.setPermissionSetUris(Collections
.singleton(PermissionSets.URI_DBA));
user.setMd5Password(Authenticator.applyMd5Encoding(password));
user.setArgon2Password(AuthenticatorStub.applyArgon2iEncodingStub(password));
user.setMd5Password("");
user.setLoginCount(loginCount);
user.setPasswordChangeRequired(loginCount == 0);
return user;

View file

@ -191,7 +191,8 @@ public class AuthenticateTest extends AbstractTestClass {
user.setEmailAddress(userInfo.username);
user.setUri(userInfo.uri);
user.setPermissionSetUris(userInfo.permissionSetUris);
user.setMd5Password(Authenticator.applyMd5Encoding(userInfo.password));
user.setArgon2Password(AuthenticatorStub.applyArgon2iEncodingStub(userInfo.password));
user.setMd5Password("");
user.setLoginCount(userInfo.loginCount);
user.setPasswordChangeRequired(userInfo.loginCount == 0);
return user;

View file

@ -91,20 +91,20 @@ public class UserAccountsDaoJenaTest extends AbstractTestClass {
@Before
public void createUserAccountValues() {
user1 = userAccount(URI_USER1, "email@able.edu", "Zack", "Roberts",
"garbage", "", 0L, false, 5, 12345678L, Status.ACTIVE, "user1",
"garbage", "" ,"", 0L, false, 5, 12345678L, Status.ACTIVE, "user1",
false, collection(URI_ROLE1), false, EMPTY);
userNew = userAccount("", "email@here", "Joe", "Blow", "XXXX", "YYYY",
userNew = userAccount("", "email@here", "Joe", "Blow", "XXXX","", "YYYY",
0L, false, 1, 0L, Status.ACTIVE, "jblow", false, EMPTY, false,
EMPTY);
userA = userAccount("", "aahern@here", "Alf", "Ahern", "XXXX", "YYYY",
userA = userAccount("", "aahern@here", "Alf", "Ahern", "XXXX", "", "YYYY",
0L, false, 1, 0L, Status.ACTIVE, "aahern", false, EMPTY, false,
collection(URI_PROFILE1));
userB = userAccount("", "email@here", "Betty", "Boop", "XXXX", "YYYY",
userB = userAccount("", "email@here", "Betty", "Boop", "XXXX", "", "YYYY",
0L, false, 1, 0L, Status.ACTIVE, "bboop", false, EMPTY, false,
collection(URI_PROFILE1, URI_PROFILE2));
userC = userAccount("", "ccallas@here", "Charlie", "Callas", "XXXX",
"YYYY", 0L, false, 1, 0L, Status.ACTIVE, "ccallas", false,
userC = userAccount("", "ccallas@here", "Charlie", "Callas", "XXXX", "",
"YYYY", 0L, false, 1, 0L, Status.ACTIVE, "ccallas", false,
EMPTY, false, collection(URI_PROFILE2));
}
@ -179,7 +179,7 @@ public class UserAccountsDaoJenaTest extends AbstractTestClass {
@Test
public void updateUserAccountSuccess() {
UserAccount orig = userAccount(URI_USER1, "updatedEmail@able.edu",
"Ezekiel", "Roberts", "differentHash", "oldHash", 1L, false,
"Ezekiel", "Roberts", "differentHash", "", "oldHash", 1L, false,
43, 1020304050607080L, Status.ACTIVE, "updatedUser1", false,
collection(URI_ROLE1, URI_ROLE3), false, EMPTY);
@ -379,7 +379,7 @@ public class UserAccountsDaoJenaTest extends AbstractTestClass {
}
private UserAccount userAccount(String uri, String emailAddress,
String firstName, String lastName, String md5Password,
String firstName, String lastName, String argon2Password, String md5Password,
String oldPassword, long passwordLinkExpires,
boolean passwordChangeRequired, int loginCount, long lastLoginTime,
Status status, String externalAuthId, boolean externalAuthOnly,
@ -390,6 +390,7 @@ public class UserAccountsDaoJenaTest extends AbstractTestClass {
ua.setEmailAddress(emailAddress);
ua.setFirstName(firstName);
ua.setLastName(lastName);
ua.setArgon2Password(argon2Password);
ua.setMd5Password(md5Password);
ua.setOldPassword(oldPassword);
ua.setPasswordLinkExpires(passwordLinkExpires);
@ -411,6 +412,7 @@ public class UserAccountsDaoJenaTest extends AbstractTestClass {
out.setEmailAddress(in.getEmailAddress());
out.setFirstName(in.getFirstName());
out.setLastName(in.getLastName());
out.setArgon2Password(in.getArgon2Password());
out.setMd5Password(in.getMd5Password());
out.setOldPassword(in.getOldPassword());
out.setPasswordLinkExpires(in.getPasswordLinkExpires());
@ -433,7 +435,7 @@ public class UserAccountsDaoJenaTest extends AbstractTestClass {
assertEquals("email", e.getEmailAddress(), a.getEmailAddress());
assertEquals("first name", e.getFirstName(), a.getFirstName());
assertEquals("last name", e.getLastName(), a.getLastName());
assertEquals("password", e.getMd5Password(), a.getMd5Password());
assertEquals("password", e.getArgon2Password(), e.getArgon2Password());
assertEquals("old password", e.getOldPassword(), a.getOldPassword());
assertEquals("link expires", e.getPasswordLinkExpires(),
a.getPasswordLinkExpires());

View file

@ -113,6 +113,7 @@ mydomain:user08
auth:firstName "Mary" ;
auth:lastName "McInerney" ;
auth:md5password "garbage" ;
auth:argon2password "betterGarbage" ;
auth:passwordChangeExpires 0 ;
auth:loginCount 7 ;
auth:lastLoginTime 1122334455667788 ;
@ -138,6 +139,7 @@ mydomain:user10
auth:firstName "Bob" ;
auth:lastName "Caruso" ;
auth:md5password "garbage" ;
auth:argon2password "betterGarbage" ;
auth:passwordChangeExpires 1100234965897 ;
auth:loginCount 50 ;
auth:lastLoginTime 1020304050607080 ;