package com.constellio.model.services.security.authentification;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.constellio.data.dao.managers.config.ConfigManager;
import com.constellio.data.dao.managers.config.PropertiesAlteration;
import com.constellio.data.dao.managers.config.values.PropertiesConfiguration;
import com.constellio.data.utils.hashing.HashingService;
import com.constellio.data.utils.hashing.HashingServiceException;
import com.constellio.model.services.security.authentification.PasswordFileAuthenticationServiceRuntimeException.IncorrectPassword;
import com.constellio.model.services.users.UserUtils;
public class PasswordFileAuthenticationService implements AuthenticationService {
private static final Logger LOGGER = LoggerFactory.getLogger(PasswordFileAuthenticationService.class);
final String AUTHENTIFICATION_PROPERTIES = "/authentification.properties";
private final ConfigManager configManager;
private final HashingService hashingService;
public PasswordFileAuthenticationService(ConfigManager configManager, HashingService hashingService) {
this.configManager = configManager;
this.hashingService = hashingService;
configManager.createPropertiesDocumentIfInexistent(AUTHENTIFICATION_PROPERTIES, new PropertiesAlteration() {
@Override
public void alter(Map<String, String> properties) {
}
});
}
@Override
public boolean supportPasswordChange() {
return true;
}
@Override
public boolean authenticate(String username, String password) {
try {
validateCurrentPassword(username, password);
} catch (IncorrectPassword e) {
LOGGER.warn("Cannot authenticate. Incorrect password", e);
return false;
}
return true;
}
@Override
public void changePassword(final String username, String password, final String newPassword) {
validatePasswords(username, password, newPassword);
final String newPasswordHash = calculeHash(newPassword);
updatePassword(username, newPasswordHash);
}
@Override
public void changePassword(String username, String newPassword) {
validateNewPassword(newPassword);
final String newPasswordHash = calculeHash(newPassword);
updatePassword(username, newPasswordHash);
}
@Override
public void reloadServiceConfiguration() {
//not supported
}
void validatePasswords(String username, String password, String newPassword) {
validateCurrentPassword(username, password);
validateNewPassword(newPassword);
}
void validateNewPassword(String newPassword) {
if (newPassword == null || newPassword.equals("")) {
throw new PasswordFileAuthenticationServiceRuntimeException.InvalidPasswordException();
}
}
void validateCurrentPassword(String username, String password) {
if (username == null || password == null) {
throw new IncorrectPassword();
}
String currentPasswordHash = getPasswordHash(username);
String passwordHash = calculeHash(password);
if (currentPasswordHash == null || !currentPasswordHash.equals(passwordHash)) {
throw new IncorrectPassword();
}
}
private String getPasswordHash(String username) {
PropertiesConfiguration propertiesConfiguration = configManager.getProperties(AUTHENTIFICATION_PROPERTIES);
Map<String, String> properties = propertiesConfiguration.getProperties();
String usernameKey = UserUtils.cleanUsername(username);
for (Entry<String, String> entry : properties.entrySet()) {
String entryKey = UserUtils.cleanUsername(entry.getKey());
if (entryKey.equals(usernameKey)) {
return entry.getValue();
}
}
return null;
}
private void updatePassword(final String username, final String newPasswordHash) {
final String usernameKey = UserUtils.cleanUsername(username);
configManager.updateProperties(AUTHENTIFICATION_PROPERTIES, new PropertiesAlteration() {
@Override
public void alter(Map<String, String> properties) {
Set<String> keysToRemove = new HashSet<String>();
for (Map.Entry<String, String> entry : properties.entrySet()) {
final String aUsernameKey = UserUtils.cleanUsername(entry.getKey());
if (aUsernameKey.equals(usernameKey)) {
keysToRemove.add(entry.getKey());
}
}
for (String keyToRemove : keysToRemove) {
properties.remove(keyToRemove);
}
properties.put(usernameKey, newPasswordHash);
}
});
}
private String calculeHash(String text) {
final String textHash;
try {
textHash = hashingService.getHashFromBytes(text.getBytes());
} catch (HashingServiceException e) {
throw new PasswordFileAuthenticationServiceRuntimeException.CannotCalculateHash(text, e);
}
return textHash;
}
}