blob: f2ba483588da50cd47bf8137b5fd6d876d407a4e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.internal.security.storage;
import java.util.*;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.preferences.ConfigurationScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.equinox.internal.security.auth.AuthPlugin;
import org.eclipse.equinox.internal.security.auth.nls.SecAuthMessages;
import org.eclipse.equinox.internal.security.storage.friends.IStorageConstants;
import org.eclipse.equinox.security.storage.StorageException;
import org.eclipse.equinox.security.storage.provider.PasswordProvider;
import org.eclipse.osgi.util.NLS;
//XXX add validation on module IDs - AZaz09 and dots, absolutely no tabs
// XXX reserved name DEFAULT_PASSWORD_ID
/**
* Finds appropriate password provider module to use.
*/
public class PasswordProviderSelector implements IRegistryEventListener {
final private static String EXTENSION_POINT = "org.eclipse.equinox.security.secureStorage"; //$NON-NLS-1$
final private static String STORAGE_MODULE = "provider";//$NON-NLS-1$
final private static String MODULE_PRIORITY = "priority";//$NON-NLS-1$
final private static String MODULE_DESCRIPTION = "description";//$NON-NLS-1$
final private static String CLASS_NAME = "class";//$NON-NLS-1$
final private static String HINTS_NAME = "hint";//$NON-NLS-1$
final private static String HINT_VALUE = "value";//$NON-NLS-1$
private Map modules = new HashMap(5); // cache of modules found
public class ExtStorageModule {
public String moduleID;
public IConfigurationElement element;
public int priority;
public String name;
public String description;
public List hints;
public ExtStorageModule(String id, IConfigurationElement element, int priority, String name, String description, List hints) {
super();
this.element = element;
this.moduleID = id;
this.priority = priority;
this.name = name;
this.description = description;
this.hints = hints;
}
}
static private PasswordProviderSelector instance = null;
static public PasswordProviderSelector getInstance() {
if (instance == null) {
instance = new PasswordProviderSelector();
IExtensionRegistry registry = RegistryFactory.getRegistry();
registry.addListener(instance, EXTENSION_POINT);
}
return instance;
}
public void stop() {
if (instance != null) {
IExtensionRegistry registry = RegistryFactory.getRegistry();
registry.removeListener(instance);
instance = null;
}
}
private PasswordProviderSelector() {
// hides default constructor; use getInstance()
}
public List findAvailableModules(String expectedID) {
IExtensionRegistry registry = RegistryFactory.getRegistry();
IExtensionPoint point = registry.getExtensionPoint(EXTENSION_POINT);
IExtension[] extensions = point.getExtensions();
ArrayList allAvailableModules = new ArrayList(extensions.length);
for (int i = 0; i < extensions.length; i++) {
String moduleID = extensions[i].getUniqueIdentifier();
if (moduleID == null) // IDs on those extensions are mandatory; if not specified, ignore the extension
continue;
moduleID = moduleID.toLowerCase();
if (expectedID != null && !expectedID.equals(moduleID))
continue;
IConfigurationElement[] elements = extensions[i].getConfigurationElements();
if (elements.length == 0)
continue;
IConfigurationElement element = elements[0]; // only one module is allowed per extension
if (!STORAGE_MODULE.equals(element.getName())) {
reportError(SecAuthMessages.unexpectedConfigElement, element.getName(), element, null);
continue;
}
String attribute = element.getAttribute(MODULE_PRIORITY);
int priority = -1;
if (attribute != null) {
priority = Integer.parseInt(attribute);
if (priority < 0)
priority = 0;
if (priority > 10)
priority = 10;
}
String name = extensions[i].getLabel();
String description = element.getAttribute(MODULE_DESCRIPTION);
List suppliedHints = null;
IConfigurationElement[] hints = element.getChildren(HINTS_NAME);
if (hints.length != 0) {
suppliedHints = new ArrayList(hints.length);
for (int j = 0; j < hints.length; j++) {
String hint = hints[j].getAttribute(HINT_VALUE);
if (hint != null)
suppliedHints.add(hint);
}
}
allAvailableModules.add(new ExtStorageModule(moduleID, element, priority, name, description, suppliedHints));
}
Collections.sort(allAvailableModules, new Comparator() {
public int compare(Object o1, Object o2) {
int p1 = ((ExtStorageModule) o1).priority;
int p2 = ((ExtStorageModule) o2).priority;
return p2 - p1;
}
});
return allAvailableModules;
}
public PasswordProviderModuleExt findStorageModule(String expectedID) throws StorageException {
if (expectedID != null)
expectedID = expectedID.toLowerCase(); // ID is case-insensitive
synchronized (modules) {
if (modules.containsKey(expectedID))
return (PasswordProviderModuleExt) modules.get(expectedID);
}
List allAvailableModules = findAvailableModules(expectedID);
HashSet disabledModules = getDisabledModules();
for (Iterator i = allAvailableModules.iterator(); i.hasNext();) {
ExtStorageModule module = (ExtStorageModule) i.next();
if (expectedID == null && disabledModules != null && disabledModules.contains(module.moduleID))
continue;
Object clazz;
try {
clazz = module.element.createExecutableExtension(CLASS_NAME);
} catch (CoreException e) {
reportError(SecAuthMessages.instantiationFailed, module.element.getAttribute(CLASS_NAME), module.element, e);
continue;
}
if (!(clazz instanceof PasswordProvider))
continue;
PasswordProviderModuleExt result = new PasswordProviderModuleExt((PasswordProvider) clazz, module.moduleID);
// cache the result
synchronized (modules) {
if (expectedID == null)
modules.put(null, result);
modules.put(module.moduleID, result);
}
return result;
}
// the secure storage module was not found - error in app's configuration
String msg;
if (expectedID == null)
msg = SecAuthMessages.noSecureStorageModules;
else
msg = NLS.bind(SecAuthMessages.noSecureStorageModule, expectedID);
throw new StorageException(StorageException.NO_SECURE_MODULE, msg);
}
private void reportError(String template, String arg, IConfigurationElement element, Throwable e) {
String supplier = element.getContributor().getName();
String message = NLS.bind(template, arg, supplier);
AuthPlugin.getDefault().logError(message, e);
}
//////////////////////////////////////////////////////////////////////////////////////////////
// Synch local cache with the registry
public void added(IExtension[] extensions) {
clearCaches();
}
public void added(IExtensionPoint[] extensionPoints) {
clearCaches();
}
public void removed(IExtension[] extensions) {
clearCaches();
}
public void removed(IExtensionPoint[] extensionPoints) {
clearCaches();
}
/**
* Clear whole cache as priorities might have changed after new modules were added.
*/
public void clearCaches() {
synchronized (modules) {
modules.clear();
// If module was removed, clear its entry from the password cache.
// The code below clears all entries for simplicity, in future this
// can be made more limiting if a scenario exists where module
// removal/addition is a frequent event.
SecurePreferencesMapper.clearPasswordCache();
}
}
public boolean isLoggedIn() {
synchronized (modules) {
return (modules.size() != 0);
}
}
protected HashSet getDisabledModules() {
IEclipsePreferences node = new ConfigurationScope().getNode(AuthPlugin.PI_AUTH);
String tmp = node.get(IStorageConstants.DISABLED_PROVIDERS_KEY, null);
if (tmp == null || tmp.length() == 0)
return null;
HashSet disabledModules = new HashSet();
String[] disabledProviders = tmp.split(","); //$NON-NLS-1$
for (int i = 0; i < disabledProviders.length; i++) {
disabledModules.add(disabledProviders[i]);
}
return disabledModules;
}
}