[Major] Refactoring password parsing
This commit is contained in:
parent
9f86f84f35
commit
ff773e76fd
|
@ -73,8 +73,8 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
|
|||
//Specify the search scope
|
||||
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
|
||||
String searchFilter =
|
||||
"(&(objectCategory=person)(objectClass=user)(userPrincipalName=" + username + this.domain + "))";
|
||||
String searchFilter = "(&(objectCategory=person)(objectClass=user)(userPrincipalName=" + username +
|
||||
this.domain + "))";
|
||||
|
||||
// Search for objects using the filter
|
||||
NamingEnumeration<SearchResult> answer = ctx.search(this.searchBase, searchFilter, searchCtls);
|
||||
|
@ -86,8 +86,8 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
|
|||
answer = ctx.search(this.searchBase, searchFilter, searchCtls);
|
||||
|
||||
if (!answer.hasMore())
|
||||
throw new AccessDeniedException("Could not login with user: " + username + this.domain
|
||||
+ " on Ldap: no LDAP Data, for either userPrincipalName or sAMAccountName");
|
||||
throw new AccessDeniedException("Could not login with user: " + username + this.domain +
|
||||
" on Ldap: no LDAP Data, for either userPrincipalName or sAMAccountName");
|
||||
}
|
||||
|
||||
SearchResult searchResult = answer.next();
|
||||
|
@ -139,8 +139,8 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
|
|||
|
||||
Map<String, String> properties = buildProperties(username, attrs, ldapGroups, strolchRoles);
|
||||
|
||||
return new User(username, username, null, null, null, -1, -1, firstName, lastName, UserState.REMOTE,
|
||||
strolchRoles, locale, properties, false, new UserHistory());
|
||||
return new User(username, username, null, firstName, lastName, UserState.REMOTE, strolchRoles, locale,
|
||||
properties, false, new UserHistory());
|
||||
}
|
||||
|
||||
protected abstract Map<String, String> buildProperties(String username, Attributes attrs, Set<String> ldapGroups,
|
||||
|
|
|
@ -15,9 +15,12 @@
|
|||
*/
|
||||
package li.strolch.privilege.handler;
|
||||
|
||||
import static java.lang.String.valueOf;
|
||||
import static li.strolch.privilege.base.PrivilegeConstants.*;
|
||||
import static li.strolch.privilege.helper.XmlConstants.*;
|
||||
import li.strolch.privilege.base.PrivilegeException;
|
||||
import li.strolch.privilege.helper.XmlConstants;
|
||||
import li.strolch.privilege.model.internal.PasswordCrypt;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
|
@ -29,19 +32,16 @@ import java.security.spec.InvalidKeySpecException;
|
|||
import java.text.MessageFormat;
|
||||
import java.util.Map;
|
||||
|
||||
import li.strolch.privilege.base.PrivilegeException;
|
||||
import li.strolch.privilege.helper.Crypt;
|
||||
import li.strolch.privilege.helper.XmlConstants;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import static java.lang.String.valueOf;
|
||||
import static li.strolch.privilege.base.PrivilegeConstants.*;
|
||||
import static li.strolch.privilege.helper.XmlConstants.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This default {@link EncryptionHandler} creates tokens using a {@link SecureRandom} object. Hashing is done by using
|
||||
* {@link MessageDigest} and the configured algorithm which is passed in the parameters
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Required parameters:
|
||||
* <ul>
|
||||
* <li>{@link XmlConstants#XML_PARAM_HASH_ALGORITHM}</li>
|
||||
|
@ -87,11 +87,6 @@ public class DefaultEncryptionHandler implements EncryptionHandler {
|
|||
return this.parameterMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Crypt newCryptInstance() {
|
||||
return new Crypt().setAlgorithm(this.algorithm).setIterations(this.iterations).setKeyLength(this.keyLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return this.algorithm;
|
||||
|
@ -122,11 +117,11 @@ public class DefaultEncryptionHandler implements EncryptionHandler {
|
|||
}
|
||||
|
||||
@Override
|
||||
public byte[] hashPasswordWithoutSalt(char[] password) {
|
||||
public PasswordCrypt hashPasswordWithoutSalt(char[] password) {
|
||||
try {
|
||||
|
||||
MessageDigest digest = MessageDigest.getInstance(this.nonSaltAlgorithm);
|
||||
return digest.digest(new String(password).getBytes());
|
||||
return new PasswordCrypt(digest.digest(new String(password).getBytes()), null);
|
||||
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new PrivilegeException(MessageFormat.format("Algorithm {0} was not found!", nonSaltAlgorithm),
|
||||
|
@ -135,24 +130,32 @@ public class DefaultEncryptionHandler implements EncryptionHandler {
|
|||
}
|
||||
|
||||
@Override
|
||||
public byte[] hashPassword(char[] password, byte[] salt) {
|
||||
public PasswordCrypt hashPassword(char[] password, byte[] salt) {
|
||||
return hashPassword(password, salt, this.algorithm, this.iterations, this.keyLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] hashPassword(char[] password, byte[] salt, String algorithm, int iterations, int keyLength) {
|
||||
public PasswordCrypt hashPassword(char[] password, byte[] salt, String algorithm, int iterations, int keyLength) {
|
||||
|
||||
try {
|
||||
SecretKeyFactory skf = SecretKeyFactory.getInstance(algorithm);
|
||||
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, keyLength);
|
||||
SecretKey key = skf.generateSecret(spec);
|
||||
return key.getEncoded();
|
||||
|
||||
return new PasswordCrypt(key.getEncoded(), salt, algorithm, iterations, keyLength);
|
||||
|
||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPasswordCryptOutdated(PasswordCrypt passwordCrypt) {
|
||||
return passwordCrypt.getSalt() == null || passwordCrypt.getHashAlgorithm() == null ||
|
||||
passwordCrypt.getHashIterations() != this.iterations ||
|
||||
passwordCrypt.getHashKeyLength() != this.keyLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(Map<String, String> parameterMap) {
|
||||
this.parameterMap = parameterMap;
|
||||
|
@ -161,18 +164,18 @@ public class DefaultEncryptionHandler implements EncryptionHandler {
|
|||
|
||||
// get hash algorithm parameters
|
||||
this.algorithm = parameterMap.getOrDefault(XML_PARAM_HASH_ALGORITHM, DEFAULT_ALGORITHM);
|
||||
this.nonSaltAlgorithm = parameterMap
|
||||
.getOrDefault(XML_PARAM_HASH_ALGORITHM_NON_SALT, DEFAULT_ALGORITHM_NON_SALT);
|
||||
this.iterations = Integer
|
||||
.parseInt(parameterMap.getOrDefault(XML_PARAM_HASH_ITERATIONS, valueOf(DEFAULT_ITERATIONS)));
|
||||
this.keyLength = Integer
|
||||
.parseInt(parameterMap.getOrDefault(XML_PARAM_HASH_KEY_LENGTH, valueOf(DEFAULT_KEY_LENGTH)));
|
||||
this.nonSaltAlgorithm = parameterMap.getOrDefault(XML_PARAM_HASH_ALGORITHM_NON_SALT,
|
||||
DEFAULT_ALGORITHM_NON_SALT);
|
||||
this.iterations = Integer.parseInt(
|
||||
parameterMap.getOrDefault(XML_PARAM_HASH_ITERATIONS, valueOf(DEFAULT_ITERATIONS)));
|
||||
this.keyLength = Integer.parseInt(
|
||||
parameterMap.getOrDefault(XML_PARAM_HASH_KEY_LENGTH, valueOf(DEFAULT_KEY_LENGTH)));
|
||||
|
||||
// test non-salt hash algorithm
|
||||
try {
|
||||
hashPasswordWithoutSalt("test".toCharArray());
|
||||
DefaultEncryptionHandler.logger.info(MessageFormat
|
||||
.format("Using non-salt hashing algorithm {0}", this.nonSaltAlgorithm));
|
||||
DefaultEncryptionHandler.logger.info(
|
||||
MessageFormat.format("Using non-salt hashing algorithm {0}", this.nonSaltAlgorithm));
|
||||
} catch (Exception e) {
|
||||
String msg = "[{0}] Defined parameter {1} is invalid because of underlying exception: {2}";
|
||||
msg = MessageFormat.format(msg, EncryptionHandler.class.getName(), XML_PARAM_HASH_ALGORITHM_NON_SALT,
|
||||
|
@ -183,12 +186,11 @@ public class DefaultEncryptionHandler implements EncryptionHandler {
|
|||
// test hash algorithm
|
||||
try {
|
||||
hashPassword("test".toCharArray(), "test".getBytes());
|
||||
DefaultEncryptionHandler.logger
|
||||
.info(MessageFormat.format("Using hashing algorithm {0}", this.algorithm));
|
||||
DefaultEncryptionHandler.logger.info(MessageFormat.format("Using hashing algorithm {0}", this.algorithm));
|
||||
} catch (Exception e) {
|
||||
String msg = "[{0}] Defined parameter {1} is invalid because of underlying exception: {2}";
|
||||
msg = MessageFormat
|
||||
.format(msg, EncryptionHandler.class.getName(), XML_PARAM_HASH_ALGORITHM, e.getLocalizedMessage());
|
||||
msg = MessageFormat.format(msg, EncryptionHandler.class.getName(), XML_PARAM_HASH_ALGORITHM,
|
||||
e.getLocalizedMessage());
|
||||
throw new PrivilegeException(msg, e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,20 @@
|
|||
*/
|
||||
package li.strolch.privilege.handler;
|
||||
|
||||
import static java.text.MessageFormat.format;
|
||||
import static li.strolch.utils.helper.ExceptionHelper.getRootCause;
|
||||
import static li.strolch.utils.helper.StringHelper.*;
|
||||
import li.strolch.privilege.base.*;
|
||||
import li.strolch.privilege.model.*;
|
||||
import li.strolch.privilege.model.internal.*;
|
||||
import li.strolch.privilege.policy.PrivilegePolicy;
|
||||
import li.strolch.privilege.xml.CertificateStubsDomWriter;
|
||||
import li.strolch.privilege.xml.CertificateStubsSaxReader;
|
||||
import li.strolch.privilege.xml.CertificateStubsSaxReader.CertificateStub;
|
||||
import li.strolch.utils.collections.Tuple;
|
||||
import li.strolch.utils.concurrent.ElementLockingHandler;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
import li.strolch.utils.helper.AesCryptoHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.SAXParseException;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.io.File;
|
||||
|
@ -34,21 +45,9 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import li.strolch.privilege.base.*;
|
||||
import li.strolch.privilege.model.*;
|
||||
import li.strolch.privilege.model.internal.*;
|
||||
import li.strolch.privilege.policy.PrivilegePolicy;
|
||||
import li.strolch.privilege.xml.CertificateStubsDomWriter;
|
||||
import li.strolch.privilege.xml.CertificateStubsSaxReader;
|
||||
import li.strolch.privilege.xml.CertificateStubsSaxReader.CertificateStub;
|
||||
import li.strolch.utils.collections.Tuple;
|
||||
import li.strolch.utils.concurrent.ElementLockingHandler;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
import li.strolch.utils.helper.AesCryptoHelper;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.SAXParseException;
|
||||
import static java.text.MessageFormat.format;
|
||||
import static li.strolch.utils.helper.ExceptionHelper.getRootCause;
|
||||
import static li.strolch.utils.helper.StringHelper.*;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
|
@ -418,24 +417,23 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
}
|
||||
|
||||
UserHistory history = new UserHistory();
|
||||
byte[] passwordHash = null;
|
||||
byte[] salt = null;
|
||||
PasswordCrypt passwordCrypt = null;
|
||||
if (password != null) {
|
||||
|
||||
// validate password meets basic requirements
|
||||
validatePassword(certificate.getLocale(), password);
|
||||
|
||||
// get new salt for user
|
||||
salt = this.encryptionHandler.nextSalt();
|
||||
byte[] salt = this.encryptionHandler.nextSalt();
|
||||
|
||||
// hash password
|
||||
passwordHash = this.encryptionHandler.hashPassword(password, salt);
|
||||
passwordCrypt = this.encryptionHandler.hashPassword(password, salt);
|
||||
|
||||
history.setLastPasswordChange(ZonedDateTime.now());
|
||||
}
|
||||
|
||||
// create new user
|
||||
User newUser = createUser(userRep, history, passwordHash, salt, false);
|
||||
User newUser = createUser(userRep, history, passwordCrypt, false);
|
||||
|
||||
// detect privilege conflicts
|
||||
assertNoPrivilegeConflict(newUser);
|
||||
|
@ -481,10 +479,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
// add user
|
||||
|
||||
// make sure userId is not set
|
||||
if (isNotEmpty(userRep.getUserId())) {
|
||||
String msg = "UserId can not be set when adding a new user!";
|
||||
throw new PrivilegeModelException(format(msg, userRep.getUsername()));
|
||||
}
|
||||
if (isNotEmpty(userRep.getUserId()))
|
||||
throw new PrivilegeModelException("UserId can not be set when adding a new user!");
|
||||
|
||||
// set userId
|
||||
userRep.setUserId(getUniqueId());
|
||||
|
@ -495,7 +491,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
validateRolesExist(userRep);
|
||||
|
||||
// create new user
|
||||
user = createUser(userRep, new UserHistory(), null, null, false);
|
||||
user = createUser(userRep, new UserHistory(), null, false);
|
||||
|
||||
// detect privilege conflicts
|
||||
assertNoPrivilegeConflict(user);
|
||||
|
@ -514,9 +510,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
userRep.setUserId(existingUser.getUserId());
|
||||
|
||||
UserHistory history = existingUser.getHistory().getClone();
|
||||
byte[] passwordHash = existingUser.getPassword();
|
||||
byte[] salt = existingUser.getSalt();
|
||||
user = createUser(userRep, history, passwordHash, salt, existingUser.isPasswordChangeRequested());
|
||||
user = createUser(userRep, history, existingUser.getPasswordCrypt(),
|
||||
existingUser.isPasswordChangeRequested());
|
||||
|
||||
// detect privilege conflicts
|
||||
assertNoPrivilegeConflict(user);
|
||||
|
@ -570,23 +565,22 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
}
|
||||
|
||||
UserHistory history = existingUser.getHistory().getClone();
|
||||
byte[] passwordHash = null;
|
||||
byte[] salt = null;
|
||||
PasswordCrypt passwordCrypt = null;
|
||||
if (password != null) {
|
||||
|
||||
// validate password meets basic requirements
|
||||
validatePassword(certificate.getLocale(), password);
|
||||
|
||||
// get new salt for user
|
||||
salt = this.encryptionHandler.nextSalt();
|
||||
byte[] salt = this.encryptionHandler.nextSalt();
|
||||
|
||||
// hash password
|
||||
passwordHash = this.encryptionHandler.hashPassword(password, salt);
|
||||
passwordCrypt = this.encryptionHandler.hashPassword(password, salt);
|
||||
|
||||
history.setLastPasswordChange(ZonedDateTime.now());
|
||||
}
|
||||
|
||||
User newUser = createUser(userRep, history, passwordHash, salt, existingUser.isPasswordChangeRequested());
|
||||
User newUser = createUser(userRep, history, passwordCrypt, existingUser.isPasswordChangeRequested());
|
||||
|
||||
// detect privilege conflicts
|
||||
assertNoPrivilegeConflict(newUser);
|
||||
|
@ -617,7 +611,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private User createUser(UserRep userRep, UserHistory history, byte[] passwordHash, byte[] salt,
|
||||
private User createUser(UserRep userRep, UserHistory history, PasswordCrypt passwordCrypt,
|
||||
boolean passwordChangeRequested) {
|
||||
String userId = userRep.getUserId();
|
||||
String userName = userRep.getUsername();
|
||||
|
@ -627,9 +621,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
Set<String> roles = userRep.getRoles();
|
||||
Locale locale = userRep.getLocale();
|
||||
Map<String, String> properties = userRep.getProperties();
|
||||
return new User(userId, userName, passwordHash, salt, this.encryptionHandler.getAlgorithm(),
|
||||
this.encryptionHandler.getIterations(), this.encryptionHandler.getKeyLength(), firstName, lastName,
|
||||
state, roles, locale, properties, passwordChangeRequested, history);
|
||||
return new User(userId, userName, passwordCrypt, firstName, lastName, state, roles, locale, properties,
|
||||
passwordChangeRequested, history);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -658,8 +651,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
|
||||
String userId = existingUser.getUserId();
|
||||
String username = existingUser.getUsername();
|
||||
byte[] password = existingUser.getPassword();
|
||||
byte[] salt = existingUser.getSalt();
|
||||
PasswordCrypt passwordCrypt = existingUser.getPasswordCrypt();
|
||||
String firstName = existingUser.getFirstname();
|
||||
String lastName = existingUser.getLastname();
|
||||
UserState userState = existingUser.getUserState();
|
||||
|
@ -667,10 +659,6 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
Locale locale = existingUser.getLocale();
|
||||
Map<String, String> propertyMap = existingUser.getProperties();
|
||||
|
||||
String hashAlgorithm = existingUser.getHashAlgorithm();
|
||||
int hashIterations = existingUser.getHashIterations();
|
||||
int hashKeyLength = existingUser.getHashKeyLength();
|
||||
|
||||
// get updated fields
|
||||
if (isNotEmpty(userRep.getFirstname()))
|
||||
firstName = userRep.getFirstname();
|
||||
|
@ -687,9 +675,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
roles = userRep.getRoles();
|
||||
|
||||
// create new user
|
||||
User newUser = new User(userId, username, password, salt, hashAlgorithm, hashIterations, hashKeyLength,
|
||||
firstName, lastName, userState, roles, locale, propertyMap, existingUser.isPasswordChangeRequested(),
|
||||
existingUser.getHistory().getClone());
|
||||
User newUser = new User(userId, username, passwordCrypt, firstName, lastName, userState, roles, locale,
|
||||
propertyMap, existingUser.isPasswordChangeRequested(), existingUser.getHistory().getClone());
|
||||
|
||||
// detect privilege conflicts
|
||||
assertNoPrivilegeConflict(newUser);
|
||||
|
@ -775,11 +762,10 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
Set<String> newRoles = new HashSet<>(currentRoles);
|
||||
newRoles.add(roleName);
|
||||
|
||||
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPassword(),
|
||||
existingUser.getSalt(), existingUser.getHashAlgorithm(), existingUser.getHashIterations(),
|
||||
existingUser.getHashKeyLength(), existingUser.getFirstname(), existingUser.getLastname(),
|
||||
existingUser.getUserState(), newRoles, existingUser.getLocale(), existingUser.getProperties(),
|
||||
existingUser.isPasswordChangeRequested(), existingUser.getHistory().getClone());
|
||||
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPasswordCrypt(),
|
||||
existingUser.getFirstname(), existingUser.getLastname(), existingUser.getUserState(), newRoles,
|
||||
existingUser.getLocale(), existingUser.getProperties(), existingUser.isPasswordChangeRequested(),
|
||||
existingUser.getHistory().getClone());
|
||||
|
||||
// detect privilege conflicts
|
||||
assertNoPrivilegeConflict(newUser);
|
||||
|
@ -826,11 +812,10 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
// create new user
|
||||
Set<String> newRoles = new HashSet<>(currentRoles);
|
||||
newRoles.remove(roleName);
|
||||
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPassword(),
|
||||
existingUser.getSalt(), existingUser.getHashAlgorithm(), existingUser.getHashIterations(),
|
||||
existingUser.getHashKeyLength(), existingUser.getFirstname(), existingUser.getLastname(),
|
||||
existingUser.getUserState(), newRoles, existingUser.getLocale(), existingUser.getProperties(),
|
||||
existingUser.isPasswordChangeRequested(), existingUser.getHistory().getClone());
|
||||
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPasswordCrypt(),
|
||||
existingUser.getFirstname(), existingUser.getLastname(), existingUser.getUserState(), newRoles,
|
||||
existingUser.getLocale(), existingUser.getProperties(), existingUser.isPasswordChangeRequested(),
|
||||
existingUser.getHistory().getClone());
|
||||
|
||||
// delegate user replacement to persistence handler
|
||||
this.persistenceHandler.replaceUser(newUser);
|
||||
|
@ -861,11 +846,10 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
throw new PrivilegeModelException(format("User {0} does not exist!", username));
|
||||
|
||||
// create new user
|
||||
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPassword(),
|
||||
existingUser.getSalt(), existingUser.getHashAlgorithm(), existingUser.getHashIterations(),
|
||||
existingUser.getHashKeyLength(), existingUser.getFirstname(), existingUser.getLastname(),
|
||||
existingUser.getUserState(), existingUser.getRoles(), locale, existingUser.getProperties(),
|
||||
existingUser.isPasswordChangeRequested(), existingUser.getHistory().getClone());
|
||||
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPasswordCrypt(),
|
||||
existingUser.getFirstname(), existingUser.getLastname(), existingUser.getUserState(),
|
||||
existingUser.getRoles(), locale, existingUser.getProperties(), existingUser.isPasswordChangeRequested(),
|
||||
existingUser.getHistory().getClone());
|
||||
|
||||
// if the user is not setting their own locale, then make sure this user may set this user's locale
|
||||
if (!certificate.getUsername().equals(username)) {
|
||||
|
@ -905,11 +889,10 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
throw new PrivilegeModelException(format("User {0} is remote and can not set password!", username));
|
||||
|
||||
// create new user
|
||||
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPassword(),
|
||||
existingUser.getSalt(), existingUser.getHashAlgorithm(), existingUser.getHashIterations(),
|
||||
existingUser.getHashKeyLength(), existingUser.getFirstname(), existingUser.getLastname(),
|
||||
existingUser.getUserState(), existingUser.getRoles(), existingUser.getLocale(),
|
||||
existingUser.getProperties(), true, existingUser.getHistory().getClone());
|
||||
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPasswordCrypt(),
|
||||
existingUser.getFirstname(), existingUser.getLastname(), existingUser.getUserState(),
|
||||
existingUser.getRoles(), existingUser.getLocale(), existingUser.getProperties(), true,
|
||||
existingUser.getHistory().getClone());
|
||||
|
||||
// delegate user replacement to persistence handler
|
||||
this.persistenceHandler.replaceUser(newUser);
|
||||
|
@ -945,28 +928,25 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
|
||||
UserHistory history = existingUser.getHistory().getClone();
|
||||
|
||||
byte[] passwordHash = null;
|
||||
byte[] salt = null;
|
||||
PasswordCrypt passwordCrypt = null;
|
||||
if (password != null) {
|
||||
|
||||
// validate password meets basic requirements
|
||||
validatePassword(certificate.getLocale(), password);
|
||||
|
||||
// get new salt for user
|
||||
salt = this.encryptionHandler.nextSalt();
|
||||
byte[] salt = this.encryptionHandler.nextSalt();
|
||||
|
||||
// hash password
|
||||
passwordHash = this.encryptionHandler.hashPassword(password, salt);
|
||||
passwordCrypt = this.encryptionHandler.hashPassword(password, salt);
|
||||
|
||||
history.setLastPasswordChange(ZonedDateTime.now());
|
||||
}
|
||||
|
||||
// create new user
|
||||
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), passwordHash, salt,
|
||||
this.encryptionHandler.getAlgorithm(), this.encryptionHandler.getIterations(),
|
||||
this.encryptionHandler.getKeyLength(), existingUser.getFirstname(), existingUser.getLastname(),
|
||||
existingUser.getUserState(), existingUser.getRoles(), existingUser.getLocale(),
|
||||
existingUser.getProperties(), false, history);
|
||||
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), passwordCrypt,
|
||||
existingUser.getFirstname(), existingUser.getLastname(), existingUser.getUserState(),
|
||||
existingUser.getRoles(), existingUser.getLocale(), existingUser.getProperties(), false, history);
|
||||
|
||||
if (!certificate.getUsername().equals(username)) {
|
||||
// check that the user may change their own password
|
||||
|
@ -1014,11 +994,10 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
throw new PrivilegeModelException(format("User {0} does not exist!", username));
|
||||
|
||||
// create new user
|
||||
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPassword(),
|
||||
existingUser.getSalt(), existingUser.getHashAlgorithm(), existingUser.getHashIterations(),
|
||||
existingUser.getHashKeyLength(), existingUser.getFirstname(), existingUser.getLastname(), state,
|
||||
existingUser.getRoles(), existingUser.getLocale(), existingUser.getProperties(),
|
||||
existingUser.isPasswordChangeRequested(), existingUser.getHistory().getClone());
|
||||
User newUser = new User(existingUser.getUserId(), existingUser.getUsername(), existingUser.getPasswordCrypt(),
|
||||
existingUser.getFirstname(), existingUser.getLastname(), state, existingUser.getRoles(),
|
||||
existingUser.getLocale(), existingUser.getProperties(), existingUser.isPasswordChangeRequested(),
|
||||
existingUser.getHistory().getClone());
|
||||
|
||||
// validate that this user may modify this user's state
|
||||
prvCtx.validateAction(new SimpleRestrictable(PRIVILEGE_SET_USER_STATE, new Tuple(existingUser, newUser)));
|
||||
|
@ -1727,7 +1706,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
throw new InvalidCredentialsException(msg);
|
||||
}
|
||||
|
||||
// make sure not a system user - they may not login in
|
||||
// make sure not a system user - they may not login
|
||||
if (user.getUserState() == UserState.SYSTEM) {
|
||||
String msg = "User {0} is a system user and may not login!";
|
||||
msg = format(msg, username);
|
||||
|
@ -1742,44 +1721,42 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
throw new AccessDeniedException(msg);
|
||||
}
|
||||
|
||||
byte[] pwHash = user.getPassword();
|
||||
if (pwHash == null)
|
||||
PasswordCrypt userPasswordCrypt = user.getPasswordCrypt();
|
||||
if (userPasswordCrypt.getPassword() == null)
|
||||
throw new InvalidCredentialsException(format("User {0} has no password and may not login!", username));
|
||||
byte[] salt = user.getSalt();
|
||||
|
||||
// we only work with hashed passwords
|
||||
byte[] passwordHash;
|
||||
if (salt == null) {
|
||||
passwordHash = this.encryptionHandler.hashPasswordWithoutSalt(password);
|
||||
} else if (user.getHashAlgorithm() == null || user.getHashIterations() == -1 || user.getHashKeyLength() == -1) {
|
||||
passwordHash = this.encryptionHandler.hashPassword(password, salt);
|
||||
PasswordCrypt requestPasswordCrypt;
|
||||
if (userPasswordCrypt.getSalt() == null) {
|
||||
requestPasswordCrypt = this.encryptionHandler.hashPasswordWithoutSalt(password);
|
||||
} else if (userPasswordCrypt.getHashAlgorithm() == null || userPasswordCrypt.getHashIterations() == -1 ||
|
||||
userPasswordCrypt.getHashKeyLength() == -1) {
|
||||
requestPasswordCrypt = this.encryptionHandler.hashPassword(password, userPasswordCrypt.getSalt());
|
||||
} else {
|
||||
passwordHash = this.encryptionHandler.hashPassword(password, salt, user.getHashAlgorithm(),
|
||||
user.getHashIterations(), user.getHashKeyLength());
|
||||
requestPasswordCrypt = this.encryptionHandler.hashPassword(password, userPasswordCrypt.getSalt(),
|
||||
userPasswordCrypt.getHashAlgorithm(), userPasswordCrypt.getHashIterations(),
|
||||
userPasswordCrypt.getHashKeyLength());
|
||||
}
|
||||
|
||||
// validate password
|
||||
if (!Arrays.equals(passwordHash, pwHash))
|
||||
if (!Arrays.equals(requestPasswordCrypt.getPassword(), userPasswordCrypt.getPassword()))
|
||||
throw new InvalidCredentialsException(format("Password is incorrect for {0}", username));
|
||||
|
||||
// see if we need to update the hash
|
||||
if (user.getHashAlgorithm() == null || user.getHashIterations() != this.encryptionHandler.getIterations() ||
|
||||
user.getHashKeyLength() != this.encryptionHandler.getKeyLength()) {
|
||||
if (this.encryptionHandler.isPasswordCryptOutdated(userPasswordCrypt)) {
|
||||
|
||||
logger.warn("Updating user " + username + " due to change in hashing algorithm properties ");
|
||||
|
||||
// get new salt for user
|
||||
salt = this.encryptionHandler.nextSalt();
|
||||
byte[] salt = this.encryptionHandler.nextSalt();
|
||||
|
||||
// hash password
|
||||
passwordHash = this.encryptionHandler.hashPassword(password, salt);
|
||||
PasswordCrypt newPasswordCrypt = this.encryptionHandler.hashPassword(password, salt);
|
||||
|
||||
// create new user
|
||||
User newUser = new User(user.getUserId(), user.getUsername(), passwordHash, salt,
|
||||
this.encryptionHandler.getAlgorithm(), this.encryptionHandler.getIterations(),
|
||||
this.encryptionHandler.getKeyLength(), user.getFirstname(), user.getLastname(), user.getUserState(),
|
||||
user.getRoles(), user.getLocale(), user.getProperties(), user.isPasswordChangeRequested(),
|
||||
user.getHistory().getClone());
|
||||
User newUser = new User(user.getUserId(), user.getUsername(), newPasswordCrypt, user.getFirstname(),
|
||||
user.getLastname(), user.getUserState(), user.getRoles(), user.getLocale(), user.getProperties(),
|
||||
user.isPasswordChangeRequested(), user.getHistory().getClone());
|
||||
|
||||
// delegate user replacement to persistence handler
|
||||
this.persistenceHandler.replaceUser(newUser);
|
||||
|
@ -2115,21 +2092,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
throw new PrivilegeModelException(msg);
|
||||
}
|
||||
|
||||
File persistSessionsPath = new File(persistSessionsPathS);
|
||||
if (!persistSessionsPath.getParentFile().isDirectory()) {
|
||||
String msg = "Path for param {0} is invalid as parent does not exist or is not a directory. Value: {1}";
|
||||
msg = format(msg, PARAM_PERSIST_SESSIONS_PATH, persistSessionsPath.getAbsolutePath());
|
||||
throw new PrivilegeModelException(msg);
|
||||
}
|
||||
|
||||
if (persistSessionsPath.exists() && (!persistSessionsPath.isFile() || !persistSessionsPath.canWrite())) {
|
||||
String msg
|
||||
= "Path for param {0} is invalid as file exists but is not a file or not writeable. Value: {1}";
|
||||
msg = format(msg, PARAM_PERSIST_SESSIONS_PATH, persistSessionsPath.getAbsolutePath());
|
||||
throw new PrivilegeModelException(msg);
|
||||
}
|
||||
|
||||
this.persistSessionsPath = persistSessionsPath;
|
||||
this.persistSessionsPath = getPersistSessionFile(persistSessionsPathS);
|
||||
logger.info(format("Enabling persistence of sessions to {0}", this.persistSessionsPath.getAbsolutePath()));
|
||||
} else {
|
||||
String msg = "Parameter {0} has illegal value {1}. Overriding with {2}";
|
||||
|
@ -2139,6 +2102,22 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private static File getPersistSessionFile(String persistSessionsPathS) {
|
||||
File persistSessionsPath = new File(persistSessionsPathS);
|
||||
if (!persistSessionsPath.getParentFile().isDirectory()) {
|
||||
String msg = "Path for param {0} is invalid as parent does not exist or is not a directory. Value: {1}";
|
||||
msg = format(msg, PARAM_PERSIST_SESSIONS_PATH, persistSessionsPath.getAbsolutePath());
|
||||
throw new PrivilegeModelException(msg);
|
||||
}
|
||||
|
||||
if (persistSessionsPath.exists() && (!persistSessionsPath.isFile() || !persistSessionsPath.canWrite())) {
|
||||
String msg = "Path for param {0} is invalid as file exists but is not a file or not writeable. Value: {1}";
|
||||
msg = format(msg, PARAM_PERSIST_SESSIONS_PATH, persistSessionsPath.getAbsolutePath());
|
||||
throw new PrivilegeModelException(msg);
|
||||
}
|
||||
return persistSessionsPath;
|
||||
}
|
||||
|
||||
private void handleConflictResolutionParam(Map<String, String> parameterMap) {
|
||||
String privilegeConflictResolutionS = parameterMap.get(PARAM_PRIVILEGE_CONFLICT_RESOLUTION);
|
||||
if (privilegeConflictResolutionS == null) {
|
||||
|
@ -2163,14 +2142,14 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
String secretKeyS = parameterMap.get(PARAM_SECRET_KEY);
|
||||
if (isEmpty(secretKeyS)) {
|
||||
String msg = "Parameter {0} may not be empty";
|
||||
msg = format(msg, PARAM_SECRET_KEY, PARAM_PRIVILEGE_CONFLICT_RESOLUTION);
|
||||
msg = format(msg, PARAM_SECRET_KEY);
|
||||
throw new PrivilegeModelException(msg);
|
||||
}
|
||||
|
||||
String secretSaltS = parameterMap.get(PARAM_SECRET_SALT);
|
||||
if (isEmpty(secretSaltS)) {
|
||||
String msg = "Parameter {0} may not be empty";
|
||||
msg = format(msg, PARAM_SECRET_SALT, PARAM_PRIVILEGE_CONFLICT_RESOLUTION);
|
||||
msg = format(msg, PARAM_SECRET_SALT);
|
||||
throw new PrivilegeModelException(msg);
|
||||
}
|
||||
|
||||
|
@ -2367,8 +2346,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
}
|
||||
|
||||
// validate password
|
||||
byte[] pwHash = user.getPassword();
|
||||
if (pwHash != null) {
|
||||
if (user.getPasswordCrypt() != null) {
|
||||
String msg = format("System users must not have a password: {0}", user.getUsername());
|
||||
throw new AccessDeniedException(msg);
|
||||
}
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
*/
|
||||
package li.strolch.privilege.handler;
|
||||
|
||||
import java.util.Map;
|
||||
import li.strolch.privilege.model.internal.PasswordCrypt;
|
||||
|
||||
import li.strolch.privilege.helper.Crypt;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The {@link EncryptionHandler} exposes API which is used to handle encrypting of strings, or returning secure tokens
|
||||
|
@ -27,13 +27,6 @@ import li.strolch.privilege.helper.Crypt;
|
|||
*/
|
||||
public interface EncryptionHandler {
|
||||
|
||||
/**
|
||||
* Returns a new crypt instance
|
||||
*
|
||||
* @return a new crypt instance
|
||||
*/
|
||||
Crypt newCryptInstance();
|
||||
|
||||
/**
|
||||
* Returns the configured algorithm
|
||||
*
|
||||
|
@ -72,49 +65,43 @@ public interface EncryptionHandler {
|
|||
/**
|
||||
* Hashes the given password configured algorithm
|
||||
*
|
||||
* @param password
|
||||
* the password
|
||||
* @param password the password
|
||||
*
|
||||
* @return the hashed password
|
||||
* @return the {@link PasswordCrypt}
|
||||
*/
|
||||
byte[] hashPasswordWithoutSalt(final char[] password);
|
||||
PasswordCrypt hashPasswordWithoutSalt(final char[] password);
|
||||
|
||||
/**
|
||||
* Hashes the given password with the given salt with the configured algorithm
|
||||
*
|
||||
* @param password
|
||||
* the password
|
||||
* @param salt
|
||||
* the salt
|
||||
* @param password the password
|
||||
* @param salt the salt
|
||||
*
|
||||
* @return the hashed password
|
||||
* @return the {@link PasswordCrypt}
|
||||
*/
|
||||
byte[] hashPassword(final char[] password, final byte[] salt);
|
||||
PasswordCrypt hashPassword(final char[] password, final byte[] salt);
|
||||
|
||||
/**
|
||||
* Hashes the given password with the given salt and algorithm properties
|
||||
*
|
||||
* @param password
|
||||
* the password
|
||||
* @param salt
|
||||
* the salt
|
||||
* @param algorithm
|
||||
* the algorithm
|
||||
* @param iterations
|
||||
* the iterations
|
||||
* @param keyLength
|
||||
* the keyLength
|
||||
* @param password the password
|
||||
* @param salt the salt
|
||||
* @param algorithm the algorithm
|
||||
* @param iterations the iterations
|
||||
* @param keyLength the keyLength
|
||||
*
|
||||
* @return the hashed password
|
||||
* @return the {@link PasswordCrypt}
|
||||
*/
|
||||
byte[] hashPassword(final char[] password, final byte[] salt, String algorithm, int iterations, int keyLength);
|
||||
PasswordCrypt hashPassword(final char[] password, final byte[] salt, String algorithm, int iterations,
|
||||
int keyLength);
|
||||
|
||||
boolean isPasswordCryptOutdated(PasswordCrypt userPasswordCrypt);
|
||||
|
||||
/**
|
||||
* Initialize the concrete {@link EncryptionHandler}. The passed parameter map contains any configuration the
|
||||
* concrete {@link EncryptionHandler} might need
|
||||
*
|
||||
* @param parameterMap
|
||||
* a map containing configuration properties
|
||||
* @param parameterMap a map containing configuration properties
|
||||
*/
|
||||
void initialize(Map<String, String> parameterMap);
|
||||
|
||||
|
|
|
@ -1,212 +0,0 @@
|
|||
package li.strolch.privilege.helper;
|
||||
|
||||
import static li.strolch.privilege.base.PrivilegeConstants.DEFAULT_SMALL_ITERATIONS;
|
||||
import static li.strolch.utils.helper.StringHelper.fromHexString;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
|
||||
public class Crypt {
|
||||
|
||||
private String algorithm;
|
||||
private int keyLength;
|
||||
private int iterations;
|
||||
private byte[] salt;
|
||||
private byte[] password;
|
||||
|
||||
public Crypt() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
public Crypt setAlgorithm(String algorithm) {
|
||||
this.algorithm = algorithm;
|
||||
return this;
|
||||
}
|
||||
|
||||
public byte[] getSalt() {
|
||||
return salt;
|
||||
}
|
||||
|
||||
public Crypt setSalt(byte[] salt) {
|
||||
this.salt = salt;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getKeyLength() {
|
||||
return this.keyLength;
|
||||
}
|
||||
|
||||
public Crypt setKeyLength(int keyLength) {
|
||||
this.keyLength = keyLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getIterations() {
|
||||
return this.iterations;
|
||||
}
|
||||
|
||||
public Crypt setIterations(int iterations) {
|
||||
this.iterations = iterations;
|
||||
return this;
|
||||
}
|
||||
|
||||
public byte[] getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public Crypt setPassword(byte[] password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Crypt parseCrypt(String crypt) {
|
||||
DBC.PRE.assertNotEmpty("crypt can no be empty", crypt);
|
||||
|
||||
if (crypt.contains("$")) {
|
||||
String[] parts = crypt.split("\\$");
|
||||
|
||||
if (parts.length == 5) {
|
||||
|
||||
setAlgorithm(parts[1], true);
|
||||
|
||||
Map<String, String> algOptions = parseAlgOptions(parts[2]);
|
||||
if (!algOptions.containsKey("rounds"))
|
||||
this.iterations = DEFAULT_SMALL_ITERATIONS;
|
||||
else
|
||||
this.iterations = Integer.parseInt(algOptions.get("rounds"));
|
||||
|
||||
this.salt = fromHexString(parts[3]);
|
||||
this.password = fromHexString(parts[4]);
|
||||
|
||||
} else if (parts.length == 4) {
|
||||
|
||||
setAlgorithm(parts[1], true);
|
||||
this.iterations = DEFAULT_SMALL_ITERATIONS;
|
||||
this.salt = fromHexString(parts[2]);
|
||||
this.password = fromHexString(parts[3]);
|
||||
|
||||
} else if (parts.length == 3) {
|
||||
|
||||
setAlgorithm(parts[1], false);
|
||||
this.password = fromHexString(parts[2]);
|
||||
|
||||
} else {
|
||||
throw new IllegalStateException("Wrong number of $ chars in " + crypt + ": " + parts.length);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
this.algorithm = "SHA-512";
|
||||
this.password = fromHexString(crypt);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void assertSame(char[] password) {
|
||||
if (!isSame(password))
|
||||
throw new IllegalStateException("Passwords not the same");
|
||||
}
|
||||
|
||||
public boolean isSame(char[] password) {
|
||||
if (this.password == null)
|
||||
throw new IllegalStateException("password not set, call parseCrypt() first!");
|
||||
if (password == null)
|
||||
throw new IllegalStateException("password must not be null");
|
||||
|
||||
try {
|
||||
|
||||
byte[] hash;
|
||||
if (this.salt == null) {
|
||||
|
||||
hash = StringHelper.hash(this.algorithm, new String(password).getBytes());
|
||||
|
||||
} else {
|
||||
|
||||
PBEKeySpec spec = new PBEKeySpec(password, this.salt, this.iterations, this.keyLength);
|
||||
SecretKeyFactory skf = SecretKeyFactory.getInstance(this.algorithm);
|
||||
SecretKey key = skf.generateSecret(spec);
|
||||
hash = key.getEncoded();
|
||||
}
|
||||
|
||||
return Arrays.equals(hash, this.password);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed validation password for algorithm " + this.algorithm, e);
|
||||
}
|
||||
}
|
||||
|
||||
public String toCryptString() {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("$");
|
||||
switch (this.algorithm) {
|
||||
case "MD5" -> sb.append("1");
|
||||
case "PBKDF2WithHmacSHA256", "SHA-256" -> sb.append("5");
|
||||
case "PBKDF2WithHmacSHA512", "SHA-512" -> sb.append("6");
|
||||
default -> throw new IllegalStateException("Unhandled algorithm " + this.algorithm);
|
||||
}
|
||||
|
||||
if (this.iterations != 0 && this.iterations != DEFAULT_SMALL_ITERATIONS) {
|
||||
sb.append("$");
|
||||
sb.append("rounds");
|
||||
sb.append("=");
|
||||
sb.append(iterations);
|
||||
}
|
||||
|
||||
if (this.salt != null) {
|
||||
sb.append("$");
|
||||
sb.append(StringHelper.toHexString(this.salt));
|
||||
}
|
||||
|
||||
sb.append("$");
|
||||
sb.append(StringHelper.toHexString(this.password));
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private Map<String, String> parseAlgOptions(String part) {
|
||||
String[] options = part.split(",");
|
||||
Map<String, String> algOptions = new HashMap<>(options.length);
|
||||
for (String option : options) {
|
||||
if (option.trim().isEmpty())
|
||||
continue;
|
||||
if (!option.contains("="))
|
||||
throw new IllegalStateException("Option " + option + " is missing = char");
|
||||
String[] keyValue = option.split("=");
|
||||
algOptions.put(keyValue[0].trim(), keyValue[1].trim());
|
||||
}
|
||||
|
||||
return algOptions;
|
||||
}
|
||||
|
||||
private void setAlgorithm(String id, boolean hasSalt) {
|
||||
switch (id) {
|
||||
case "1" -> {
|
||||
this.algorithm = "MD5";
|
||||
this.keyLength = 0;
|
||||
}
|
||||
case "5" -> {
|
||||
this.algorithm = hasSalt ? "PBKDF2WithHmacSHA256" : "SHA-256";
|
||||
this.keyLength = 256;
|
||||
}
|
||||
case "6" -> {
|
||||
this.algorithm = hasSalt ? "PBKDF2WithHmacSHA512" : "SHA-512";
|
||||
this.keyLength = 256;
|
||||
}
|
||||
default -> throw new IllegalStateException("Unhandled ID " + id);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package li.strolch.privilege.helper;
|
||||
|
||||
import li.strolch.privilege.model.internal.User;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
|
||||
public class CryptHelper {
|
||||
|
||||
public static String buildPasswordString(User user) {
|
||||
return buildPasswordString(user.getHashAlgorithm(), user.getHashIterations(), user.getHashKeyLength(),
|
||||
user.getSalt(), user.getPassword());
|
||||
}
|
||||
|
||||
public static String buildPasswordString(String hashAlgorithm, int hashIterations, int hashKeyLength, byte[] salt,
|
||||
byte[] passwordArr) {
|
||||
String algo = hashAlgorithm + "," + hashIterations + "," + hashKeyLength;
|
||||
String hash = StringHelper.toHexString(salt);
|
||||
String password = StringHelper.toHexString(passwordArr);
|
||||
return "$" + algo + "$" + hash + "$" + password;
|
||||
}
|
||||
}
|
|
@ -15,8 +15,9 @@
|
|||
*/
|
||||
package li.strolch.privilege.helper;
|
||||
|
||||
import static li.strolch.privilege.base.PrivilegeConstants.*;
|
||||
import static li.strolch.privilege.helper.CryptHelper.buildPasswordString;
|
||||
import li.strolch.privilege.handler.DefaultEncryptionHandler;
|
||||
import li.strolch.privilege.model.internal.PasswordCrypt;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import java.io.BufferedReader;
|
||||
|
@ -24,8 +25,8 @@ import java.io.InputStreamReader;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import li.strolch.privilege.handler.DefaultEncryptionHandler;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
import static li.strolch.privilege.base.PrivilegeConstants.*;
|
||||
import static li.strolch.privilege.model.internal.PasswordCrypt.buildPasswordString;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
|
@ -37,11 +38,9 @@ import li.strolch.utils.helper.StringHelper;
|
|||
public class PasswordCreator {
|
||||
|
||||
/**
|
||||
* @param args
|
||||
* the args from the command line, NOT USED
|
||||
* @param args the args from the command line, NOT USED
|
||||
*
|
||||
* @throws Exception
|
||||
* thrown if anything goes wrong
|
||||
* @throws Exception thrown if anything goes wrong
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
|
@ -136,18 +135,16 @@ public class PasswordCreator {
|
|||
}
|
||||
byte[] salt = saltS.getBytes();
|
||||
|
||||
byte[] passwordHash = encryptionHandler.hashPassword(password, salt);
|
||||
String passwordHashS = StringHelper.toHexString(passwordHash);
|
||||
PasswordCrypt passwordCrypt = encryptionHandler.hashPassword(password, salt);
|
||||
String passwordHashS = StringHelper.toHexString(passwordCrypt.getPassword());
|
||||
System.out.println("Hash is: " + passwordHashS);
|
||||
System.out.println("Salt is: " + saltS);
|
||||
System.out.println();
|
||||
|
||||
System.out.println(
|
||||
XmlConstants.XML_ATTR_PASSWORD + "=\"" + passwordHashS + "\" " + XmlConstants.XML_ATTR_SALT + "=\""
|
||||
+ saltS + "\"");
|
||||
System.out.println(
|
||||
XmlConstants.XML_ATTR_PASSWORD + "=\"" + buildPasswordString(hashAlgorithm, iterations, keyLength,
|
||||
salt, passwordHash) + "\"");
|
||||
XmlConstants.XML_ATTR_PASSWORD + "=\"" + passwordHashS + "\" " + XmlConstants.XML_ATTR_SALT +
|
||||
"=\"" + saltS + "\"");
|
||||
System.out.println(XmlConstants.XML_ATTR_PASSWORD + "=\"" + passwordCrypt.buildPasswordString() + "\"");
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
package li.strolch.privilege.model.internal;
|
||||
|
||||
import li.strolch.privilege.helper.XmlConstants;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static li.strolch.utils.helper.StringHelper.*;
|
||||
|
||||
public class PasswordCrypt {
|
||||
|
||||
private final byte[] password;
|
||||
private final byte[] salt;
|
||||
private final String hashAlgorithm;
|
||||
private final int hashIterations;
|
||||
private final int hashKeyLength;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PasswordCrypt.class);
|
||||
|
||||
public PasswordCrypt(byte[] password, byte[] salt) {
|
||||
this.password = password;
|
||||
this.salt = salt;
|
||||
this.hashAlgorithm = null;
|
||||
this.hashIterations = -1;
|
||||
this.hashKeyLength = -1;
|
||||
}
|
||||
|
||||
public PasswordCrypt(byte[] password, byte[] salt, String hashAlgorithm, int hashIterations, int hashKeyLength) {
|
||||
this.password = password;
|
||||
this.salt = salt;
|
||||
this.hashAlgorithm = hashAlgorithm;
|
||||
this.hashIterations = hashIterations;
|
||||
this.hashKeyLength = hashKeyLength;
|
||||
}
|
||||
|
||||
public byte[] getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public byte[] getSalt() {
|
||||
return salt;
|
||||
}
|
||||
|
||||
public String getHashAlgorithm() {
|
||||
return hashAlgorithm;
|
||||
}
|
||||
|
||||
public int getHashIterations() {
|
||||
return hashIterations;
|
||||
}
|
||||
|
||||
public int getHashKeyLength() {
|
||||
return hashKeyLength;
|
||||
}
|
||||
|
||||
public static PasswordCrypt parse(String passwordS, String saltS) {
|
||||
if (isEmpty(passwordS))
|
||||
return null;
|
||||
|
||||
byte[] salt = null;
|
||||
if (isNotEmpty(saltS))
|
||||
salt = fromHexString(saltS.trim());
|
||||
|
||||
if (isEmpty(passwordS))
|
||||
return new PasswordCrypt(null, salt);
|
||||
|
||||
passwordS = passwordS.trim();
|
||||
|
||||
byte[] password;
|
||||
if (!passwordS.startsWith("$")) {
|
||||
password = fromHexString(passwordS);
|
||||
return new PasswordCrypt(password, salt);
|
||||
}
|
||||
|
||||
String[] parts = passwordS.split("\\$");
|
||||
if (parts.length != 4)
|
||||
throw new IllegalArgumentException(
|
||||
"Illegal password " + passwordS + ": Starts with $, but does not have 3 parts!");
|
||||
|
||||
String hashAlgorithmS = parts[1];
|
||||
String[] hashParts = hashAlgorithmS.split(",");
|
||||
|
||||
if (hashParts.length != 3)
|
||||
throw new IllegalArgumentException(
|
||||
"Illegal password " + passwordS + ": hashAlgorithm part does not have 3 parts separated by comma!");
|
||||
|
||||
String hashAlgorithm = hashParts[0];
|
||||
int hashIterations = Integer.parseInt(hashParts[1]);
|
||||
int hashKeyLength = Integer.parseInt(hashParts[2]);
|
||||
|
||||
salt = fromHexString(parts[2]);
|
||||
password = fromHexString(parts[3]);
|
||||
|
||||
return new PasswordCrypt(password, salt, hashAlgorithm, hashIterations, hashKeyLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return buildPasswordString();
|
||||
}
|
||||
|
||||
public String buildPasswordString() {
|
||||
if (this.password == null || this.salt == null || this.hashAlgorithm == null || this.hashIterations == -1 ||
|
||||
this.hashKeyLength == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return buildPasswordString(getHashAlgorithm(), getHashIterations(), getHashKeyLength(), getSalt(),
|
||||
getPassword());
|
||||
|
||||
}
|
||||
|
||||
public static String buildPasswordString(String hashAlgorithm, int hashIterations, int hashKeyLength, byte[] salt,
|
||||
byte[] passwordArr) {
|
||||
String algo = hashAlgorithm + "," + hashIterations + "," + hashKeyLength;
|
||||
String hash = toHexString(salt);
|
||||
String password = toHexString(passwordArr);
|
||||
return "$" + algo + "$" + hash + "$" + password;
|
||||
}
|
||||
}
|
|
@ -15,16 +15,16 @@
|
|||
*/
|
||||
package li.strolch.privilege.model.internal;
|
||||
|
||||
import static li.strolch.privilege.base.PrivilegeConstants.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import li.strolch.privilege.base.PrivilegeConstants;
|
||||
import li.strolch.privilege.base.PrivilegeException;
|
||||
import li.strolch.privilege.model.UserRep;
|
||||
import li.strolch.privilege.model.UserState;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static li.strolch.privilege.base.PrivilegeConstants.*;
|
||||
|
||||
/**
|
||||
* This class defines the actual login information for a given user which can be granted privileges. Every user is
|
||||
* granted a set of {@link Role}s and has a {@link UserState} including detail information like first name and lastname
|
||||
|
@ -41,11 +41,7 @@ public final class User {
|
|||
private final String userId;
|
||||
|
||||
private final String username;
|
||||
private final byte[] password;
|
||||
private final byte[] salt;
|
||||
private final String hashAlgorithm;
|
||||
private final int hashIterations;
|
||||
private final int hashKeyLength;
|
||||
private final PasswordCrypt passwordCrypt;
|
||||
|
||||
private final String firstname;
|
||||
private final String lastname;
|
||||
|
@ -62,36 +58,19 @@ public final class User {
|
|||
/**
|
||||
* Default constructor
|
||||
*
|
||||
* @param userId
|
||||
* the user's id
|
||||
* @param username
|
||||
* the user's login name
|
||||
* @param password
|
||||
* the user's password (hashed)
|
||||
* @param salt
|
||||
* the password salt
|
||||
* @param hashAlgorithm
|
||||
* the algorithm for the hash
|
||||
* @param hashIterations
|
||||
* the nr of iterations for hashing
|
||||
* @param hashKeyLength
|
||||
* the hash key length
|
||||
* @param firstname
|
||||
* the user's first name
|
||||
* @param lastname
|
||||
* the user's lastname
|
||||
* @param userState
|
||||
* the user's {@link UserState}
|
||||
* @param roles
|
||||
* the set of {@link Role}s assigned to this user
|
||||
* @param locale
|
||||
* the user's {@link Locale}
|
||||
* @param propertyMap
|
||||
* a {@link Map} containing string value pairs of properties for this user
|
||||
* @param userId the user's id
|
||||
* @param username the user's login name
|
||||
* @param passwordCrypt the {@link PasswordCrypt} containing user's password information
|
||||
* @param firstname the user's first name
|
||||
* @param lastname the user's lastname
|
||||
* @param userState the user's {@link UserState}
|
||||
* @param roles the set of {@link Role}s assigned to this user
|
||||
* @param locale the user's {@link Locale}
|
||||
* @param propertyMap a {@link Map} containing string value pairs of properties for this user
|
||||
*/
|
||||
public User(String userId, String username, byte[] password, byte[] salt, String hashAlgorithm, int hashIterations,
|
||||
int hashKeyLength, String firstname, String lastname, UserState userState, Set<String> roles, Locale locale,
|
||||
Map<String, String> propertyMap, boolean passwordChangeRequested, UserHistory history) {
|
||||
public User(String userId, String username, PasswordCrypt passwordCrypt, String firstname, String lastname,
|
||||
UserState userState, Set<String> roles, Locale locale, Map<String, String> propertyMap,
|
||||
boolean passwordChangeRequested, UserHistory history) {
|
||||
|
||||
if (StringHelper.isEmpty(userId))
|
||||
throw new PrivilegeException("No UserId defined!");
|
||||
|
@ -109,7 +88,7 @@ public final class User {
|
|||
if (history == null)
|
||||
throw new PrivilegeException("History must not be null!");
|
||||
|
||||
// password, salt and hash* may be null, meaning not able to login
|
||||
// passwordCrypt may be null, meaning not able to login
|
||||
// roles may be null, meaning not able to login and must be added later
|
||||
// locale may be null, meaning use system default
|
||||
// properties may be null, meaning no properties
|
||||
|
@ -117,12 +96,7 @@ public final class User {
|
|||
this.userId = userId;
|
||||
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.salt = salt;
|
||||
|
||||
this.hashAlgorithm = hashAlgorithm;
|
||||
this.hashIterations = hashIterations;
|
||||
this.hashKeyLength = hashKeyLength;
|
||||
this.passwordCrypt = passwordCrypt;
|
||||
|
||||
this.userState = userState;
|
||||
|
||||
|
@ -163,48 +137,12 @@ public final class User {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the hashed password for this {@link User}
|
||||
* Returns the {@link PasswordCrypt} for this user, null if not password set
|
||||
*
|
||||
* @return the hashed password for this {@link User}
|
||||
* @return the {@link PasswordCrypt} for this user, null if not password set
|
||||
*/
|
||||
public byte[] getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the salt for this {@link User}
|
||||
*
|
||||
* @return the salt for this {@link User}
|
||||
*/
|
||||
public byte[] getSalt() {
|
||||
return this.salt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hash algorithm
|
||||
*
|
||||
* @return the hash algorithm
|
||||
*/
|
||||
public String getHashAlgorithm() {
|
||||
return this.hashAlgorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hashIterations
|
||||
*
|
||||
* @return hashIterations
|
||||
*/
|
||||
public int getHashIterations() {
|
||||
return this.hashIterations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hashKeyLength
|
||||
*
|
||||
* @return hashKeyLength
|
||||
*/
|
||||
public int getHashKeyLength() {
|
||||
return this.hashKeyLength;
|
||||
public PasswordCrypt getPasswordCrypt() {
|
||||
return this.passwordCrypt;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -238,8 +176,7 @@ public final class User {
|
|||
/**
|
||||
* Returns true if this user has the specified role
|
||||
*
|
||||
* @param role
|
||||
* the name of the {@link Role} to check for
|
||||
* @param role the name of the {@link Role} to check for
|
||||
*
|
||||
* @return true if the this user has the specified role
|
||||
*/
|
||||
|
@ -279,8 +216,7 @@ public final class User {
|
|||
/**
|
||||
* Returns the property with the given key
|
||||
*
|
||||
* @param key
|
||||
* the key for which the property is to be returned
|
||||
* @param key the key for which the property is to be returned
|
||||
*
|
||||
* @return the property with the given key, or null if the property is not defined
|
||||
*/
|
||||
|
@ -357,9 +293,9 @@ public final class User {
|
|||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User [userId=" + this.userId + ", username=" + this.username + ", firstname=" + this.firstname
|
||||
+ ", lastname=" + this.lastname + ", locale=" + this.locale + ", userState=" + this.userState
|
||||
+ ", roles=" + this.roles + "]";
|
||||
return "User [userId=" + this.userId + ", username=" + this.username + ", firstname=" + this.firstname +
|
||||
", lastname=" + this.lastname + ", locale=" + this.locale + ", userState=" + this.userState +
|
||||
", roles=" + this.roles + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,14 +15,8 @@
|
|||
*/
|
||||
package li.strolch.privilege.xml;
|
||||
|
||||
import static java.util.Comparator.comparing;
|
||||
import static li.strolch.privilege.helper.CryptHelper.buildPasswordString;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import li.strolch.privilege.helper.XmlConstants;
|
||||
import li.strolch.privilege.model.internal.PasswordCrypt;
|
||||
import li.strolch.privilege.model.internal.User;
|
||||
import li.strolch.privilege.model.internal.UserHistory;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
|
@ -31,6 +25,15 @@ import li.strolch.utils.iso8601.ISO8601;
|
|||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Comparator.comparing;
|
||||
import static li.strolch.privilege.helper.XmlConstants.*;
|
||||
import static li.strolch.privilege.model.internal.PasswordCrypt.buildPasswordString;
|
||||
import static li.strolch.utils.helper.StringHelper.*;
|
||||
|
||||
/**
|
||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||
*/
|
||||
|
@ -50,90 +53,90 @@ public class PrivilegeUsersDomWriter {
|
|||
|
||||
// create document root
|
||||
Document doc = XmlHelper.createDocument();
|
||||
Element rootElement = doc.createElement(XmlConstants.XML_USERS);
|
||||
Element rootElement = doc.createElement(XML_USERS);
|
||||
doc.appendChild(rootElement);
|
||||
|
||||
this.users.forEach(user -> {
|
||||
|
||||
// create the user element
|
||||
Element userElement = doc.createElement(XmlConstants.XML_USER);
|
||||
Element userElement = doc.createElement(XML_USER);
|
||||
rootElement.appendChild(userElement);
|
||||
|
||||
userElement.setAttribute(XmlConstants.XML_ATTR_USER_ID, user.getUserId());
|
||||
userElement.setAttribute(XmlConstants.XML_ATTR_USERNAME, user.getUsername());
|
||||
userElement.setAttribute(XML_ATTR_USER_ID, user.getUserId());
|
||||
userElement.setAttribute(XML_ATTR_USERNAME, user.getUsername());
|
||||
writePassword(user, userElement);
|
||||
|
||||
// add first name element
|
||||
if (StringHelper.isNotEmpty(user.getFirstname())) {
|
||||
Element firstnameElement = doc.createElement(XmlConstants.XML_FIRSTNAME);
|
||||
if (isNotEmpty(user.getFirstname())) {
|
||||
Element firstnameElement = doc.createElement(XML_FIRSTNAME);
|
||||
firstnameElement.setTextContent(user.getFirstname());
|
||||
userElement.appendChild(firstnameElement);
|
||||
}
|
||||
|
||||
// add last name element
|
||||
if (StringHelper.isNotEmpty(user.getLastname())) {
|
||||
Element lastnameElement = doc.createElement(XmlConstants.XML_LASTNAME);
|
||||
if (isNotEmpty(user.getLastname())) {
|
||||
Element lastnameElement = doc.createElement(XML_LASTNAME);
|
||||
lastnameElement.setTextContent(user.getLastname());
|
||||
userElement.appendChild(lastnameElement);
|
||||
}
|
||||
|
||||
// add state element
|
||||
Element stateElement = doc.createElement(XmlConstants.XML_STATE);
|
||||
Element stateElement = doc.createElement(XML_STATE);
|
||||
stateElement.setTextContent(user.getUserState().toString());
|
||||
userElement.appendChild(stateElement);
|
||||
|
||||
// add locale element
|
||||
Element localeElement = doc.createElement(XmlConstants.XML_LOCALE);
|
||||
Element localeElement = doc.createElement(XML_LOCALE);
|
||||
localeElement.setTextContent(user.getLocale().toLanguageTag());
|
||||
userElement.appendChild(localeElement);
|
||||
|
||||
// add password change requested element
|
||||
if (user.isPasswordChangeRequested()) {
|
||||
Element passwordChangeRequestedElement = doc.createElement(XmlConstants.XML_PASSWORD_CHANGE_REQUESTED);
|
||||
Element passwordChangeRequestedElement = doc.createElement(XML_PASSWORD_CHANGE_REQUESTED);
|
||||
passwordChangeRequestedElement.setTextContent(Boolean.toString(true));
|
||||
userElement.appendChild(passwordChangeRequestedElement);
|
||||
}
|
||||
|
||||
// add all the role elements
|
||||
Element rolesElement = doc.createElement(XmlConstants.XML_ROLES);
|
||||
Element rolesElement = doc.createElement(XML_ROLES);
|
||||
userElement.appendChild(rolesElement);
|
||||
user.getRoles().stream().sorted().forEach(roleName -> {
|
||||
Element roleElement = doc.createElement(XmlConstants.XML_ROLE);
|
||||
Element roleElement = doc.createElement(XML_ROLE);
|
||||
roleElement.setTextContent(roleName);
|
||||
rolesElement.appendChild(roleElement);
|
||||
});
|
||||
|
||||
// add the parameters
|
||||
if (!user.getProperties().isEmpty()) {
|
||||
Element parametersElement = doc.createElement(XmlConstants.XML_PROPERTIES);
|
||||
Element parametersElement = doc.createElement(XML_PROPERTIES);
|
||||
userElement.appendChild(parametersElement);
|
||||
user.getProperties().entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> {
|
||||
Element paramElement = doc.createElement(XmlConstants.XML_PROPERTY);
|
||||
paramElement.setAttribute(XmlConstants.XML_ATTR_NAME, entry.getKey());
|
||||
paramElement.setAttribute(XmlConstants.XML_ATTR_VALUE, entry.getValue());
|
||||
Element paramElement = doc.createElement(XML_PROPERTY);
|
||||
paramElement.setAttribute(XML_ATTR_NAME, entry.getKey());
|
||||
paramElement.setAttribute(XML_ATTR_VALUE, entry.getValue());
|
||||
parametersElement.appendChild(paramElement);
|
||||
});
|
||||
}
|
||||
|
||||
if (!user.isHistoryEmpty()) {
|
||||
UserHistory history = user.getHistory();
|
||||
Element historyElement = doc.createElement(XmlConstants.XML_HISTORY);
|
||||
Element historyElement = doc.createElement(XML_HISTORY);
|
||||
userElement.appendChild(historyElement);
|
||||
|
||||
if (!history.isFirstLoginEmpty()) {
|
||||
Element element = doc.createElement(XmlConstants.XML_FIRST_LOGIN);
|
||||
Element element = doc.createElement(XML_FIRST_LOGIN);
|
||||
element.setTextContent(ISO8601.toString(history.getFirstLogin()));
|
||||
historyElement.appendChild(element);
|
||||
}
|
||||
|
||||
if (!history.isLastLoginEmpty()) {
|
||||
Element element = doc.createElement(XmlConstants.XML_LAST_LOGIN);
|
||||
Element element = doc.createElement(XML_LAST_LOGIN);
|
||||
element.setTextContent(ISO8601.toString(history.getLastLogin()));
|
||||
historyElement.appendChild(element);
|
||||
}
|
||||
|
||||
if (!history.isLastPasswordChangeEmpty()) {
|
||||
Element element = doc.createElement(XmlConstants.XML_LAST_PASSWORD_CHANGE);
|
||||
Element element = doc.createElement(XML_LAST_PASSWORD_CHANGE);
|
||||
element.setTextContent(ISO8601.toString(history.getLastPasswordChange()));
|
||||
historyElement.appendChild(element);
|
||||
}
|
||||
|
@ -145,14 +148,15 @@ public class PrivilegeUsersDomWriter {
|
|||
}
|
||||
|
||||
private void writePassword(User user, Element userElement) {
|
||||
if (user.getPassword() != null && user.getSalt() != null && user.getHashAlgorithm() != null
|
||||
&& user.getHashIterations() != -1 && user.getHashKeyLength() != -1) {
|
||||
userElement.setAttribute(XmlConstants.XML_ATTR_PASSWORD, buildPasswordString(user));
|
||||
PasswordCrypt passwordCrypt = user.getPasswordCrypt();
|
||||
String passwordString = passwordCrypt.buildPasswordString();
|
||||
if (passwordString != null) {
|
||||
userElement.setAttribute(XML_ATTR_PASSWORD, passwordString);
|
||||
} else {
|
||||
if (user.getPassword() != null)
|
||||
userElement.setAttribute(XmlConstants.XML_ATTR_PASSWORD, StringHelper.toHexString(user.getPassword()));
|
||||
if (user.getSalt() != null)
|
||||
userElement.setAttribute(XmlConstants.XML_ATTR_SALT, StringHelper.toHexString(user.getSalt()));
|
||||
if (passwordCrypt.getPassword() != null)
|
||||
userElement.setAttribute(XML_ATTR_PASSWORD, toHexString(passwordCrypt.getPassword()));
|
||||
if (passwordCrypt.getSalt() != null)
|
||||
userElement.setAttribute(XML_ATTR_SALT, toHexString(passwordCrypt.getSalt()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.text.MessageFormat;
|
|||
import java.util.*;
|
||||
|
||||
import li.strolch.privilege.model.UserState;
|
||||
import li.strolch.privilege.model.internal.PasswordCrypt;
|
||||
import li.strolch.privilege.model.internal.User;
|
||||
import li.strolch.privilege.model.internal.UserHistory;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
|
@ -116,11 +117,7 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
|
|||
|
||||
String userId;
|
||||
String username;
|
||||
byte[] password;
|
||||
byte[] salt;
|
||||
String hashAlgorithm;
|
||||
int hashIterations = -1;
|
||||
int hashKeyLength = -1;
|
||||
PasswordCrypt passwordCrypt;
|
||||
String firstName;
|
||||
String lastname;
|
||||
UserState userState;
|
||||
|
@ -145,50 +142,12 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
|
|||
|
||||
String password = attributes.getValue(XML_ATTR_PASSWORD);
|
||||
String salt = attributes.getValue(XML_ATTR_SALT);
|
||||
parsePassword(password, salt);
|
||||
this.passwordCrypt = PasswordCrypt.parse(password, salt);
|
||||
} else if (qName.equals(XML_HISTORY)) {
|
||||
this.history = new UserHistory();
|
||||
}
|
||||
}
|
||||
|
||||
private void parsePassword(String passwordS, String salt) {
|
||||
|
||||
if (StringHelper.isNotEmpty(salt))
|
||||
this.salt = StringHelper.fromHexString(salt.trim());
|
||||
|
||||
if (StringHelper.isEmpty(passwordS))
|
||||
return;
|
||||
|
||||
passwordS = passwordS.trim();
|
||||
|
||||
if (!passwordS.startsWith("$")) {
|
||||
this.password = StringHelper.fromHexString(passwordS);
|
||||
} else {
|
||||
|
||||
String[] parts = passwordS.split("\\$");
|
||||
if (parts.length != 4) {
|
||||
logger.error("Illegal password " + passwordS + ": Starts with $, but does not have 3 parts!");
|
||||
} else {
|
||||
|
||||
String hashAlgorithm = parts[1];
|
||||
String[] hashParts = hashAlgorithm.split(",");
|
||||
|
||||
if (hashParts.length != 3) {
|
||||
logger.error("Illegal password " + passwordS
|
||||
+ ": hashAlgorithm part does not have 3 parts separated by comma!");
|
||||
} else {
|
||||
|
||||
this.hashAlgorithm = hashParts[0];
|
||||
this.hashIterations = Integer.parseInt(hashParts[1]);
|
||||
this.hashKeyLength = Integer.parseInt(hashParts[2]);
|
||||
|
||||
this.salt = StringHelper.fromHexString(parts[2]);
|
||||
this.password = StringHelper.fromHexString(parts[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) {
|
||||
this.text.append(ch, start, length);
|
||||
|
@ -198,37 +157,37 @@ public class PrivilegeUsersSaxReader extends DefaultHandler {
|
|||
public void endElement(String uri, String localName, String qName) {
|
||||
|
||||
switch (qName) {
|
||||
case XML_FIRSTNAME -> this.firstName = this.text.toString().trim();
|
||||
case XML_LASTNAME -> this.lastname = this.text.toString().trim();
|
||||
case XML_STATE -> this.userState = UserState.valueOf(this.text.toString().trim());
|
||||
case XML_LOCALE -> this.locale = Locale.forLanguageTag(this.text.toString().trim());
|
||||
case XML_PASSWORD_CHANGE_REQUESTED ->
|
||||
this.passwordChangeRequested = Boolean.parseBoolean(this.text.toString().trim());
|
||||
case XML_FIRST_LOGIN -> this.history.setFirstLogin(ISO8601.parseToZdt(this.text.toString().trim()));
|
||||
case XML_LAST_LOGIN -> this.history.setLastLogin(ISO8601.parseToZdt(this.text.toString().trim()));
|
||||
case XML_LAST_PASSWORD_CHANGE ->
|
||||
this.history.setLastPasswordChange(ISO8601.parseToZdt(this.text.toString().trim()));
|
||||
case XML_ROLE -> this.userRoles.add(this.text.toString().trim());
|
||||
case XML_USER -> {
|
||||
if (this.history == null)
|
||||
this.history = new UserHistory();
|
||||
case XML_FIRSTNAME -> this.firstName = this.text.toString().trim();
|
||||
case XML_LASTNAME -> this.lastname = this.text.toString().trim();
|
||||
case XML_STATE -> this.userState = UserState.valueOf(this.text.toString().trim());
|
||||
case XML_LOCALE -> this.locale = Locale.forLanguageTag(this.text.toString().trim());
|
||||
case XML_PASSWORD_CHANGE_REQUESTED ->
|
||||
this.passwordChangeRequested = Boolean.parseBoolean(this.text.toString().trim());
|
||||
case XML_FIRST_LOGIN -> this.history.setFirstLogin(ISO8601.parseToZdt(this.text.toString().trim()));
|
||||
case XML_LAST_LOGIN -> this.history.setLastLogin(ISO8601.parseToZdt(this.text.toString().trim()));
|
||||
case XML_LAST_PASSWORD_CHANGE ->
|
||||
this.history.setLastPasswordChange(ISO8601.parseToZdt(this.text.toString().trim()));
|
||||
case XML_ROLE -> this.userRoles.add(this.text.toString().trim());
|
||||
case XML_USER -> {
|
||||
if (this.history == null)
|
||||
this.history = new UserHistory();
|
||||
|
||||
User user = new User(this.userId, this.username, this.password, this.salt, this.hashAlgorithm,
|
||||
hashIterations, hashKeyLength, this.firstName, this.lastname, this.userState, this.userRoles,
|
||||
this.locale, this.parameters, this.passwordChangeRequested, this.history);
|
||||
User user = new User(this.userId, this.username, this.passwordCrypt, this.firstName, this.lastname,
|
||||
this.userState, this.userRoles, this.locale, this.parameters, this.passwordChangeRequested,
|
||||
this.history);
|
||||
|
||||
logger.info(MessageFormat.format("New User: {0}", user));
|
||||
String username = caseInsensitiveUsername ? user.getUsername().toLowerCase() : user.getUsername();
|
||||
users.put(username, user);
|
||||
}
|
||||
default -> {
|
||||
if (!(qName.equals(XML_ROLES) //
|
||||
|| qName.equals(XML_PARAMETER) //
|
||||
|| qName.equals(XML_HISTORY) //
|
||||
|| qName.equals(XML_PARAMETERS))) {
|
||||
throw new IllegalArgumentException("Unhandled tag " + qName);
|
||||
logger.info(MessageFormat.format("New User: {0}", user));
|
||||
String username = caseInsensitiveUsername ? user.getUsername().toLowerCase() : user.getUsername();
|
||||
users.put(username, user);
|
||||
}
|
||||
default -> {
|
||||
if (!(qName.equals(XML_ROLES) //
|
||||
|| qName.equals(XML_PARAMETER) //
|
||||
|| qName.equals(XML_HISTORY) //
|
||||
|| qName.equals(XML_PARAMETERS))) {
|
||||
throw new IllegalArgumentException("Unhandled tag " + qName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
package li.strolch.privilege.test;
|
||||
|
||||
import static li.strolch.privilege.base.PrivilegeConstants.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import li.strolch.privilege.handler.DefaultEncryptionHandler;
|
||||
import li.strolch.privilege.helper.XmlConstants;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import li.strolch.privilege.handler.DefaultEncryptionHandler;
|
||||
import li.strolch.privilege.helper.Crypt;
|
||||
import li.strolch.privilege.helper.XmlConstants;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import static li.strolch.privilege.base.PrivilegeConstants.*;
|
||||
|
||||
public class CryptTest {
|
||||
|
||||
|
@ -27,74 +25,28 @@ public class CryptTest {
|
|||
encryptionHandler.initialize(parameterMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAssertSamePassword01() {
|
||||
|
||||
String hash = "$1$21232f297a57a5a743894a0e4a801fc3";
|
||||
char[] password = "admin".toCharArray();
|
||||
|
||||
Crypt crypt = new Crypt().parseCrypt(hash);
|
||||
crypt.assertSame(password);
|
||||
assertEquals(hash, crypt.toCryptString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAssertSamePassword05() {
|
||||
|
||||
String hash = "$5$8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918";
|
||||
char[] password = "admin".toCharArray();
|
||||
|
||||
Crypt crypt = new Crypt().parseCrypt(hash);
|
||||
crypt.assertSame(password);
|
||||
assertEquals(hash, crypt.toCryptString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAssertSamePassword06() {
|
||||
|
||||
String hash = "$6$c7ad44cbad762a5da0a452f9e854fdc1e0e7a52a38015f23f3eab1d80b931dd472634dfac71cd34ebc35d16ab7fb8a90c81f975113d6c7538dc69dd8de9077ec";
|
||||
char[] password = "admin".toCharArray();
|
||||
|
||||
Crypt crypt = new Crypt().parseCrypt(hash);
|
||||
crypt.assertSame(password);
|
||||
assertEquals(hash, crypt.toCryptString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAssertSamePassword15() {
|
||||
|
||||
String hash = "$5$61646d696e$f4aec2c20dd0c3ff0547f4bd56facd76097cce7c613da80c67842b6a357fdc04";
|
||||
char[] password = "admin".toCharArray();
|
||||
|
||||
Crypt crypt = new Crypt().parseCrypt(hash);
|
||||
crypt.assertSame(password);
|
||||
assertEquals(hash, crypt.toCryptString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAssertSamePassword16() {
|
||||
|
||||
String hash = "$6$rounds=5000$61646d696e$5a39ca7443147f9bf549ee0c2d5ded0640690ed56ef8c903e1b0da2a3339010b";
|
||||
char[] password = "admin".toCharArray();
|
||||
|
||||
Crypt crypt = new Crypt().parseCrypt(hash);
|
||||
crypt.assertSame(password);
|
||||
assertEquals(hash, crypt.toCryptString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAssertSamePassword20() {
|
||||
|
||||
char[] password = "admin".toCharArray();
|
||||
|
||||
byte[] salt = "admin".getBytes();
|
||||
byte[] passwordHash = encryptionHandler.hashPassword(password, salt);
|
||||
|
||||
Crypt crypt = encryptionHandler.newCryptInstance();
|
||||
crypt.setSalt(salt);
|
||||
crypt.setPassword(passwordHash);
|
||||
|
||||
String hash = "$6$61646d696e$cb69962946617da006a2f95776d78b49e5ec7941d2bdb2d25cdb05f957f64344";
|
||||
assertEquals(hash, crypt.toCryptString());
|
||||
// char[] password = "admin".toCharArray();
|
||||
//
|
||||
// byte[] salt = "admin".getBytes();
|
||||
// byte[] passwordHash = encryptionHandler.hashPassword(password, salt);
|
||||
//
|
||||
// Crypt crypt = encryptionHandler.newCryptInstance();
|
||||
// crypt.setSalt(salt);
|
||||
// crypt.setPassword(passwordHash);
|
||||
//
|
||||
//
|
||||
// encryptionHandler.
|
||||
// String hash = "$PBKDF2WithHmacSHA512,100000,256$943f2d9208079322e50297f018c44d77d4e887e07bc9b37b2b80d121ad7dbd6e$8bcd819c99e79975e93e5f8bd87a376737afacd4427d7b33f0f69d0fc8030da5";
|
||||
//
|
||||
// requestPasswordCrypt = encryptionHandler.hashPassword(password, userPasswordCrypt.getSalt(),
|
||||
// userPasswordCrypt.getHashAlgorithm(), userPasswordCrypt.getHashIterations(),
|
||||
// userPasswordCrypt.getHashKeyLength());
|
||||
//
|
||||
// // validate password
|
||||
// if (!Arrays.equals(requestPasswordCrypt.getPassword(), userPasswordCrypt.getPassword()))
|
||||
// throw new InvalidCredentialsException(format("Password is incorrect for {0}", username));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue