/*
* Copyright (c) 2005-2010, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.identity.user.store.configuration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.core.AbstractAdmin;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.user.store.configuration.beans.RandomPassword;
import org.wso2.carbon.identity.user.store.configuration.beans.RandomPasswordContainer;
import org.wso2.carbon.identity.user.store.configuration.cache.RandomPasswordContainerCache;
import org.wso2.carbon.identity.user.store.configuration.dto.PropertyDTO;
import org.wso2.carbon.identity.user.store.configuration.dto.UserStoreDTO;
import org.wso2.carbon.identity.user.store.configuration.internal.UserStoreConfigListenersHolder;
import org.wso2.carbon.identity.user.store.configuration.listener.UserStoreConfigListener;
import org.wso2.carbon.identity.user.store.configuration.utils.IdentityUserStoreMgtException;
import org.wso2.carbon.identity.user.store.configuration.utils.SecondaryUserStoreConfigurationUtil;
import org.wso2.carbon.identity.user.store.configuration.utils.UserStoreConfigurationConstant;
import org.wso2.carbon.ndatasource.common.DataSourceException;
import org.wso2.carbon.ndatasource.core.DataSourceManager;
import org.wso2.carbon.ndatasource.core.services.WSDataSourceMetaInfo;
import org.wso2.carbon.ndatasource.core.services.WSDataSourceMetaInfo.WSDataSourceDefinition;
import org.wso2.carbon.ndatasource.rdbms.RDBMSConfiguration;
import org.wso2.carbon.user.api.Properties;
import org.wso2.carbon.user.api.Property;
import org.wso2.carbon.user.api.RealmConfiguration;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.core.UserCoreConstants;
import org.wso2.carbon.user.core.UserStoreConfigConstants;
import org.wso2.carbon.user.core.common.AbstractUserStoreManager;
import org.wso2.carbon.user.core.config.XMLProcessorUtils;
import org.wso2.carbon.user.core.jdbc.JDBCRealmConstants;
import org.wso2.carbon.user.core.tenant.TenantCache;
import org.wso2.carbon.user.core.tenant.TenantIdKey;
import org.wso2.carbon.user.core.tracker.UserStoreManagerRegistry;
import org.wso2.carbon.user.core.util.UserCoreUtil;
import org.wso2.carbon.utils.CarbonUtils;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import org.xml.sax.SAXException;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class UserStoreConfigAdminService extends AbstractAdmin {
public static final Log log = LogFactory.getLog(UserStoreConfigAdminService.class);
public static final String DISABLED = "Disabled";
public static final String DESCRIPTION = "Description";
public static final String USERSTORES = "userstores";
private static final String deploymentDirectory = CarbonUtils.getCarbonRepository() + USERSTORES;
XMLProcessorUtils xmlProcessorUtils = new XMLProcessorUtils();
/**
* Get details of current secondary user store configurations
*
* @return : Details of all the configured secondary user stores
* @throws UserStoreException
*/
public UserStoreDTO[] getSecondaryRealmConfigurations() throws IdentityUserStoreMgtException {
ArrayList<UserStoreDTO> domains = new ArrayList<UserStoreDTO>();
RealmConfiguration secondaryRealmConfiguration = null;
try {
secondaryRealmConfiguration = CarbonContext.getThreadLocalCarbonContext().getUserRealm().
getRealmConfiguration().getSecondaryRealmConfig();
} catch (UserStoreException e) {
String errorMessage = "Error while retrieving user store configurations";
log.error(errorMessage, e);
throw new IdentityUserStoreMgtException(errorMessage);
}
//not editing primary store
if (secondaryRealmConfiguration == null) {
return null;
} else {
do {
Map<String, String> userStoreProperties = secondaryRealmConfiguration.getUserStoreProperties();
UserStoreDTO userStoreDTO = new UserStoreDTO();
String uuid = userStoreProperties.get(UserStoreConfigurationConstant.UNIQUE_ID_CONSTANT);
if (uuid == null) {
uuid = UUID.randomUUID().toString();
}
String randomPhrase = UserStoreConfigurationConstant.RANDOM_PHRASE_PREFIX + uuid;
String className = secondaryRealmConfiguration.getUserStoreClass();
userStoreDTO.setClassName(secondaryRealmConfiguration.getUserStoreClass());
userStoreDTO.setDescription(secondaryRealmConfiguration.getUserStoreProperty(DESCRIPTION));
userStoreDTO.setDomainId(secondaryRealmConfiguration.getUserStoreProperty(UserStoreConfigConstants.DOMAIN_NAME));
if (userStoreProperties.get(DISABLED) != null) {
userStoreDTO.setDisabled(Boolean.valueOf(userStoreProperties.get(DISABLED)));
}
userStoreProperties.put("Class", className);
userStoreProperties.put(UserStoreConfigurationConstant.UNIQUE_ID_CONSTANT, uuid);
RandomPassword[] randomPasswords = getRandomPasswordProperties(className, randomPhrase,
secondaryRealmConfiguration);
if (randomPasswords != null) {
updatePasswordContainer(randomPasswords, uuid);
}
String originalPassword = null;
if (userStoreProperties.containsKey(UserStoreConfigConstants.connectionPassword)) {
originalPassword = userStoreProperties.get(UserStoreConfigConstants.connectionPassword);
userStoreProperties.put(UserStoreConfigConstants.connectionPassword, randomPhrase);
}
if (userStoreProperties.containsKey(JDBCRealmConstants.PASSWORD)) {
originalPassword = userStoreProperties.get(JDBCRealmConstants.PASSWORD);
userStoreProperties.put(JDBCRealmConstants.PASSWORD, randomPhrase);
}
userStoreDTO.setProperties(convertMapToArray(userStoreProperties));
//Now revert back to original password
if (userStoreProperties.containsKey(UserStoreConfigConstants.connectionPassword)) {
if (originalPassword != null) {
userStoreProperties.put(UserStoreConfigConstants.connectionPassword, originalPassword);
}
}
if (userStoreProperties.containsKey(JDBCRealmConstants.PASSWORD)) {
if (originalPassword != null) {
userStoreProperties.put(JDBCRealmConstants.PASSWORD, originalPassword);
}
}
domains.add(userStoreDTO);
secondaryRealmConfiguration = secondaryRealmConfiguration.getSecondaryRealmConfig();
} while (secondaryRealmConfiguration != null);
}
return domains.toArray(new UserStoreDTO[domains.size()]);
}
/**
* Get user store properties of a given active user store manager as an array
*
* @param properties: properties of the user store
* @return key#value
*/
private PropertyDTO[] convertMapToArray(Map<String, String> properties) {
Set<Map.Entry<String, String>> propertyEntries = properties.entrySet();
ArrayList<PropertyDTO> propertiesList = new ArrayList<PropertyDTO>();
String key;
String value;
for (Map.Entry<String, String> entry : propertyEntries) {
key = (String) entry.getKey();
value = (String) entry.getValue();
PropertyDTO propertyDTO = new PropertyDTO(key, value);
propertiesList.add(propertyDTO);
}
return propertiesList.toArray(new PropertyDTO[propertiesList.size()]);
}
/**
* Get available user store manager implementations
*
* @return: Available implementations for user store managers
*/
public String[] getAvailableUserStoreClasses() throws IdentityUserStoreMgtException {
Set<String> classNames = UserStoreManagerRegistry.getUserStoreManagerClasses();
return classNames.toArray(new String[classNames.size()]);
}
/**
* Get User Store Manager default properties for a given implementation
*
* @param className:Implementation class name for the user store
* @return : list of default properties(mandatory+optional)
*/
public Properties getUserStoreManagerProperties(String className) throws IdentityUserStoreMgtException {
Properties properties = UserStoreManagerRegistry.getUserStoreProperties(className);
if (properties != null && properties.getOptionalProperties() != null) {
Property[] optionalProperties = properties.getOptionalProperties();
boolean foundUniqueIDProperty = false;
for (Property property : optionalProperties) {
if (UserStoreConfigurationConstant.UNIQUE_ID_CONSTANT.equals(property.getName())) {
foundUniqueIDProperty = true;
break;
}
}
if (!foundUniqueIDProperty) {
if (log.isDebugEnabled()) {
log.debug("Inserting property : " + UserStoreConfigurationConstant.UNIQUE_ID_CONSTANT +
" since " + UserStoreConfigurationConstant.UNIQUE_ID_CONSTANT +
" property not defined as an optional property in " + className + " class");
}
List<Property> optionalPropertyList = new ArrayList<>(Arrays.asList(optionalProperties));
Property uniqueIDProperty = new Property(
UserStoreConfigurationConstant.UNIQUE_ID_CONSTANT, "", "", null);
optionalPropertyList.add(uniqueIDProperty);
properties.setOptionalProperties(
optionalPropertyList.toArray(new Property[optionalPropertyList.size()]));
}
}
return properties;
}
/**
* Save the sent configuration to xml file
*
* @param userStoreDTO: Represent the configuration of user store
* @throws DataSourceException
* @throws TransformerException
* @throws ParserConfigurationException
*/
public void addUserStore(UserStoreDTO userStoreDTO) throws IdentityUserStoreMgtException {
String domainName = userStoreDTO.getDomainId();
try {
xmlProcessorUtils.isValidDomain(domainName, true);
} catch (UserStoreException e) {
String errorMessage = e.getMessage();
log.error(errorMessage, e);
throw new IdentityUserStoreMgtException(errorMessage);
}
File userStoreConfigFile = createConfigurationFile(domainName);
// This is a redundant check
if (userStoreConfigFile.exists()) {
String errorMessage = "Cannot add user store " + domainName + ". User store already exists.";
log.error(errorMessage);
throw new IdentityUserStoreMgtException(errorMessage);
}
try {
writeUserMgtXMLFile(userStoreConfigFile, userStoreDTO, false);
if (log.isDebugEnabled()) {
log.debug("New user store successfully written to the file" + userStoreConfigFile.getAbsolutePath());
}
} catch (IdentityUserStoreMgtException e) {
String errorMessage = e.getMessage();
log.error(errorMessage, e);
throw new IdentityUserStoreMgtException(errorMessage);
}
}
/**
* Edit currently existing user store
*
* @param userStoreDTO: Represent the configuration of user store
* @throws DataSourceException
* @throws TransformerException
* @throws ParserConfigurationException
*/
public void editUserStore(UserStoreDTO userStoreDTO) throws IdentityUserStoreMgtException {
String domainName = userStoreDTO.getDomainId();
boolean isValidDomain = false;
try {
isValidDomain = xmlProcessorUtils.isValidDomain(domainName, false);
} catch (UserStoreException e) {
String errorMessage = e.getMessage();
log.error(errorMessage, e);
throw new IdentityUserStoreMgtException(errorMessage);
}
if (isValidDomain) {
File userStoreConfigFile = createConfigurationFile(domainName);
if (!userStoreConfigFile.exists()) {
String msg = "Cannot edit user store " + domainName + ". User store cannot be edited.";
log.error(msg);
throw new IdentityUserStoreMgtException(msg);
}
try {
writeUserMgtXMLFile(userStoreConfigFile, userStoreDTO, true);
if (log.isDebugEnabled()) {
log.debug("Edited user store successfully written to the file" + userStoreConfigFile.getAbsolutePath());
}
} catch (IdentityUserStoreMgtException e) {
String errorMessage = e.getMessage();
log.error(errorMessage, e);
throw new IdentityUserStoreMgtException(errorMessage);
}
} else {
String errorMessage = "Trying to edit an invalid domain : " + domainName;
throw new IdentityUserStoreMgtException(errorMessage);
}
}
/**
* Edit currently existing user store with a change of its domain name
*
* @param userStoreDTO: Represent the configuration of new user store
* @param previousDomainName
* @throws DataSourceException
* @throws TransformerException
* @throws ParserConfigurationException
*/
public void editUserStoreWithDomainName(String previousDomainName, UserStoreDTO userStoreDTO)
throws IdentityUserStoreMgtException{
boolean isDebugEnabled = log.isDebugEnabled();
String domainName = userStoreDTO.getDomainId();
if (isDebugEnabled) {
log.debug("Changing user store " + previousDomainName + " to " + domainName);
}
File userStoreConfigFile = null;
File previousUserStoreConfigFile = null;
String fileName = domainName.replace(".", "_");
String previousFileName = previousDomainName.replace(".", "_");
if(!IdentityUtil.isValidFileName(fileName)){
String message = "Provided domain name : '" + domainName + "' is invalid.";
log.error(message);
throw new IdentityUserStoreMgtException(message);
}
if(!IdentityUtil.isValidFileName(previousFileName)){
String message = "Provided domain name : '" + previousDomainName + "' is invalid.";
log.error(message);
throw new IdentityUserStoreMgtException(message);
}
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
if (tenantId == MultitenantConstants.SUPER_TENANT_ID) {
File userStore = new File(deploymentDirectory);
if (!userStore.exists()) {
if (new File(deploymentDirectory).mkdir()) {
//folder 'userstores' created
log.info("folder 'userstores' created for super tenant " + fileName);
} else {
log.error("Error at creating 'userstores' directory to store configurations for super tenant");
}
}
userStoreConfigFile = new File(deploymentDirectory + File.separator + fileName + ".xml");
previousUserStoreConfigFile = new File(deploymentDirectory + File.separator + previousFileName + ".xml");
} else {
String tenantFilePath = CarbonUtils.getCarbonTenantsDirPath();
tenantFilePath = tenantFilePath + File.separator + tenantId + File.separator + USERSTORES;
File userStore = new File(tenantFilePath);
if (!userStore.exists()) {
if (new File(tenantFilePath).mkdir()) {
//folder 'userstores' created
log.info("folder 'userstores' created for tenant " + tenantId);
} else {
log.error("Error at creating 'userstores' directory to store configurations for tenant:" + tenantId);
}
}
userStoreConfigFile = new File(tenantFilePath + File.separator + fileName + ".xml");
previousUserStoreConfigFile = new File(tenantFilePath + File.separator + previousFileName + ".xml");
}
if (!previousUserStoreConfigFile.exists()) {
String errorMessage = "Cannot update user store domain name. Previous domain name " + previousDomainName +
" does not exists.";
throw new IdentityUserStoreMgtException(errorMessage);
}
if (userStoreConfigFile.exists()) {
String errorMessage = "Cannot update user store domain name. An user store already exists with new domain " +
domainName + ".";
throw new IdentityUserStoreMgtException(errorMessage);
}
try {
// Run pre user-store name update listeners
List<UserStoreConfigListener> userStoreConfigListeners = UserStoreConfigListenersHolder.getInstance()
.getUserStoreConfigListeners();
for (UserStoreConfigListener userStoreConfigListener : userStoreConfigListeners) {
userStoreConfigListener.onUserStoreNamePreUpdate(CarbonContext.getThreadLocalCarbonContext().getTenantId
(), previousDomainName, domainName);
}
// Update persisted domain name
AbstractUserStoreManager userStoreManager = (AbstractUserStoreManager) CarbonContext.
getThreadLocalCarbonContext().getUserRealm().getUserStoreManager();
userStoreManager.updatePersistedDomain(previousDomainName, domainName);
if (log.isDebugEnabled()) {
log.debug("Renamed persisted domain name from" + previousDomainName + " to " + domainName +
" of tenant:" + tenantId + " from UM_DOMAIN.");
}
} catch (UserStoreException e) {
String errorMessage = "Error while updating user store domain : " + domainName;
log.error(errorMessage, e);
throw new IdentityUserStoreMgtException(errorMessage);
}
try {
previousUserStoreConfigFile.delete();
writeUserMgtXMLFile(userStoreConfigFile, userStoreDTO, true);
} catch (IdentityUserStoreMgtException e) {
String errorMessage = e.getMessage();
log.error(errorMessage, e);
throw new IdentityUserStoreMgtException(errorMessage);
}
}
/**
* Deletes the user store specified
*
* @param domainName: domain name of the user stores to be deleted
*/
public void deleteUserStore(String domainName) throws IdentityUserStoreMgtException {
deleteUserStoresSet(new String[] {domainName});
}
/**
* Delete the given list of user stores
*
* @param domains: domain names of user stores to be deleted
*/
public void deleteUserStoresSet(String[] domains) throws IdentityUserStoreMgtException {
boolean isDebugEnabled = log.isDebugEnabled();
if (domains == null || domains.length <= 0) {
throw new IdentityUserStoreMgtException("No selected user stores to delete");
}
if (!validateDomainsForDelete(domains)) {
if (log.isDebugEnabled()) {
log.debug("Failed to delete user store : No privileges to delete own user store configurations ");
}
throw new IdentityUserStoreMgtException("No privileges to delete own user store configurations.");
}
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
String path;
if (tenantId == MultitenantConstants.SUPER_TENANT_ID) {
path = deploymentDirectory;
} else {
path = CarbonUtils.getCarbonTenantsDirPath() + File.separator + tenantId + File.separator + USERSTORES;
}
File file = new File(path);
for (String domainName : domains) {
if (isDebugEnabled) {
log.debug("Deleting, .... " + domainName + " domain.");
}
try {
// Run pre user-store name update listeners
List<UserStoreConfigListener> userStoreConfigListeners = UserStoreConfigListenersHolder.getInstance()
.getUserStoreConfigListeners();
for (UserStoreConfigListener userStoreConfigListener : userStoreConfigListeners) {
userStoreConfigListener.onUserStorePreDelete(CarbonContext.getThreadLocalCarbonContext().getTenantId
(), domainName);
}
// Delete persisted domain name
AbstractUserStoreManager userStoreManager = (AbstractUserStoreManager) CarbonContext.
getThreadLocalCarbonContext().getUserRealm().getUserStoreManager();
userStoreManager.deletePersistedDomain(domainName);
if (isDebugEnabled) {
log.debug("Removed persisted domain name: " + domainName + " of tenant:" + tenantId + " from " +
"UM_DOMAIN.");
}
//clear cache to make the modification effective
UserCoreUtil.getRealmService().clearCachedUserRealm(tenantId);
TenantCache.getInstance().clearCacheEntry(new TenantIdKey(tenantId));
} catch (UserStoreException e) {
String errorMessage = "Error while deleting user store : " + domainName;
log.error(errorMessage, e);
throw new IdentityUserStoreMgtException(errorMessage);
}
// Delete file
deleteFile(file, domainName.replace(".", "_").concat(".xml"));
}
}
private boolean validateDomainsForDelete(String[] domains) {
String userDomain = IdentityUtil.extractDomainFromName(PrivilegedCarbonContext.getThreadLocalCarbonContext()
.getUsername());
for (String domain : domains) {
if (domain.equalsIgnoreCase(userDomain)) {
//Trying to delete own domain
return false;
}
}
return true;
}
/**
* Adds an array of properties
*
* @param propertyDTOs : List of user store properties
* @param doc: Document
* @param parent : Parent element of the properties to be added
*/
private void addProperties(String userStoreClass, PropertyDTO[] propertyDTOs, Document doc, Element parent,
boolean editSecondaryUserStore) throws IdentityUserStoreMgtException {
RandomPasswordContainer randomPasswordContainer = null;
if (editSecondaryUserStore) {
String uniqueID = getUniqueIDFromUserDTO(propertyDTOs);
randomPasswordContainer = getAndRemoveRandomPasswordContainer(uniqueID);
if (randomPasswordContainer == null) {
String errorMsg = "randomPasswordContainer is null for uniqueID therefore " +
"proceeding without encryption=" + uniqueID;
log.error(errorMsg);//need this error log to further identify the reason for throwing this exception
throw new IdentityUserStoreMgtException("Longer delay causes the edit operation be to " +
"abandoned");
}
}
//First check for mandatory field with #encrypt
Property[] mandatoryProperties = getMandatoryProperties(userStoreClass);
for (PropertyDTO propertyDTO : propertyDTOs) {
String propertyDTOName = propertyDTO.getName();
if (UserStoreConfigurationConstant.UNIQUE_ID_CONSTANT.equalsIgnoreCase(propertyDTOName)) {
continue;
}
String propertyDTOValue = propertyDTO.getValue();
if (propertyDTOValue != null) {
boolean encrypted = false;
if (isPropertyToBeEncrypted(mandatoryProperties, propertyDTOName)) {
if (randomPasswordContainer != null) {
RandomPassword randomPassword = getRandomPassword(randomPasswordContainer, propertyDTOName);
if (randomPassword != null) {
if (propertyDTOValue.equalsIgnoreCase(randomPassword.getRandomPhrase())) {
propertyDTOValue = randomPassword.getPassword();
}
}
}
try {
propertyDTOValue = SecondaryUserStoreConfigurationUtil.encryptPlainText(propertyDTOValue);
encrypted = true;
} catch (IdentityUserStoreMgtException e) {
log.error("addProperties failed to encrypt", e);
//its ok to continue from here
}
}
addProperty(propertyDTOName, propertyDTOValue, doc, parent, encrypted);
}
}
}
/**
* Adds a property
*
* @param name: Name of property
* @param value: Value
* @param doc: Document
* @param parent: Parent element of the property to be added as a child
*/
private void addProperty(String name, String value, Document doc, Element parent, boolean encrypted) {
Element property = doc.createElement("Property");
Attr attr;
if (encrypted) {
attr = doc.createAttribute("encrypted");
attr.setValue("true");
property.setAttributeNode(attr);
}
attr = doc.createAttribute("name");
attr.setValue(name);
property.setAttributeNode(attr);
property.setTextContent(value);
parent.appendChild(property);
}
private void deleteFile(File file, final String userStoreName) throws IdentityUserStoreMgtException {
if(!IdentityUtil.isValidFileName(userStoreName)) {
String message = "Provided domain name : '" + userStoreName + "' is invalid.";
log.error(message);
throw new IdentityUserStoreMgtException(message);
}
File[] deleteCandidates = file.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.equalsIgnoreCase(userStoreName);
}
});
for (File file1 : deleteCandidates) {
if (file1.delete()) {
log.info("File " + file.getName() + " deleted successfully");
} else {
log.error("error at deleting file:" + file.getName());
}
}
}
/**
* Update a domain to be disabled/enabled
*
* @param domain: Name of the domain to be updated
* @param isDisable : Whether to disable/enable domain(true/false)
*/
public void changeUserStoreState(String domain, Boolean isDisable) throws IdentityUserStoreMgtException,
TransformerConfigurationException {
String currentAuthorizedUserName = CarbonContext.getThreadLocalCarbonContext().getUsername();
int index = currentAuthorizedUserName.indexOf(UserCoreConstants.DOMAIN_SEPARATOR);
String currentUserDomain = null;
if (index > 0) {
currentUserDomain = currentAuthorizedUserName.substring(0, index);
}
if (currentUserDomain != null && currentUserDomain.equalsIgnoreCase(domain) && isDisable) {
log.error("Error while disabling user store from a user who is in the same user store.");
throw new IdentityUserStoreMgtException("Error while updating user store state.");
}
File userStoreConfigFile = createConfigurationFile(domain);
StreamResult result = new StreamResult(userStoreConfigFile);
if (!userStoreConfigFile.exists()) {
String errorMessage = "Cannot edit user store." + domain + " does not exist.";
throw new IdentityUserStoreMgtException(errorMessage);
}
DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = null;
try {
documentFactory.setFeature(UserStoreConfigurationConstant.EXTERNAL_GENERAL_ENTITIES_URI, false);
documentBuilder = documentFactory.newDocumentBuilder();
Document doc = documentBuilder.parse(userStoreConfigFile);
NodeList elements = doc.getElementsByTagName("Property");
for (int i = 0; i < elements.getLength(); i++) {
//Assumes a property element only have attribute 'name'
if ("Disabled".compareToIgnoreCase(elements.item(i).getAttributes().item(0).getNodeValue()) == 0) {
elements.item(i).setTextContent(String.valueOf(isDisable));
break;
}
}
DOMSource source = new DOMSource(doc);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "6");
transformer.transform(source, result);
if (log.isDebugEnabled()) {
log.debug("New state :" + isDisable + " of the user store \'" + domain + "\' successfully written to the file system");
}
} catch (ParserConfigurationException | SAXException | TransformerException | IOException e) {
log.error(e.getMessage(),e);
throw new IdentityUserStoreMgtException("Error while updating user store state",e);
}
}
/**
* Check the connection heath for JDBC userstores
* @param domainName
* @param driverName
* @param connectionURL
* @param username
* @param connectionPassword
* @param messageID
* @return
* @throws DataSourceException
*/
public boolean testRDBMSConnection(String domainName, String driverName, String connectionURL, String username,
String connectionPassword, String messageID) throws IdentityUserStoreMgtException {
RandomPasswordContainer randomPasswordContainer;
if (messageID != null) {
randomPasswordContainer = getRandomPasswordContainer(messageID);
if (randomPasswordContainer != null) {
RandomPassword randomPassword = getRandomPassword(randomPasswordContainer, JDBCRealmConstants.PASSWORD);
if (randomPassword != null) {
if (connectionPassword.equalsIgnoreCase(randomPassword.getRandomPhrase())) {
connectionPassword = randomPassword.getPassword();
}
}
}
}
WSDataSourceMetaInfo wSDataSourceMetaInfo = new WSDataSourceMetaInfo();
RDBMSConfiguration rdbmsConfiguration = new RDBMSConfiguration();
rdbmsConfiguration.setUrl(connectionURL);
rdbmsConfiguration.setUsername(username);
rdbmsConfiguration.setPassword(connectionPassword);
rdbmsConfiguration.setDriverClassName(driverName);
WSDataSourceDefinition wSDataSourceDefinition = new WSDataSourceDefinition();
ByteArrayOutputStream out = new ByteArrayOutputStream();
JAXBContext context;
try {
context = JAXBContext.newInstance(RDBMSConfiguration.class);
Marshaller marshaller = context.createMarshaller();
marshaller.marshal(rdbmsConfiguration, out);
} catch (JAXBException e) {
String errorMessage = "Error while checking RDBMS connection health";
log.error(errorMessage, e);
throw new IdentityUserStoreMgtException(errorMessage);
}
wSDataSourceDefinition.setDsXMLConfiguration(out.toString());
wSDataSourceDefinition.setType("RDBMS");
wSDataSourceMetaInfo.setName(domainName);
wSDataSourceMetaInfo.setDefinition(wSDataSourceDefinition);
try {
return DataSourceManager.getInstance().getDataSourceRepository().testDataSourceConnection(wSDataSourceMetaInfo.
extractDataSourceMetaInfo());
} catch (DataSourceException e) {
String errorMessage = e.getMessage();
// Does not print the error log since the log is already printed by DataSourceRepository
// log.error(message, e);
throw new IdentityUserStoreMgtException(errorMessage);
}
}
private File createConfigurationFile(String domainName) throws IdentityUserStoreMgtException {
String fileName = domainName.replace(".", "_");
if(!IdentityUtil.isValidFileName(fileName)){
String message = "Provided domain name : '" + domainName + "' is invalid.";
log.error(message);
throw new IdentityUserStoreMgtException(message);
}
File userStoreConfigFile;
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
if (tenantId == MultitenantConstants.SUPER_TENANT_ID) {
File userStore = new File(deploymentDirectory);
if (!userStore.exists()) {
if (new File(deploymentDirectory).mkdir()) {
//folder 'userstores' created
log.info("folder 'userstores' created to store configurations for super tenant");
} else {
log.error("Error at creating 'userstores' directory to store configurations for super tenant");
}
}
userStoreConfigFile = new File(deploymentDirectory + File.separator + fileName + ".xml");
} else {
String tenantFilePath = CarbonUtils.getCarbonTenantsDirPath();
tenantFilePath = tenantFilePath + File.separator + tenantId + File.separator + USERSTORES;
File userStore = new File(tenantFilePath);
if (!userStore.exists()) {
if (new File(tenantFilePath).mkdir()) {
//folder 'userstores' created
log.info("folder 'userstores' created to store configurations for tenant = " + tenantId);
} else {
log.error("Error at creating 'userstores' directory to store configurations for tenant:" + tenantId);
}
}
userStoreConfigFile = new File(tenantFilePath + File.separator + fileName + ".xml");
}
return userStoreConfigFile;
}
private void writeUserMgtXMLFile(File userStoreConfigFile, UserStoreDTO userStoreDTO,
boolean editSecondaryUserStore) throws IdentityUserStoreMgtException {
StreamResult result = new StreamResult(userStoreConfigFile);
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
//create UserStoreManager element
Element userStoreElement = doc.createElement(UserCoreConstants.RealmConfig.LOCAL_NAME_USER_STORE_MANAGER);
doc.appendChild(userStoreElement);
Attr attrClass = doc.createAttribute("class");
attrClass.setValue(userStoreDTO.getClassName());
userStoreElement.setAttributeNode(attrClass);
addProperties(userStoreDTO.getClassName(), userStoreDTO.getProperties(), doc, userStoreElement,
editSecondaryUserStore);
addProperty(UserStoreConfigConstants.DOMAIN_NAME, userStoreDTO.getDomainId(), doc, userStoreElement, false);
addProperty(DESCRIPTION, userStoreDTO.getDescription(), doc, userStoreElement, false);
DOMSource source = new DOMSource(doc);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "6");
transformer.transform(source, result);
} catch (ParserConfigurationException e) {
String errMsg = " Error occurred due to serious parser configuration exception of " + userStoreConfigFile;
throw new IdentityUserStoreMgtException(errMsg, e);
} catch (TransformerException e) {
String errMsg = " Error occurred during the transformation process of " + userStoreConfigFile;
throw new IdentityUserStoreMgtException(errMsg, e);
}
}
/**
* Obtains the mandatory properties for a given userStoreClass
*
* @param userStoreClass userStoreClass name
* @return Property[] of Mandatory Properties
*/
private Property[] getMandatoryProperties(String userStoreClass) {
return UserStoreManagerRegistry.getUserStoreProperties(userStoreClass).getMandatoryProperties();
}
/**
* Check whether the given property should be encrypted or not.
*
* @param mandatoryProperties mandatory property array
* @param propertyName property name
* @return returns true if the property should be encrypted
*/
private boolean isPropertyToBeEncrypted(Property[] mandatoryProperties,
String propertyName) {
for (Property property : mandatoryProperties) {
if (propertyName.equalsIgnoreCase(property.getName())) {
return property.getDescription().contains(UserStoreConfigurationConstant.ENCRYPT_TEXT);
}
}
return false;
}
/**
* Generate the RandomPassword[] from secondaryRealmConfiguration for given userStoreClass
*
* @param userStoreClass Extract the mandatory properties of this class
* @param randomPhrase The randomly generated keyword which will be stored in
* RandomPassword object
* @param secondaryRealmConfiguration RealmConfiguration object consists the properties
* @return RandomPassword[] array for each property
*/
private RandomPassword[] getRandomPasswordProperties(String userStoreClass,
String randomPhrase, RealmConfiguration secondaryRealmConfiguration) {
//First check for mandatory field with #encrypt
Property[] mandatoryProperties = getMandatoryProperties(userStoreClass);
ArrayList<RandomPassword> randomPasswordArrayList = new ArrayList<RandomPassword>();
for (Property property : mandatoryProperties) {
String propertyName = property.getName();
if (property.getDescription().contains(UserStoreConfigurationConstant.ENCRYPT_TEXT)) {
RandomPassword randomPassword = new RandomPassword();
randomPassword.setPropertyName(propertyName);
randomPassword.setPassword(secondaryRealmConfiguration.getUserStoreProperty(propertyName));
randomPassword.setRandomPhrase(randomPhrase);
randomPasswordArrayList.add(randomPassword);
}
}
return randomPasswordArrayList.toArray(new RandomPassword[randomPasswordArrayList.size()]);
}
/**
* Create and update the RandomPasswordContainer with given unique ID and randomPasswords array
*
* @param randomPasswords array contains the elements to be encrypted with thier random
* password phrase, password and unique id
* @param uuid Unique id of the RandomPasswordContainer
*/
private void updatePasswordContainer(RandomPassword[] randomPasswords, String uuid) {
if (randomPasswords != null) {
if (log.isDebugEnabled()) {
log.debug("updatePasswordContainer reached for number of random password properties length = " +
randomPasswords.length);
}
RandomPasswordContainer randomPasswordContainer = new RandomPasswordContainer();
randomPasswordContainer.setRandomPasswords(randomPasswords);
randomPasswordContainer.setUniqueID(uuid);
RandomPasswordContainerCache.getInstance().getRandomPasswordContainerCache().put(uuid,
randomPasswordContainer);
}
}
/**
* Get the RandomPasswordContainer object from the cache for given unique id
*
* @param uniqueID Get and Remove the unique id for that particualr cache
* @return RandomPasswordContainer of particular unique ID
*/
private RandomPasswordContainer getAndRemoveRandomPasswordContainer(String uniqueID) {
return RandomPasswordContainerCache.getInstance().getRandomPasswordContainerCache().getAndRemove(uniqueID);
}
/**
* Get the RandomPasswordContainer object from the cache for given unique id
*
* @param uniqueID Get the unique id for that particular cache
* @return RandomPasswordContainer of particular unique ID
*/
private RandomPasswordContainer getRandomPasswordContainer(String uniqueID) {
return RandomPasswordContainerCache.getInstance().getRandomPasswordContainerCache().get(uniqueID);
}
/**
* Obtain the UniqueID ID constant value from the propertyDTO object which was set well
* before sending the edit request.
*
* @param propertyDTOs PropertyDTO[] object passed from JSP page
* @return unique id string value
*/
private String getUniqueIDFromUserDTO(PropertyDTO[] propertyDTOs) {
int length = propertyDTOs.length;
for (int i = length - 1; i >= 0; i--) {
PropertyDTO propertyDTO = propertyDTOs[i];
if (propertyDTO != null && propertyDTO.getName() != null && propertyDTO.getName()
.equalsIgnoreCase(UserStoreConfigurationConstant.UNIQUE_ID_CONSTANT)) {
return propertyDTO.getValue();
}
}
return null;
}
/**
* Finds the RandomPassword object for a given propertyName in the RandomPasswordContainer
* ( Which is unique per uniqueID )
*
* @param randomPasswordContainer RandomPasswordContainer object of an unique id
* @param propertyName RandomPassword object to be obtained for that property
* @return Returns the RandomPassword object from the
*/
private RandomPassword getRandomPassword(RandomPasswordContainer randomPasswordContainer,
String propertyName) {
RandomPassword[] randomPasswords = randomPasswordContainer.getRandomPasswords();
if (randomPasswords != null) {
for (RandomPassword randomPassword : randomPasswords) {
if (randomPassword.getPropertyName().equalsIgnoreCase(propertyName)) {
return randomPassword;
}
}
}
return null;
}
/**
*
* @param message
* @param e
* @throws IdentityUserStoreMgtException
*/
private void handleException(String message, Exception e) throws IdentityUserStoreMgtException {
log.error(message, e);
throw new IdentityUserStoreMgtException(message);
}
}