package com.constellio.model.services.users;
import static com.constellio.data.threads.BackgroundThreadExceptionHandling.CONTINUE;
import static com.constellio.model.services.users.UserUtils.cleanUsername;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.jdom2.Document;
import org.joda.time.LocalDateTime;
import com.constellio.data.dao.managers.config.ConfigManager;
import com.constellio.data.dao.managers.config.DocumentAlteration;
import com.constellio.data.dao.managers.config.events.ConfigUpdatedEventListener;
import com.constellio.data.dao.services.factories.DataLayerFactory;
import com.constellio.data.threads.BackgroundThreadConfiguration;
import com.constellio.data.threads.BackgroundThreadsManager;
import com.constellio.data.utils.Factory;
import com.constellio.data.utils.TimeProvider;
import com.constellio.data.utils.dev.Toggle;
import com.constellio.model.conf.ModelLayerConfiguration;
import com.constellio.model.entities.security.global.UserCredential;
import com.constellio.model.entities.security.global.UserCredentialStatus;
import com.constellio.model.entities.security.global.XmlUserCredential;
import com.constellio.model.services.collections.CollectionsListManager;
import com.constellio.model.services.encrypt.EncryptionServices;
import com.constellio.model.services.factories.ModelLayerFactory;
public class XmlUserCredentialsManager implements UserCredentialsManager, ConfigUpdatedEventListener {
public static final String USER_CREDENTIALS_CONFIG = "/userCredentialsConfig.xml";
private final ConfigManager configManager;
private final BackgroundThreadsManager backgroundThreadsManager;
private final ModelLayerConfiguration configuration;
private Map<String, UserCredential> cache = new LinkedHashMap<>();
private List<String> usersWithServiceKey = null;
private CollectionsListManager collectionsListManager;
private ModelLayerFactory modelLayerFactory;
public XmlUserCredentialsManager(DataLayerFactory dataLayerFactory, ModelLayerFactory modelLayerFactory,
ModelLayerConfiguration configuration) {
this.configManager = dataLayerFactory.getConfigManager();
this.modelLayerFactory = modelLayerFactory;
this.collectionsListManager = modelLayerFactory.getCollectionsListManager();
this.configuration = configuration;
this.backgroundThreadsManager = dataLayerFactory.getBackgroundThreadsManager();
}
private void init() {
usersWithServiceKey = new ArrayList<>();
UserServices userServices = modelLayerFactory.newUserServices();
for (UserCredential userCredential :
userServices.getActiveUserCredentials()) {
if (userCredential.getServiceKey() != null) {
usersWithServiceKey.add(userCredential.getUsername());
}
}
}
@Override
public void initialize() {
registerListener(configManager);
Document document = configManager.getXML(USER_CREDENTIALS_CONFIG).getDocument();
UserCredentialsReader reader = newUserCredencialsReader(document);
cache = Collections.unmodifiableMap(reader.readAll(collectionsListManager.getCollections()));
Runnable removedTimedOutTokens = new Runnable() {
@Override
public void run() {
removeTimedOutTokens();
}
};
this.backgroundThreadsManager.configure(BackgroundThreadConfiguration
.repeatingAction("removeTimedOutTokens", removedTimedOutTokens)
.handlingExceptionWith(CONTINUE)
.executedEvery(configuration.getTokenRemovalThreadDelayBetweenChecks()));
}
@Override
public UserCredential create(String username, String firstName, String lastName, String email, List<String> globalGroups,
List<String> collections, UserCredentialStatus status) {
return new XmlUserCredential(username, firstName, lastName, email, globalGroups, collections, status);
}
@Override
public UserCredential create(String username, String firstName, String lastName, String email, List<String> globalGroups,
List<String> collections, UserCredentialStatus status, String domain, List<String> msExchDelegateListBL, String dn) {
return new XmlUserCredential(
username, firstName, lastName, email, globalGroups, collections, status, domain, msExchDelegateListBL, dn);
}
@Override
public UserCredential create(String username, String firstName, String lastName, String email, String serviceKey,
boolean systemAdmin, List<String> globalGroups, List<String> collections, Map<String, LocalDateTime> tokens,
UserCredentialStatus status) {
return new XmlUserCredential(
username, firstName, lastName, email, serviceKey, systemAdmin, globalGroups, collections, tokens, status);
}
@Override
public UserCredential create(String username, String firstName, String lastName, String email, String serviceKey,
boolean systemAdmin, List<String> globalGroups, List<String> collections, Map<String, LocalDateTime> tokens,
UserCredentialStatus status, String domain, List<String> msExchDelegateListBL, String dn) {
return new XmlUserCredential(username, firstName, lastName, email, serviceKey, systemAdmin, globalGroups, collections,
tokens, status, domain, msExchDelegateListBL, dn);
}
@Override
public UserCredential create(String username, String firstName, String lastName, String email, List<String> personalEmails, String serviceKey,
boolean systemAdmin, List<String> globalGroups, List<String> collections, Map<String, LocalDateTime> tokens,
UserCredentialStatus status, String domain, List<String> msExchDelegateListBL, String dn) {
return new XmlUserCredential(username, firstName, lastName, email, serviceKey, systemAdmin, globalGroups, collections,
tokens, status, domain, msExchDelegateListBL, dn);
}
@Override
public UserCredential create(String username, String firstName, String lastName, String email, List<String> personalEmails, String serviceKey, boolean systemAdmin, List<String> globalGroups, List<String> collections, Map<String, LocalDateTime> tokens, UserCredentialStatus status, String domain, List<String> msExchDelegateListBL, String dn, String jobTitle, String phone, String fax, String address) {
return new XmlUserCredential(username, firstName, lastName, email, personalEmails, serviceKey, systemAdmin, globalGroups, collections,
tokens, status, domain, msExchDelegateListBL, dn, address, phone, fax, jobTitle);
}
@Override
public void addUpdate(UserCredential userCredential) {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
initIfRequired();
configManager.updateXML(USER_CREDENTIALS_CONFIG, newAddUpdateUserCredentialsDocumentAlteration(userCredential));
if (userCredential.getServiceKey() != null) {
usersWithServiceKey.add(userCredential.getUsername());
}
}
@Override
public UserCredential getUserCredential(String username) {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
String cacheKey = cleanUsername(username);
return cache.get(cacheKey);
}
@Override
public List<UserCredential> getUserCredentials() {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
List<UserCredential> userCredentials = new ArrayList<>(cache.values());
sort(userCredentials);
return Collections.unmodifiableList(userCredentials);
}
List<UserCredential> getUserCredentialsByStatus(UserCredentialStatus status) {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
List<UserCredential> userCredentials = new ArrayList<>();
for (UserCredential userCredential : getUserCredentials()) {
if (status == userCredential.getStatus()) {
userCredentials.add(userCredential);
}
}
return Collections.unmodifiableList(userCredentials);
}
@Override
public List<UserCredential> getActiveUserCredentials() {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
return Collections.unmodifiableList(getUserCredentialsByStatus(UserCredentialStatus.ACTIVE));
}
@Override
public List<UserCredential> getSuspendedUserCredentials() {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
return Collections.unmodifiableList(getUserCredentialsByStatus(UserCredentialStatus.SUSPENDED));
}
@Override
public List<UserCredential> getPendingApprovalUserCredentials() {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
return Collections.unmodifiableList(getUserCredentialsByStatus(UserCredentialStatus.PENDING));
}
@Override
public List<UserCredential> getDeletedUserCredentials() {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
return Collections.unmodifiableList(getUserCredentialsByStatus(UserCredentialStatus.DELETED));
}
@Override
public List<UserCredential> getUserCredentialsInGlobalGroup(String group) {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
List<UserCredential> userCredentials = new ArrayList<>();
for (UserCredential userCredential : getActiveUserCredentials()) {
if (userCredential.getGlobalGroups().contains(group)) {
userCredentials.add(userCredential);
}
}
return Collections.unmodifiableList(userCredentials);
}
@Override
public void removeCollection(String collection) {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
configManager.updateXML(USER_CREDENTIALS_CONFIG, newRemoveCollectionDocumentAlteration(collection));
}
@Override
public void removeToken(String token) {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
configManager.updateXML(USER_CREDENTIALS_CONFIG, newRemoveTokenDocumentAlteration(token));
}
@Override
public void removeUserCredentialFromCollection(UserCredential userCredential, String collection) {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
configManager.updateXML(USER_CREDENTIALS_CONFIG, newRemoveUserDocumentAlteration(userCredential, collection));
}
@Override
public void removeGroup(String codeGroup) {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
configManager.updateXML(USER_CREDENTIALS_CONFIG, newRemoveGroupDocumentAlteration(codeGroup));
}
void registerListener(ConfigManager configManager) {
if (!configManager.exist(USER_CREDENTIALS_CONFIG)) {
createEmptyUserCredentialsConfig();
}
configManager.registerListener(USER_CREDENTIALS_CONFIG, this);
}
void createEmptyUserCredentialsConfig() {
Document document = new Document();
UserCredentialsWriter writer = new UserCredentialsWriter(document, newEncryptionServicesFactory());
writer.createEmptyUserCredentials();
configManager.add(USER_CREDENTIALS_CONFIG, document);
}
Factory<EncryptionServices> newEncryptionServicesFactory() {
return new Factory<EncryptionServices>() {
@Override
public EncryptionServices get() {
return modelLayerFactory.newEncryptionServices();
}
};
}
@Override
public void onConfigUpdated(String configPath) {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
Document document = configManager.getXML(USER_CREDENTIALS_CONFIG).getDocument();
UserCredentialsReader reader = newUserCredencialsReader(document);
cache = Collections.unmodifiableMap(reader.readAll(collectionsListManager.getCollections()));
}
DocumentAlteration newAddUpdateUserCredentialsDocumentAlteration(final UserCredential userCredential) {
return new DocumentAlteration() {
@Override
public void alter(Document document) {
newUserCredencialsWriter(document).addUpdate(userCredential);
}
};
}
DocumentAlteration newRemoveCollectionDocumentAlteration(final String collection) {
return new DocumentAlteration() {
@Override
public void alter(Document document) {
newUserCredencialsWriter(document).removeCollection(collection);
}
};
}
DocumentAlteration newRemoveTokenDocumentAlteration(final String token) {
return new DocumentAlteration() {
@Override
public void alter(Document document) {
newUserCredencialsWriter(document).removeToken(token);
}
};
}
DocumentAlteration newRemoveUserDocumentAlteration(final UserCredential userCredential, final String collection) {
return new DocumentAlteration() {
@Override
public void alter(Document document) {
newUserCredencialsWriter(document).removeUserFromCollection(userCredential, collection);
}
};
}
DocumentAlteration newRemoveGroupDocumentAlteration(final String groupCode) {
return new DocumentAlteration() {
@Override
public void alter(Document document) {
newUserCredencialsWriter(document).removeGroup(groupCode);
}
};
}
UserCredentialsWriter newUserCredencialsWriter(Document document) {
return new UserCredentialsWriter(document, newEncryptionServicesFactory());
}
UserCredentialsReader newUserCredencialsReader(Document document) {
return new UserCredentialsReader(document, newEncryptionServicesFactory());
}
ConfigManager getConfigManager() {
return configManager;
}
@Override
public String getUsernameByServiceKey(String serviceKey) {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
initIfRequired();
for (String usernameWithServiceKey : usersWithServiceKey) {
UserCredential userCredential = getUserCredential(usernameWithServiceKey);
if (userCredential != null && serviceKey.equals(userCredential.getServiceKey())) {
return userCredential.getUsername();
}
}
return null;
}
synchronized private void initIfRequired() {
if (usersWithServiceKey == null) {
init();
}
}
@Override
public String getServiceKeyByToken(String token) {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
initIfRequired();
for (String usernameWithServiceKey : usersWithServiceKey) {
UserCredential userCredential = getUserCredential(usernameWithServiceKey);
if (userCredential.getAccessTokens().containsKey(token) && !userCredential.getAccessTokens().get(token)
.isBefore(new LocalDateTime())) {
return userCredential.getServiceKey();
}
}
return null;
}
@Override
public void close() {
}
@Override
public void removeTimedOutTokens() {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
for (UserCredential userCredential : getUserCredentials()) {
UserCredential modifiedUserCredential = null;
for (Map.Entry<String, LocalDateTime> token : userCredential.getAccessTokens().entrySet()) {
if (!token.getValue().isAfter(TimeProvider.getLocalDateTime())) {
if (modifiedUserCredential == null) {
modifiedUserCredential = userCredential;
}
modifiedUserCredential = modifiedUserCredential.withRemovedToken(token.getKey());
}
}
if (modifiedUserCredential != null) {
addUpdate(modifiedUserCredential);
}
}
}
private void sort(List<UserCredential> userCredentials) {
Collections.sort(userCredentials, new Comparator<UserCredential>() {
@Override
public int compare(UserCredential o1, UserCredential o2) {
return o1.getUsername().toLowerCase().compareTo(o2.getUsername().toLowerCase());
}
});
}
@Override
public void rewrite() {
Toggle.NEW_USERCREDENTIAL_SERVICES.ensureDisabled();
configManager.updateXML(USER_CREDENTIALS_CONFIG, new DocumentAlteration() {
@Override
public void alter(Document document) {
UserCredentialsReader reader = newUserCredencialsReader(document);
Map<String, UserCredential> allUsers = reader.readAll(collectionsListManager.getCollections());
newUserCredencialsWriter(document).rewrite(allUsers.values());
}
});
Document document = configManager.getXML(USER_CREDENTIALS_CONFIG).getDocument();
UserCredentialsReader reader = newUserCredencialsReader(document);
cache = Collections.unmodifiableMap(reader.readAll(collectionsListManager.getCollections()));
}
}