/* * 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.notification.mgt; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.osgi.framework.BundleContext; import org.wso2.carbon.identity.notification.mgt.bean.ModuleConfiguration; import org.wso2.carbon.identity.notification.mgt.bean.Subscription; import org.wso2.carbon.utils.CarbonUtils; import org.wso2.securevault.SecretResolver; import org.wso2.securevault.SecretResolverFactory; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; /** * Configuration builder class for Message Management component. Responsible for reading msg-mgt * .properties file and extract properties and distribute them to relevant message sending * components. */ @SuppressWarnings("unused") public class NotificationMgtConfigBuilder { private static final Log log = LogFactory.getLog(NotificationMgtConfigBuilder.class); /** * All properties configured in msg-mgt.properties file */ private Properties notificationMgtConfigProperties; /** * Map of configurations which are specific to notification sending modules */ private Map<String, ModuleConfiguration> moduleConfiguration; /** * Thread pool size for message sending task */ private String threadPoolSize; /** * Load properties file and set Module properties * * @param bundleContext Bundle context * @throws NotificationManagementException */ public NotificationMgtConfigBuilder(BundleContext bundleContext) throws NotificationManagementException { notificationMgtConfigProperties = loadProperties(bundleContext); setThreadPoolSize(); resolveSecrets(); moduleConfiguration = new HashMap<String, ModuleConfiguration>(); build(); } /** * Sets the thread pool size read from configurations */ private void setThreadPoolSize() { threadPoolSize = (String) notificationMgtConfigProperties.remove(NotificationMgtConstants.Configs. THREAD_POOL_SIZE); } /** * Load properties which are defined in msg-mgt.properties file * * @param bundleContext Bundle context * @return Set of properties which are defined in msg-mgt.properties file * @throws NotificationManagementException */ private Properties loadProperties(BundleContext bundleContext) throws NotificationManagementException { Properties properties = new Properties(); InputStream inStream = null; // Open the default configuration file in carbon conf directory path . File MessageMgtPropertyFile = new File(CarbonUtils.getCarbonConfigDirPath() + File.separator + "identity" + File.separator, NotificationMgtConstants.MODULE_CONFIG_FILE); try { // If the configuration exists in the carbon conf directory, read properties from there if (MessageMgtPropertyFile.exists()) { inStream = new FileInputStream(MessageMgtPropertyFile); // Else read properties form either bundle context path or class path } else { URL url; if (bundleContext != null) { if ((url = bundleContext.getBundle().getResource(NotificationMgtConstants.MODULE_CONFIG_FILE)) != null) { inStream = url.openStream(); } else { log.warn("Bundle context could not find resource " + NotificationMgtConstants .MODULE_CONFIG_FILE); } // If bundle context is not found, read properties file from class path } else { if ((url = this.getClass().getClassLoader().getResource(NotificationMgtConstants. MODULE_CONFIG_FILE)) != null) { inStream = url.openStream(); } else { log.warn("Class resource loader could not find resource " + NotificationMgtConstants. MODULE_CONFIG_FILE); } } } if (inStream != null) { properties.load(inStream); } //Even if the configurations are not found, individual modules can behave themselves without configuration } catch (FileNotFoundException e) { log.warn("Could not find configuration file for Message Sending module", e); } catch (IOException e) { log.warn("Error while opening input stream for property file", e); // Finally close input stream } finally { try { if (inStream != null) { inStream.close(); } } catch (IOException e) { log.error("Error while closing input stream ", e); } } return properties; } /** * Build and store per module configuration objects */ private void build() { Properties moduleNames = NotificationManagementUtils.getSubProperties(NotificationMgtConstants.Configs. MODULE_NAME, notificationMgtConfigProperties); Enumeration propertyNames = moduleNames.propertyNames(); // Iterate through events and build event objects while (propertyNames.hasMoreElements()) { String key = (String) propertyNames.nextElement(); String moduleName = (String) moduleNames.remove(key); moduleConfiguration.put(moduleName, buildModuleConfigurations(moduleName)); } } /** * Building per module configuration objects * * @param moduleName Name of the module * @return ModuleConfiguration object which has configurations for the given module name */ private ModuleConfiguration buildModuleConfigurations(String moduleName) { Properties moduleProperties = getModuleProperties(moduleName); List<Subscription> subscriptionList = buildSubscriptionList(moduleName, moduleProperties); return new ModuleConfiguration(getModuleProperties(moduleName), subscriptionList); } /** * Build a list of subscription by a particular module * * @param moduleName Name of the module * @param moduleProperties Set of properties which * @return A list of subscriptions by the module */ private List<Subscription> buildSubscriptionList(String moduleName, Properties moduleProperties) { // Get subscribed events Properties subscriptions = NotificationManagementUtils.getSubProperties(moduleName + "." + NotificationMgtConstants.Configs.SUBSCRIPTION, moduleProperties); List<Subscription> subscriptionList = new ArrayList<Subscription>(); Enumeration propertyNames = subscriptions.propertyNames(); // Iterate through events and build event objects while (propertyNames.hasMoreElements()) { String key = (String) propertyNames.nextElement(); String subscriptionName = (String) subscriptions.remove(key); // Read all the event properties starting from the event prefix Properties subscriptionProperties = NotificationManagementUtils.getPropertiesWithPrefix (moduleName + "." + NotificationMgtConstants.Configs.SUBSCRIPTION + "." + subscriptionName, moduleProperties); Subscription subscription = new Subscription(subscriptionName, subscriptionProperties); subscriptionList.add(subscription); } return subscriptionList; } /** * Retrieve all properties defined for a particular module * * @param moduleName Name of the module * @return A set of properties which are defined for a particular module */ private Properties getModuleProperties(String moduleName) { return NotificationManagementUtils.getPropertiesWithPrefix(moduleName, notificationMgtConfigProperties); } /** * Returns a module configuration object for the passed mdoule name * * @param moduleName Name of the module * @return Module configuration object which is relevant to the given name. */ public ModuleConfiguration getModuleConfigurations(String moduleName) { return this.moduleConfiguration.get(moduleName); } public String getThreadPoolSize() { return threadPoolSize; } /** * There can be sensitive information like passwords in configuration file. If they are encrypted using secure * vault, this method will resolve them and replace with original values. */ private void resolveSecrets() { SecretResolver secretResolver = SecretResolverFactory.create(notificationMgtConfigProperties); Enumeration propertyNames = notificationMgtConfigProperties.propertyNames(); if (secretResolver != null && secretResolver.isInitialized()) { // Iterate through whole config file and find encrypted properties and resolve them while (propertyNames.hasMoreElements()) { String key = (String) propertyNames.nextElement(); if (secretResolver.isTokenProtected(key)) { if (log.isDebugEnabled()) { log.debug("Resolving and replacing secret for " + key); } // Resolving the secret password. String value = secretResolver.resolve(key); // Replaces the original encrypted property with resolved property notificationMgtConfigProperties.put(key, value); } else { if (log.isDebugEnabled()) { log.debug("No encryption done for value with key :" + key); } } } } else { if(log.isDebugEnabled()){ log.debug("Secret Resolver is not present. Will not resolve encryptions in config file"); } } } }