/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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.apache.felix.jaas.internal; import java.util.Dictionary; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.apache.felix.jaas.LoginModuleFactory; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Properties; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.PropertyOption; import org.apache.felix.scr.annotations.PropertyUnbounded; import org.apache.sling.commons.osgi.PropertiesUtil; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.ServiceRegistration; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.cm.ManagedServiceFactory; import org.osgi.service.log.LogService; import static org.apache.felix.jaas.internal.Util.trimToNull; @Component(label = "%jaas.name", description = "%jaas.description", metatype = true, ds = false, name = JaasConfigFactory.SERVICE_PID, configurationFactory = true) @Properties({ @Property(name = LoginModuleFactory.JAAS_CONTROL_FLAG, value = "required", options = { @PropertyOption(name = "required", value = "%jaas.flag.required"), @PropertyOption(name = "requisite", value = "%jaas.flag.requisite"), @PropertyOption(name = "sufficient", value = "%jaas.flag.sufficient"), @PropertyOption(name = "optional", value = "%jaas.flag.optional") }), @Property(name = LoginModuleFactory.JAAS_RANKING, intValue = 0), @Property(name = LoginModuleFactory.JAAS_REALM_NAME), @Property(name = "webconsole.configurationFactory.nameHint", value = "{" + LoginModuleFactory.JAAS_RANKING + "} : {" + JaasConfigFactory.JAAS_CLASS_NAME + "}" + " ({" + LoginModuleFactory.JAAS_CONTROL_FLAG + "})") }) public class JaasConfigFactory implements ManagedServiceFactory { public static final String SERVICE_PID = "org.apache.felix.jaas.Configuration.factory"; @Property static final String JAAS_CLASS_NAME = "jaas.classname"; @Property(unbounded = PropertyUnbounded.ARRAY) static final String JAAS_OPTIONS = "jaas.options"; private final Logger log; private final LoginModuleCreator factory; private final BundleContext context; private final ConcurrentMap<String, ServiceRegistration> registrations = new ConcurrentHashMap<String, ServiceRegistration>(); public JaasConfigFactory(BundleContext context, LoginModuleCreator factory, Logger log) { this.context = context; this.factory = factory; this.log = log; Hashtable<String, String> props = new Hashtable<String, String>(); props.put(Constants.SERVICE_VENDOR, "Apache Software Foundation"); props.put(Constants.SERVICE_PID, SERVICE_PID); context.registerService(ManagedServiceFactory.class.getName(), this, props); } @Override public String getName() { return "JaasConfigFactory"; } @SuppressWarnings("unchecked") @Override public void updated(String pid, Dictionary config) throws ConfigurationException { String className = trimToNull(PropertiesUtil.toString(config.get(JAAS_CLASS_NAME), null)); String flag = trimToNull(PropertiesUtil.toString(config.get(LoginModuleFactory.JAAS_CONTROL_FLAG), "required")); int ranking = PropertiesUtil.toInteger(config.get(LoginModuleFactory.JAAS_RANKING), 0); //TODO support system property substitution e.g. ${user.home} //in property values Map options = PropertiesUtil.toMap(config.get(JAAS_OPTIONS), new String[0]); String realmName = trimToNull(PropertiesUtil.toString(config.get(LoginModuleFactory.JAAS_REALM_NAME), null)); if (className == null) { log.log(LogService.LOG_WARNING, "Class name for the LoginModule is required. Configuration would be ignored" + config); return; } //Combine the config. As the jaas.options is required for capturing config //via felix webconsole. However in normal usage people would like to provide //key=value pair directly in config. So merge both to provide a combined //view Map combinedOptions = convertToMap(config); combinedOptions.putAll(options); LoginModuleProvider lmf = new ConfigLoginModuleProvider(realmName, className, combinedOptions, ControlFlag.from(flag).flag(), ranking, factory); ServiceRegistration reg = context.registerService(LoginModuleFactory.class.getName(), lmf, null); ServiceRegistration oldReg = registrations.put(pid, reg); //Remove earlier registration if any if(oldReg != null) { oldReg.unregister(); } } @Override public void deleted(String pid) { ServiceRegistration reg = registrations.remove(pid); if (reg != null) { reg.unregister(); } } //~----------------------------------- Utility Methods @SuppressWarnings("unchecked") private static Map convertToMap(Dictionary config) { Map copy = new HashMap(); Enumeration e = config.keys(); while (e.hasMoreElements()) { Object key = e.nextElement(); Object value = config.get(key); copy.put(key, value); } return copy; } }