/* * Copyright (c) 2014, 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.mgt; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.mgt.constants.IdentityMgtConstants; import org.wso2.carbon.identity.mgt.mail.DefaultEmailSendingModule; import org.wso2.carbon.identity.mgt.password.DefaultPasswordGenerator; import org.wso2.carbon.identity.mgt.password.RandomPasswordGenerator; import org.wso2.carbon.identity.mgt.policy.PolicyEnforcer; import org.wso2.carbon.identity.mgt.policy.PolicyRegistry; import org.wso2.carbon.identity.mgt.store.RegistryRecoveryDataStore; import org.wso2.carbon.identity.mgt.store.UserIdentityDataStore; import org.wso2.carbon.identity.mgt.store.UserRecoveryDataStore; import org.wso2.carbon.identity.mgt.store.UserStoreBasedIdentityDataStore; import org.wso2.carbon.user.api.RealmConfiguration; import org.wso2.carbon.user.core.jdbc.JDBCRealmConstants; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * encapsulates recovery config data */ public class IdentityMgtConfig { private static final Log log = LogFactory.getLog(IdentityMgtConfig.class); private static IdentityMgtConfig identityMgtConfig; private boolean saasEnable; private boolean listenerEnable; private int noOfUserChallenges; private boolean notificationInternallyManaged; private boolean captchaVerificationInternallyManaged; private String challengeQuestionSeparator; private int authPolicyMaxLoginAttempts; private int temporaryPasswordExpireTime; private boolean enableTemporaryPassword; private boolean enableAuthPolicy; private boolean authPolicyOneTimePasswordCheck; private boolean authPolicyExpirePasswordCheck; private int authPolicyLockingTime; private int authPolicyPasswordExpireTime; private int notificationExpireTime; private boolean authPolicyAccountLockCheck; private boolean authPolicyAccountExistCheck; private boolean authPolicyAccountLockOnFailure; private boolean authPolicyAccountLockOnCreation; private boolean enableUserAccountVerification; private boolean userAccountVerificationByUser; private boolean temporaryPasswordOneTime; private String userAccountVerificationRole; private boolean notificationSending; private String digsestFunction; private RandomPasswordGenerator passwordGenerator; private UserIdentityDataStore identityDataStore; private UserRecoveryDataStore recoveryDataStore; private List<NotificationSendingModule> sendingModules = new ArrayList<NotificationSendingModule>(); private List<String> notificationTypes = new ArrayList<String>(); private String recoveryClaim; private PolicyRegistry policyRegistry = new PolicyRegistry(); protected Properties properties = new Properties(); private long registryCleanUpPeriod; /* * Define the pattern of the configuration file. Assume following * pattern in config. * Eg. Password.policy.extensions.1.min.length=6 */ private Pattern propertyPattern = Pattern.compile("(\\.\\d\\.)"); public IdentityMgtConfig(RealmConfiguration configuration) { InputStream inStream = null; File pipConfigXml = new File(IdentityUtil.getIdentityConfigDirPath(), IdentityMgtConstants.PropertyConfig .CONFIG_FILE_NAME); if (pipConfigXml.exists()) { try { inStream = new FileInputStream(pipConfigXml); properties.load(inStream); } catch (FileNotFoundException e) { log.error("Can not load identity-mgt properties file ", e); } catch (IOException e) { log.error("Can not load identity-mgt properties file ", e); } finally { if (inStream != null) { try { inStream.close(); } catch (IOException e) { log.error("Error while closing stream ", e); } } } } try { String notificationInternallyManagedProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.NOTIFICATION_SEND_INTERNALLY); if (notificationInternallyManagedProperty != null) { this.notificationInternallyManaged = Boolean. parseBoolean(notificationInternallyManagedProperty.trim()); } String saasEnableProperty = properties.getProperty(IdentityMgtConstants.PropertyConfig.USER_INFO_RECOVERY_SAA_SENABLE); if (saasEnableProperty != null) { this.saasEnable = Boolean.parseBoolean(saasEnableProperty.trim()); } String listenerEnableProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.IDENTITY_LISTENER_ENABLE); if (listenerEnableProperty != null) { this.listenerEnable = Boolean.parseBoolean(listenerEnableProperty.trim()); } String notificationSendingProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.NOTIFICATION_SEND_ENABLE); if (notificationSendingProperty != null) { this.notificationSending = Boolean.parseBoolean(notificationSendingProperty.trim()); } String recoveryClaimProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.RECOVERY_CLAIM); if (recoveryClaimProperty != null) { this.recoveryClaim = recoveryClaimProperty.trim(); } String captchaVerificationInternallyManagedProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.CAPTCHA_VERIFICATION_INTERNALLY); if (captchaVerificationInternallyManagedProperty != null) { this.captchaVerificationInternallyManaged = Boolean. parseBoolean(captchaVerificationInternallyManagedProperty.trim()); } String enableUserAccountVerificationProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.ACCOUNT_VERIFICATION_ENABLE); if (enableUserAccountVerificationProperty != null) { this.enableUserAccountVerification = Boolean.parseBoolean(enableUserAccountVerificationProperty.trim()); } String userAccountVerificationRoleProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.ACCOUNT_VERIFICATION_ROLE); if (userAccountVerificationRoleProperty != null && userAccountVerificationRoleProperty.trim().length() > 0) { this.userAccountVerificationRole = userAccountVerificationRoleProperty; } else { this.userAccountVerificationByUser = true; } String allowTemporaryPasswordProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.TEMPORARY_PASSWORD_ENABLE); if (allowTemporaryPasswordProperty != null) { this.enableTemporaryPassword = Boolean.parseBoolean(allowTemporaryPasswordProperty.trim()); } String temporaryPasswordExpireTimeProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.TEMPORARY_PASSWORD_EXPIRE_TIME); if (temporaryPasswordExpireTimeProperty != null) { this.temporaryPasswordExpireTime = Integer.parseInt(temporaryPasswordExpireTimeProperty.trim()); } String temporaryPasswordOneTimeProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.TEMPORARY_PASSWORD_ONETIME); if (temporaryPasswordOneTimeProperty != null) { this.temporaryPasswordOneTime = Boolean.parseBoolean(temporaryPasswordOneTimeProperty.trim()); } String enableAuthPolicyProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.AUTH_POLICY_ENABLE); if (enableAuthPolicyProperty != null) { this.enableAuthPolicy = Boolean.parseBoolean(enableAuthPolicyProperty.trim()); } String oneTimePasswordCheck = properties. getProperty(IdentityMgtConstants.PropertyConfig.AUTH_POLICY_PASSWORD_ONE_TIME); if (oneTimePasswordCheck != null) { this.authPolicyOneTimePasswordCheck = Boolean.parseBoolean(oneTimePasswordCheck.trim()); } String maxLoginAttemptProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.AUTH_POLICY_ACCOUNT_LOCKING_FAIL_ATTEMPTS); if (maxLoginAttemptProperty != null) { this.authPolicyMaxLoginAttempts = Integer.parseInt(maxLoginAttemptProperty.trim()); } if (this.authPolicyMaxLoginAttempts == 0) { // default value is set this.authPolicyMaxLoginAttempts = 10; } String expirePasswordCheck = properties. getProperty(IdentityMgtConstants.PropertyConfig.AUTH_POLICY_PASSWORD_EXPIRE); if (expirePasswordCheck != null) { this.authPolicyExpirePasswordCheck = Boolean.parseBoolean(expirePasswordCheck.trim()); } String authPolicyLockingTimeProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.AUTH_POLICY_ACCOUNT_LOCKING_TIME); if (authPolicyLockingTimeProperty != null) { this.authPolicyLockingTime = Integer.parseInt(authPolicyLockingTimeProperty.trim()); } String authPolicyPasswordExpireTimeProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.AUTH_POLICY_PASSWORD_EXPIRE_TIME); if (authPolicyPasswordExpireTimeProperty != null) { this.authPolicyPasswordExpireTime = Integer.parseInt(authPolicyPasswordExpireTimeProperty.trim()); } String notificationExpireTimeProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.NOTIFICATION_LINK_EXPIRE_TIME); if (notificationExpireTimeProperty != null) { this.notificationExpireTime = Integer.parseInt(notificationExpireTimeProperty.trim()); } String authPolicyAccountLockCheckProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.AUTH_POLICY_ACCOUNT_LOCK); if (authPolicyAccountLockCheckProperty != null) { this.authPolicyAccountLockCheck = Boolean.parseBoolean(authPolicyAccountLockCheckProperty.trim()); } String authPolicyAccountExistCheckProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.AUTH_POLICY_ACCOUNT_EXIST); if (authPolicyAccountExistCheckProperty != null) { this.authPolicyAccountExistCheck = Boolean.parseBoolean(authPolicyAccountExistCheckProperty.trim()); } String authPolicyAccountLockOnFailureProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.AUTH_POLICY_LOCK_ON_FAILURE); if (authPolicyAccountLockOnFailureProperty != null) { this.authPolicyAccountLockOnFailure = Boolean.parseBoolean(authPolicyAccountLockOnFailureProperty.trim()); } String authPolicyAccountLockOnCreationProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.AUTH_POLICY_ACCOUNT_LOCK_ON_CREATION); if (authPolicyAccountLockOnCreationProperty != null) { this.authPolicyAccountLockOnCreation = Boolean.parseBoolean(authPolicyAccountLockOnCreationProperty.trim()); } String digsestFunctionProperty = configuration.getUserStoreProperties().get(JDBCRealmConstants.DIGEST_FUNCTION); if (digsestFunctionProperty != null && digsestFunctionProperty.trim().length() > 0) { this.digsestFunction = digsestFunctionProperty; } String challengeQuestionSeparatorProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.CHALLENGE_QUESTION_SEPARATOR); if (challengeQuestionSeparatorProperty != null && challengeQuestionSeparatorProperty.trim().length() == 1) { this.challengeQuestionSeparator = challengeQuestionSeparatorProperty.trim(); } else { this.challengeQuestionSeparator = IdentityMgtConstants.LINE_SEPARATOR; } String passwordGeneratorProperty = properties. getProperty(IdentityMgtConstants.PropertyConfig.EXTENSION_PASSWORD_GENERATOR); if (passwordGeneratorProperty != null && passwordGeneratorProperty.trim().length() > 0) { try { Class clazz = Thread.currentThread().getContextClassLoader().loadClass(passwordGeneratorProperty); this.passwordGenerator = (RandomPasswordGenerator) clazz.newInstance(); } catch (Exception e) { log.error("Error while loading random password generator class. " + "Default random password generator would be used", e); } } String dataPersistModule = properties. getProperty(IdentityMgtConstants.PropertyConfig.EXTENSION_USER_DATA_STORE); if (dataPersistModule != null && dataPersistModule.trim().length() > 0) { try { Class clazz = Thread.currentThread().getContextClassLoader().loadClass(dataPersistModule); this.identityDataStore = (UserIdentityDataStore) clazz.newInstance(); } catch (Exception e) { log.error("Error while loading user identity data persist class. " + dataPersistModule + " Default module would be used", e); } } String recoveryPersistModule = properties. getProperty(IdentityMgtConstants.PropertyConfig.EXTENSION_USER_RECOVERY_DATA_STORE); if (dataPersistModule != null && dataPersistModule.trim().length() > 0) { try { Class clazz = Thread.currentThread().getContextClassLoader().loadClass(recoveryPersistModule); this.recoveryDataStore = (UserRecoveryDataStore) clazz.newInstance(); } catch (Exception e) { log.error("Error while loading user recovery data persist class. " + dataPersistModule + " Default module would be used", e); } } String registryCleanUpPeriod = properties.getProperty(IdentityMgtConstants.PropertyConfig .REGISTRY_CLEANUP_PERIOD); if (StringUtils.isNotBlank(registryCleanUpPeriod)) { this.registryCleanUpPeriod = Long.parseLong(registryCleanUpPeriod); } int i = 1; while (true) { String module = properties. getProperty(IdentityMgtConstants.PropertyConfig.EXTENSION_NOTIFICATION_SENDING_MODULE + "." + i); if (module == null) { break; } if (module.trim().length() > 0) { try { Class clazz = Thread.currentThread().getContextClassLoader().loadClass(module); NotificationSendingModule sendingModule = (NotificationSendingModule) clazz.newInstance(); String type = sendingModule.getNotificationType(); if (type == null || type.trim().length() == 0) { log.error("Notification type can not be null. Module " + module + " is not loaded."); } else { if (notificationTypes.contains(type)) { log.error("Same Notification type can not be supported by more than " + "one module. Module " + module + " is not loaded."); } else { notificationTypes.add(type); sendingModule.init(); sendingModules.add(sendingModule); } } } catch (Exception e) { log.error("Error while loading notification sending class " + module, e); } } i++; } // Load the configuration for Password.policy.extensions. loadPolicyExtensions(properties, IdentityMgtConstants.PropertyConfig.PASSWORD_POLICY_EXTENSIONS); if (this.passwordGenerator == null) { this.passwordGenerator = new DefaultPasswordGenerator(); } if (this.identityDataStore == null) { this.identityDataStore = new UserStoreBasedIdentityDataStore(); } if (this.recoveryDataStore == null) { this.recoveryDataStore = new RegistryRecoveryDataStore(); } if (this.sendingModules.isEmpty()) { NotificationSendingModule module = new DefaultEmailSendingModule(); module.init(); this.sendingModules.add(module); this.notificationTypes.add(module.getNotificationType()); } } catch (Exception e) { log.error("Error while loading identity mgt configurations", e); } } /** * Gets instance * <p/> * As this is only called in start up syn and null check is not needed * * @param configuration a primary <code>RealmConfiguration</code> * @return <code>IdentityMgtConfig</code> */ public static IdentityMgtConfig getInstance(RealmConfiguration configuration) { identityMgtConfig = new IdentityMgtConfig(configuration); return identityMgtConfig; } public static IdentityMgtConfig getInstance() { return identityMgtConfig; } public int getNoOfUserChallenges() { return noOfUserChallenges; } public boolean isSaasEnabled() { return saasEnable; } public boolean isNotificationInternallyManaged() { return notificationInternallyManaged; } public boolean isCaptchaVerificationInternallyManaged() { return captchaVerificationInternallyManaged; } public boolean isEnableUserAccountVerification() { return enableUserAccountVerification; } public int getAuthPolicyMaxLoginAttempts() { return authPolicyMaxLoginAttempts; } public int getTemporaryPasswordExpireTime() { return temporaryPasswordExpireTime; } public boolean isEnableTemporaryPassword() { return enableTemporaryPassword; } public boolean isEnableAuthPolicy() { return enableAuthPolicy; } public boolean isAuthPolicyExpirePasswordCheck() { return authPolicyExpirePasswordCheck; } public boolean isAuthPolicyOneTimePasswordCheck() { return authPolicyOneTimePasswordCheck; } public int getAuthPolicyLockingTime() { return authPolicyLockingTime; } public boolean isAuthPolicyAccountLockCheck() { return authPolicyAccountLockCheck; } public boolean isUserAccountVerificationByUser() { return userAccountVerificationByUser; } public boolean isTemporaryPasswordOneTime() { return temporaryPasswordOneTime; } public String getUserAccountVerificationRole() { return userAccountVerificationRole; } public String getChallengeQuestionSeparator() { return challengeQuestionSeparator; } public String getDigsestFunction() { return digsestFunction; } public RandomPasswordGenerator getPasswordGenerator() { return passwordGenerator; } public UserIdentityDataStore getIdentityDataStore() { return identityDataStore; } public boolean isNotificationSending() { return notificationSending; } public boolean isAuthPolicyAccountExistCheck() { return authPolicyAccountExistCheck; } public boolean isAuthPolicyAccountLockOnFailure() { return authPolicyAccountLockOnFailure; } public int getAuthPolicyPasswordExpireTime() { return authPolicyPasswordExpireTime; } public boolean isAuthPolicyAccountLockOnCreation() { return authPolicyAccountLockOnCreation; } public int getNotificationExpireTime() { return notificationExpireTime; } public boolean isListenerEnable() { return listenerEnable; } public List<NotificationSendingModule> getNotificationSendingModules() { return sendingModules; } public String getAccountRecoveryClaim() { return recoveryClaim; } public List<String> getNotificationTypes() { return notificationTypes; } public UserRecoveryDataStore getRecoveryDataStore() { return recoveryDataStore; } public PolicyRegistry getPolicyRegistry() { return policyRegistry; } public long getRegistryCleanUpPeriod() { return registryCleanUpPeriod; } /** * This method is used to load the policies declared in the configuration. * * @param properties Loaded properties * @param extensionType Type of extension */ private void loadPolicyExtensions(Properties properties, String extensionType) { Set<Integer> count = new HashSet(); Iterator<String> keyValues = properties.stringPropertyNames().iterator(); while (keyValues.hasNext()) { String currentProp = keyValues.next(); if (currentProp.startsWith(extensionType)) { String extensionNumber = currentProp.replaceFirst(extensionType + ".", ""); if (StringUtils.isNumeric(extensionNumber)) { count.add(Integer.parseInt(extensionNumber)); } } } //setting the number of extensionTypes as the upper bound as there can be many extension policy numbers, //eg: Password.policy.extensions.1, Password.policy.extensions.4, Password.policy.extensions.15 Iterator<Integer> countIterator = count.iterator(); while (countIterator.hasNext()) { Integer extensionIndex = countIterator.next(); String className = properties.getProperty(extensionType + "." + extensionIndex); if (className == null) { continue; } try { Class clazz = Thread.currentThread().getContextClassLoader().loadClass(className); PolicyEnforcer policy = (PolicyEnforcer) clazz.newInstance(); policy.init(getParameters(properties, extensionType, extensionIndex)); this.policyRegistry.addPolicy(policy); } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | SecurityException e) { log.error("Error while loading password policies " + className, e); } } } /** * This utility method is used to get the parameters from the configuration * file for a given policy extension. * * @param prop - properties * @param extensionKey - extension key which is defined in the * IdentityMgtConstants * @param sequence - property sequence number in the file * @return Map of parameters with key and value from the configuration file. */ private Map<String, String> getParameters(Properties prop, String extensionKey, int sequence) { Set<String> keys = prop.stringPropertyNames(); Map<String, String> keyValues = new HashMap<String, String>(); for (String key : keys) { // Get only the provided extensions. // Eg. // Password.policy.extensions.1 if (key.contains(extensionKey + "." + String.valueOf(sequence))) { Matcher m = propertyPattern.matcher(key); // Find the .1. pattern in the property key. if (m.find()) { int searchIndex = m.end(); /* * Key length is > matched pattern's end index if it has * parameters * in the config file. */ if (key.length() > searchIndex) { String propKey = key.substring(searchIndex); String propValue = prop.getProperty(key); keyValues.put(propKey, propValue); } } } } return keyValues; } public Properties getProperties() { return properties; } public String getProperty(String key) { return properties.getProperty(key); } public void setProperty(String key, String value) { this.properties.setProperty(key, value); } }