blob: 35b4ab3f2bca7c9bd17798e6aec1422a02e1ecd2 [file] [log] [blame]
/*
* 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.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.security.auth.spi.LoginModule;
import org.apache.sling.commons.osgi.ManifestHeader;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.service.log.LogService;
import org.osgi.util.tracker.BundleTracker;
class BundleLoginModuleCreator extends BundleTracker implements LoginModuleCreator
{
private static final String JAAS_MODULE_CLASS = "Jaas-ModuleClass";
private final Map<String, LoginModuleInfo> loginModuleInfo = new ConcurrentHashMap<String, LoginModuleInfo>();
private final Logger log;
public BundleLoginModuleCreator(BundleContext context, Logger log)
{
super(context, Bundle.ACTIVE, null /* customizer */);
this.log = log;
}
public LoginModule newInstance(String className)
{
LoginModuleInfo lmInfo = loginModuleInfo.get(className);
//TODO Rethink about exception handling. Probably introduce custom exception classes
if (lmInfo == null)
{
throw new AssertionError("No bundle exists to create LoginModule from "
+ className);
}
try
{
return lmInfo.newInstance();
}
catch (IllegalAccessException e)
{
throw new RuntimeException("Error occurred while creating LoginModule for "
+ className, e);
}
catch (InstantiationException e)
{
throw new RuntimeException("Error occurred while creating LoginModule for "
+ className, e);
}
}
public Map<Bundle, Set<String>> getBundleToLoginModuleMapping()
{
//With R5 we could have used Map<Bundle, T> BundleTracker.getTracked()
//Determine the bundle -> login module classes map
Map<Bundle, Set<String>> bundleMap = new HashMap<Bundle, Set<String>>();
for (LoginModuleInfo e : loginModuleInfo.values())
{
Bundle b = e.getBundle();
@SuppressWarnings("unchecked")
Set<String> classNames = (Set<String>) getObject(b);
if (classNames == null)
{
continue;
}
bundleMap.put(b, classNames);
}
return bundleMap;
}
// ---------- BundleTracker integration ----------------------------------------------
@Override
public Object addingBundle(Bundle bundle, BundleEvent event)
{
if (providesLoginModule(bundle))
{
return registerBundle(bundle);
}
return null;
}
@Override
public void removedBundle(Bundle bundle, BundleEvent event, Object object)
{
@SuppressWarnings("unchecked")
Set<String> classNames = (Set<String>) object;
for (String className : classNames)
{
loginModuleInfo.remove(className);
}
}
private boolean providesLoginModule(Bundle bundle)
{
return bundle.getHeaders().get(JAAS_MODULE_CLASS) != null;
}
private Set<String> registerBundle(Bundle bundle)
{
Set<String> classNames = parseHeader((String) bundle.getHeaders().get(
JAAS_MODULE_CLASS));
for (String className : classNames)
{
LoginModuleInfo bi = new LoginModuleInfo(className, bundle, log);
if (bi.isValid())
{
//Duplicate registration check
if (loginModuleInfo.containsKey(className))
{
LoginModuleInfo existingInfo = loginModuleInfo.get(className);
String msg = String.format(
"LoginModule class %s is already registered with Bundle %s. Entry "
+ "from bundle %s would be ignored", className,
existingInfo.getBundle(), bundle);
log.log(LogService.LOG_WARNING, msg);
continue;
}
loginModuleInfo.put(className, bi);
log.log(LogService.LOG_INFO, "Registering LoginModule class ["
+ className + "] from Bundle" + bundle);
}
else
{
log.log(LogService.LOG_WARNING,
"Could not load LoginModule class " + bi.getClassName()
+ " from bundle " + bundle);
}
}
return classNames;
}
static final class LoginModuleInfo
{
private final String className;
private final Bundle bundle;
private final Class<LoginModule> clazz;
@SuppressWarnings("unchecked")
public LoginModuleInfo(String className, Bundle bundle, Logger log)
{
this.className = className;
this.bundle = bundle;
Class<LoginModule> clazz = null;
try
{
clazz = bundle.loadClass(className);
}
catch (ClassNotFoundException e)
{
log.log(LogService.LOG_WARNING, "Error loading class [" + className
+ "] from bundle " + bundle, e);
}
this.clazz = clazz;
}
public LoginModule newInstance() throws IllegalAccessException,
InstantiationException
{
if (clazz == null)
{
throw new IllegalStateException("LoginModule class not initialized");
}
return clazz.newInstance();
}
public boolean isValid()
{
return clazz != null;
}
public String getClassName()
{
return className;
}
public Bundle getBundle()
{
return bundle;
}
}
private static Set<String> parseHeader(String header)
{
Set<String> values = new HashSet<String>();
ManifestHeader mh = ManifestHeader.parse(header);
for(ManifestHeader.Entry e : mh.getEntries())
{
values.add(e.getValue());
}
return values;
}
}