| /* |
| * Copyright (c) 2022, 2022 Contributors to the Eclipse Foundation. |
| * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0, which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the |
| * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, |
| * version 2 with the GNU Classpath Exception, which is available at |
| * https://www.gnu.org/software/classpath/license.html. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 |
| */ |
| |
| package com.sun.jaspic.config.factory; |
| |
| import java.lang.reflect.Constructor; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.locks.Lock; |
| import java.util.concurrent.locks.ReadWriteLock; |
| import java.util.concurrent.locks.ReentrantReadWriteLock; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import com.sun.jaspic.config.factory.singlemodule.DefaultAuthConfigProvider; |
| import com.sun.jaspic.config.helper.JASPICLogManager; |
| |
| import jakarta.security.auth.message.config.AuthConfigFactory; |
| import jakarta.security.auth.message.config.AuthConfigProvider; |
| import jakarta.security.auth.message.config.RegistrationListener; |
| import jakarta.security.auth.message.module.ServerAuthModule; |
| import jakarta.servlet.ServletContext; |
| |
| |
| /** |
| * This class implements methods in the abstract class AuthConfigFactory. |
| * @author Shing Wai Chan |
| */ |
| public abstract class BaseAuthConfigFactory extends AuthConfigFactory { |
| |
| private static final Logger logger = Logger.getLogger(JASPICLogManager.JASPIC_LOGGER, JASPICLogManager.RES_BUNDLE); |
| |
| private static final String CONTEXT_REGISTRATION_ID = "org.glassfish.security.message.registrationId"; |
| |
| private static final ReadWriteLock rwLock = new ReentrantReadWriteLock(true); |
| public static final Lock rLock = rwLock.readLock(); |
| public static final Lock wLock = rwLock.writeLock(); |
| |
| private static Map<String, AuthConfigProvider> id2ProviderMap; |
| private static Map<String, RegistrationContext> id2RegisContextMap; |
| private static Map<String, List<RegistrationListener>> id2RegisListenersMap; |
| private static Map<AuthConfigProvider, List<String>> provider2IdsMap; |
| |
| protected static final String CONF_FILE_NAME = "auth.conf"; |
| |
| abstract protected RegStoreFileParser getRegStore(); |
| |
| /** |
| * Get a registered AuthConfigProvider from the factory. |
| * |
| * Get the provider of ServerAuthConfig and/or |
| * ClientAuthConfig objects registered for the identified message |
| * layer and application context. |
| * |
| * @param layer a String identifying the message layer |
| * for which the registered AuthConfigProvider is |
| * to be returned. This argument may be null. |
| * |
| * @param appContext a String that identifies the application messaging |
| * context for which the registered AuthConfigProvider |
| * is to be returned. This argument may be null. |
| * |
| * @param listener the RegistrationListener whose |
| * <code>notify</code> method is to be invoked |
| * if the corresponding registration is unregistered or |
| * replaced. The value of this argument may be null. |
| * |
| * @return the implementation of the AuthConfigProvider interface |
| * registered at the factory for the layer and appContext |
| * or null if no AuthConfigProvider is selected. |
| * |
| * <p>All factories shall employ the following precedence rules to select |
| * the registered AuthConfigProvider that matches (via matchConstructors) the |
| * layer and appContext arguments: |
| *<ul> |
| * <li> The provider that is specifically registered for both the |
| * corresponding message layer and appContext |
| * shall be selected. |
| * <li> if no provider is selected according to the preceding rule, |
| * the provider specifically registered for the |
| * corresponding appContext and for all message layers |
| * shall be selected. |
| * <li> if no provider is selected according to the preceding rules, |
| * the provider specifically registered for the |
| * corresponding message layer and for all appContexts |
| * shall be selected. |
| * <li> if no provider is selected according to the preceding rules, |
| * the provider registered for all message layers and for all |
| * appContexts shall be selected. |
| * <li> if no provider is selected according to the preceding rules, |
| * the factory shall terminate its search for a registered provider. |
| *</ul> |
| */ |
| @Override |
| public AuthConfigProvider getConfigProvider(String layer, String appContext, RegistrationListener listener) { |
| AuthConfigProvider provider = null; |
| if (listener == null) { |
| rLock.lock(); |
| try { |
| provider = getConfigProviderUnderLock(layer,appContext,null); |
| } finally { |
| rLock.unlock(); |
| } |
| } else { |
| wLock.lock(); |
| try { |
| provider = getConfigProviderUnderLock(layer,appContext,listener); |
| } finally { |
| wLock.unlock(); |
| } |
| } |
| return provider; |
| } |
| |
| /** |
| * Registers within the factory, a provider |
| * of ServerAuthConfig and/or ClientAuthConfig objects for a |
| * message layer and application context identifier. |
| * |
| * <P>At most one registration may exist within the factory for a |
| * given combination of message layer |
| * and appContext. Any pre-existing |
| * registration with identical values for layer and appContext is replaced |
| * by a subsequent registration. When replacement occurs, the registration |
| * identifier, layer, and appContext identifier remain unchanged, |
| * and the AuthConfigProvider (with initialization properties) and |
| * description are replaced. |
| * |
| *<p>Within the lifetime of its Java process, a factory must assign unique |
| * registration identifiers to registrations, and must never |
| * assign a previously used registration identifier to a registration |
| * whose message layer and or appContext identifier differ from |
| * the previous use. |
| * |
| * <p>Programmatic registrations performed via this method must update |
| * (according to the replacement rules described above), the persistent |
| * declarative representation of provider registrations employed by the |
| * factory constructor. |
| * |
| * @param className the fully qualified name of an AuthConfigProvider |
| * implementation class. This argument must not be null. |
| * |
| * @param properties a Map object containing the initialization |
| * properties to be passed to the provider constructor. |
| * This argument may be null. When this argument is not null, |
| * all the values and keys occuring in the Map must be of |
| * type String. |
| * |
| * @param layer a String identifying the message layer |
| * for which the provider will be registered at the factory. |
| * A null value may be passed as an argument for this parameter, |
| * in which case, the provider is registered at all layers. |
| * |
| * @param appContext a String value that may be used by a runtime |
| * to request a configuration object from this provider. |
| * A null value may be passed as an argument for this parameter, |
| * in which case, the provider is registered for all |
| * configuration ids (at the indicated layers). |
| * |
| * @param description a text String describing the provider. |
| * this value may be null. |
| * |
| * @return a String identifier assigned by |
| * the factory to the provider registration, and that may be |
| * used to remove the registration from the provider. |
| * |
| * @exception SecurityException if the caller does not have |
| * permission to register a provider at the factory. |
| */ |
| @Override |
| public String registerConfigProvider(String className, Map properties, String layer, String appContext, |
| String description) { |
| //XXX factory must check permission |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) { |
| sm.checkPermission(AuthConfigFactory.providerRegistrationSecurityPermission); |
| } |
| //XXX do we need doPrivilege here |
| AuthConfigProvider provider = _constructProvider(className, properties, null); |
| return _register(provider, properties, layer, appContext, description, true); |
| } |
| |
| @Override |
| public String registerConfigProvider(AuthConfigProvider provider, |
| String layer, String appContext, String description) { |
| //XXX factory must check permission |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) { |
| sm.checkPermission(AuthConfigFactory.providerRegistrationSecurityPermission); |
| } |
| return _register(provider, null, layer, appContext, description, false); |
| } |
| |
| /** |
| * Remove the identified provider registration from the factory |
| * and invoke any listeners associated with the removed registration. |
| * |
| * @param registrationID a String that identifies a provider registration |
| * at the factory |
| * |
| * @return true if there was a registration with the specified identifier |
| * and it was removed. Return false if the registraionID was |
| * invalid. |
| * |
| * @exception SecurityException if the caller does not have |
| * permission to unregister the provider at the factory. |
| * |
| */ |
| @Override |
| public boolean removeRegistration(String registrationID) { |
| //XXX factory must check permission |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) { |
| sm.checkPermission(AuthConfigFactory.providerRegistrationSecurityPermission); |
| } |
| return _unRegister(registrationID); |
| } |
| |
| /** |
| * Disassociate the listener from all the provider |
| * registrations whose layer and appContext values are matched |
| * by the corresponding arguments to this method. |
| * |
| * @param listener the RegistrationListener to be detached. |
| * |
| * @param layer a String identifying the message layer or null. |
| * |
| * @param appContext a String value identifying the application context |
| * or null. |
| * |
| * @return an array of String values where each value identifies a |
| * provider registration from which the listener was removed. |
| * This method never returns null; it returns an empty array if |
| * the listener was not removed from any registrations. |
| * |
| * @exception SecurityException if the caller does not have |
| * permission to detach the listener from the factory. |
| * |
| */ |
| @Override |
| public String[] detachListener(RegistrationListener listener, String layer, String appContext) { |
| //XXX factory must check permission |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) { |
| sm.checkPermission(AuthConfigFactory.providerRegistrationSecurityPermission); |
| } |
| ArrayList<String> list = new ArrayList<>(); |
| String regisID = getRegistrationID(layer, appContext); |
| wLock.lock(); |
| try { |
| Set<String> targets = id2RegisListenersMap.keySet(); |
| for (String targetID : targets) { |
| if (regIdImplies(regisID, targetID)) { |
| List<RegistrationListener> listeners = |
| id2RegisListenersMap.get(targetID); |
| if (listeners != null && listeners.remove(listener)) { |
| list.add(targetID); |
| } |
| } |
| } |
| } finally { |
| wLock.unlock(); |
| } |
| return list.toArray(new String[list.size()]); |
| } |
| |
| /** |
| * Get the registration identifiers for all registrations of the |
| * provider instance at the factory. |
| * |
| * @param provider the AuthConfigurationProvider whose registration |
| * identifiers are to be returned. This argument may be |
| * null, in which case, it indicates that the the id's of |
| * all active registration within the factory are returned. |
| * |
| * @return an array of String values where each value identifies a |
| * provider registration at the factory. This method never returns null; |
| * it returns an empty array when their are no registrations at the |
| * factory for the identified provider. |
| */ |
| @Override |
| public String[] getRegistrationIDs(AuthConfigProvider provider) { |
| rLock.lock(); |
| try { |
| Collection<String> regisIDs = null; |
| if (provider != null) { |
| regisIDs = provider2IdsMap.get(provider); |
| } else { |
| Collection<List<String>> collList = provider2IdsMap.values(); |
| if (collList != null) { |
| regisIDs = new HashSet<>(); |
| for (List<String> listIds : collList) { |
| if (listIds != null) { |
| regisIDs.addAll(listIds); |
| } |
| } |
| } |
| } |
| return ((regisIDs != null) ? regisIDs.toArray(new String[regisIDs.size()]) : new String[0]); |
| } finally { |
| rLock.unlock(); |
| } |
| } |
| |
| /** |
| * Get the the registration context for the identified registration. |
| * |
| * @param registrationID a String that identifies a provider registration |
| * at the factory |
| * |
| * @return a RegistrationContext or null. When a Non-null value is |
| * returned, it is a copy of the registration context corresponding to the |
| * registration. Null is returned when the registration identifier does |
| * not correspond to an active registration |
| */ |
| @Override |
| public RegistrationContext getRegistrationContext(String registrationID) { |
| rLock.lock(); |
| try { |
| return id2RegisContextMap.get(registrationID); |
| } finally { |
| rLock.unlock(); |
| } |
| } |
| |
| /** |
| * Cause the factory to reprocess its persistent declarative |
| * representation of provider registrations. |
| * |
| * <p> A factory should only replace an existing registration when |
| * a change of provider implementation class or initialization |
| * properties has occurred. |
| * |
| * @exception SecurityException if the caller does not have permission |
| * to refresh the factory. |
| */ |
| @Override |
| public void refresh() { |
| //XXX factory must check permission |
| SecurityManager sm = System.getSecurityManager(); |
| if (sm != null) { |
| sm.checkPermission(AuthConfigFactory.providerRegistrationSecurityPermission); |
| } |
| Map<String, List<RegistrationListener>> preExistingListenersMap; |
| wLock.lock(); |
| try { |
| preExistingListenersMap = id2RegisListenersMap; |
| _loadFactory(); |
| } finally { |
| wLock.unlock(); |
| } |
| |
| // notify pre-existing listeners after (re)loading factory |
| if (preExistingListenersMap != null) { |
| notifyListeners(preExistingListenersMap); |
| } |
| } |
| |
| /** |
| * Gets the app context ID from the servlet context. |
| * |
| * <p> |
| * The app context ID is the ID that Jakarta Authentication associates with the given application. |
| * In this case that given application is the web application corresponding to the |
| * ServletContext. |
| * |
| * @param context the servlet context for which to obtain the Jakarta Authentication app context ID |
| * @return the app context ID for the web application corresponding to the given context |
| */ |
| public static String getAppContextID(ServletContext context) { |
| return context.getVirtualServerName() + " " + context.getContextPath(); |
| } |
| |
| @Override |
| public String registerServerAuthModule(ServerAuthModule serverAuthModule, Object context) { |
| if (!(context instanceof ServletContext)) { |
| return null; |
| } |
| |
| ServletContext servletContext = (ServletContext) context; |
| |
| // Register the factory-factory-factory for the SAM |
| String registrationId = AccessController.doPrivileged(new PrivilegedAction<String>() { |
| @Override |
| public String run() { |
| return registerConfigProvider( |
| new DefaultAuthConfigProvider(serverAuthModule), |
| "HttpServlet", |
| getAppContextID(servletContext), |
| "Default single SAM authentication config provider"); |
| } |
| }); |
| |
| // Remember the registration ID returned by the factory, so we can unregister the JASPIC module when the web module |
| // is undeployed. JASPIC being the low level API that it is won't do this automatically. |
| servletContext.setAttribute(CONTEXT_REGISTRATION_ID, registrationId); |
| |
| return registrationId; |
| } |
| |
| @Override |
| public void removeServerAuthModule(Object context) { |
| if (!(context instanceof ServletContext)) { |
| return; |
| } |
| |
| ServletContext servletContext = (ServletContext) context; |
| |
| String registrationId = (String) servletContext.getAttribute(CONTEXT_REGISTRATION_ID); |
| if (!isEmpty(registrationId)) { |
| AccessController.doPrivileged(new PrivilegedAction<Boolean>() { |
| @Override |
| public Boolean run() { |
| return removeRegistration(registrationId); |
| } |
| }); |
| } |
| } |
| |
| private static boolean isEmpty(String string) { |
| return string == null || string.isEmpty(); |
| } |
| |
| private AuthConfigProvider getConfigProviderUnderLock(String layer, String appContext, |
| RegistrationListener listener) { |
| AuthConfigProvider provider = null; |
| String regisID = getRegistrationID(layer, appContext); |
| String matchedID = null; |
| boolean providerFound = false; |
| if (id2ProviderMap.containsKey(regisID)) { |
| provider = id2ProviderMap.get(regisID); |
| providerFound = true; |
| } |
| if (!providerFound) { |
| matchedID = getRegistrationID(null, appContext); |
| if (id2ProviderMap.containsKey(matchedID)) { |
| provider = id2ProviderMap.get(matchedID); |
| providerFound = true; |
| } |
| } |
| if (!providerFound) { |
| matchedID = getRegistrationID(layer, null); |
| if (id2ProviderMap.containsKey(matchedID)) { |
| provider = id2ProviderMap.get(matchedID); |
| providerFound = true; |
| } |
| } |
| if (!providerFound) { |
| matchedID = getRegistrationID(null, null); |
| if (id2ProviderMap.containsKey(matchedID)) { |
| provider = id2ProviderMap.get(matchedID); |
| } |
| } |
| if (listener != null) { |
| List<RegistrationListener> listeners = id2RegisListenersMap.get(regisID); |
| if (listeners == null) { |
| listeners = new ArrayList<>(); |
| id2RegisListenersMap.put(regisID, listeners); |
| } |
| if (!listeners.contains(listener)) { |
| listeners.add(listener); |
| } |
| } |
| |
| return provider; |
| } |
| |
| |
| private static String getRegistrationID(String layer, String appContext) { |
| String regisID = null; |
| |
| // __0 (null, null) |
| // __1<appContext> (null, appContext) |
| // __2<layer> (layer, null) |
| // __3<nn>_<layer><appContext> (layer, appContext) |
| |
| if (layer != null) { |
| regisID = (appContext != null) ? |
| "__3" + layer.length() + "_" + layer + appContext : |
| "__2" + layer; |
| } else { |
| regisID = (appContext != null) ? |
| "__1" + appContext : |
| "__0"; |
| } |
| return regisID; |
| } |
| |
| /** |
| * This API decomposes the given regisID into layer and appContext. |
| * @param regisID |
| * @return a String array with layer and appContext |
| */ |
| private static String[] decomposeRegisID(String regisID) { |
| String layer = null; |
| String appContext = null; |
| if (regisID.equals("__0")) { |
| // null, null |
| } else if (regisID.startsWith("__1")) { |
| appContext = (regisID.length() == 3)? |
| "" : regisID.substring(3); |
| } else if (regisID.startsWith("__2")) { |
| layer = (regisID.length() == 3)? |
| "" : regisID.substring(3); |
| } else if (regisID.startsWith("__3")) { |
| int ind = regisID.indexOf('_', 3); |
| if (regisID.length() > 3 && ind > 0) { |
| String numberString = regisID.substring(3, ind); |
| int n; |
| try { |
| n = Integer.parseInt(numberString); |
| } catch (Exception ex) { |
| throw new IllegalArgumentException(); |
| } |
| layer = regisID.substring(ind + 1, ind + 1 + n); |
| appContext = regisID.substring(ind + 1 + n); |
| } else { |
| throw new IllegalArgumentException(); |
| } |
| } else { |
| throw new IllegalArgumentException(); |
| } |
| |
| return new String[]{layer, appContext}; |
| } |
| |
| private static AuthConfigProvider _constructProvider(String className, Map properties, AuthConfigFactory factory) { |
| //XXX do we need doPrivilege here |
| AuthConfigProvider provider = null; |
| if (className != null) { |
| try { |
| ClassLoader loader = Thread.currentThread().getContextClassLoader(); |
| Class c = Class.forName(className, true, loader); |
| Constructor<AuthConfigProvider> constr = c.getConstructor(Map.class, AuthConfigFactory.class); |
| provider = constr.newInstance(new Object[] {properties, factory}); |
| } catch (Throwable t) { |
| Throwable cause = t.getCause(); |
| logger.log(Level.WARNING, |
| "jmac.factory_unable_to_load_provider", |
| new Object[]{ className, t.toString(), (cause == null ? "cannot determine" : cause.toString())}); |
| } |
| } |
| return provider; |
| } |
| |
| //XXX need to update persistent state and notify effected listeners |
| private String _register(AuthConfigProvider provider, |
| Map<String, Object> properties, |
| String layer, |
| String appContext, |
| String description, |
| boolean persistent) { |
| |
| String regisID = getRegistrationID(layer, appContext); |
| RegistrationContext rc = |
| new RegistrationContextImpl(layer, appContext, description, persistent); |
| RegistrationContext prevRegisContext = null; |
| Map<String, List<RegistrationListener>> listenerMap; |
| wLock.lock(); |
| try { |
| prevRegisContext = id2RegisContextMap.get(regisID); |
| AuthConfigProvider prevProvider = id2ProviderMap.get(regisID); |
| |
| // handle the persistence first - so that any exceptions occur before |
| // the actual registration happens |
| if (persistent) { |
| _storeRegistration(regisID, rc, provider, properties); |
| } else if (prevRegisContext != null && prevRegisContext.isPersistent()) { |
| _deleteStoredRegistration(regisID, prevRegisContext); |
| } |
| |
| boolean wasRegistered = id2ProviderMap.containsKey(regisID); |
| |
| if (wasRegistered) { |
| List<String> prevRegisIDs = provider2IdsMap.get(prevProvider); |
| prevRegisIDs.remove(regisID); |
| if (prevRegisIDs.isEmpty()) { |
| provider2IdsMap.remove(prevProvider); |
| } |
| } |
| |
| id2ProviderMap.put(regisID, provider); |
| id2RegisContextMap.put(regisID, rc); |
| |
| List<String> regisIDs = provider2IdsMap.get(provider); |
| if (regisIDs == null) { |
| regisIDs = new ArrayList<>(); |
| provider2IdsMap.put(provider, regisIDs); |
| } |
| |
| if (!regisIDs.contains(regisID)) { |
| regisIDs.add(regisID); |
| } |
| |
| listenerMap = getEffectedListeners(regisID); |
| |
| } finally { |
| wLock.unlock(); |
| } |
| |
| // outside wLock to prevent dead lock |
| notifyListeners(listenerMap); |
| |
| return regisID; |
| } |
| |
| //XXX need to update persistent state and notify effected listeners |
| private boolean _unRegister(String regisID) { |
| boolean rvalue = false; |
| RegistrationContext rc = null; |
| Map<String, List<RegistrationListener>> listenerMap; |
| wLock.lock(); |
| try { |
| rc = id2RegisContextMap.remove(regisID); |
| rvalue = id2ProviderMap.containsKey(regisID); |
| AuthConfigProvider provider = id2ProviderMap.remove(regisID); |
| List<String> regisIDs = provider2IdsMap.get(provider); |
| if (regisIDs != null) { |
| regisIDs.remove(regisID); |
| } |
| if (regisIDs == null || regisIDs.isEmpty()) { |
| provider2IdsMap.remove(provider); |
| } |
| if (!rvalue) { |
| return false; |
| } |
| listenerMap = getEffectedListeners(regisID); |
| if (rc != null && rc.isPersistent()) { |
| _deleteStoredRegistration(regisID, rc); |
| } |
| } finally { |
| wLock.unlock(); |
| } |
| |
| // outside wLock to prevent dead lock |
| notifyListeners(listenerMap); |
| return rvalue; |
| } |
| |
| // the following methods implement the factory's persistence layer |
| protected void _loadFactory() { |
| try { |
| initializeMaps(); |
| |
| List<EntryInfo> entryList = getRegStore().getPersistedEntries(); |
| |
| for (EntryInfo info : entryList) { |
| if (info.isConstructorEntry()) { |
| _constructProvider(info.getClassName(), info.getProperties(), this); |
| } else { |
| boolean first = true; |
| AuthConfigProvider p = null; |
| List<RegistrationContext> contexts = (info.getRegContexts()); |
| for (RegistrationContext ctx : contexts) { |
| if (first) { |
| p = _constructProvider(info.getClassName(), info.getProperties(), null); |
| } |
| _loadRegistration(p, ctx.getMessageLayer(), ctx.getAppContext(), ctx.getDescription()); |
| } |
| } |
| } |
| } catch (Exception e) { |
| if (logger.isLoggable(Level.WARNING)) { |
| logger.log(Level.WARNING, |
| "jmac.factory_auth_config_loader_failure", e); |
| } |
| } |
| } |
| |
| /** |
| * Initialize the static maps in a static method |
| */ |
| private static void initializeMaps() { |
| id2ProviderMap = new HashMap<>(); |
| id2RegisContextMap = new HashMap<>(); |
| id2RegisListenersMap = new HashMap<>(); |
| provider2IdsMap = new HashMap<>(); |
| } |
| |
| private static String _loadRegistration(AuthConfigProvider provider, |
| String layer, |
| String appContext, |
| String description) { |
| |
| RegistrationContext rc = new RegistrationContextImpl(layer, appContext, description, true); |
| String regisID = getRegistrationID(layer, appContext); |
| id2RegisContextMap.get(regisID); |
| AuthConfigProvider prevProvider = id2ProviderMap.get(regisID); |
| boolean wasRegistered = id2ProviderMap.containsKey(regisID); |
| if (wasRegistered) { |
| List<String> prevRegisIDs = provider2IdsMap.get(prevProvider); |
| prevRegisIDs.remove(regisID); |
| if (prevRegisIDs.isEmpty()) { |
| provider2IdsMap.remove(prevProvider); |
| } |
| } |
| |
| id2ProviderMap.put(regisID, provider); |
| id2RegisContextMap.put(regisID, rc); |
| |
| List<String> regisIDs = provider2IdsMap.get(provider); |
| if (regisIDs == null) { |
| regisIDs = new ArrayList<>(); |
| provider2IdsMap.put(provider, regisIDs); |
| } |
| |
| if (!regisIDs.contains(regisID)) { |
| regisIDs.add(regisID); |
| } |
| |
| return regisID; |
| } |
| |
| |
| private void _storeRegistration(String regId, RegistrationContext ctx, AuthConfigProvider p, Map properties) { |
| |
| String className = null; |
| if (p != null) { |
| className = p.getClass().getName(); |
| } |
| if (propertiesContainAnyNonStringValues(properties)) { |
| throw new IllegalArgumentException("AuthConfigProvider cannot be registered - properties must all be of type String."); |
| } |
| if (ctx.isPersistent()) { |
| getRegStore().store(className, ctx, properties); |
| } |
| } |
| |
| |
| private boolean propertiesContainAnyNonStringValues(Map<String,Object> props) { |
| if (props != null) { |
| for(Map.Entry<String, Object> entry : props.entrySet()) { |
| if (!(entry.getValue() instanceof String)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| |
| private void _deleteStoredRegistration(String regId, RegistrationContext ctx) { |
| if (ctx.isPersistent()) { |
| getRegStore().delete(ctx); |
| } |
| } |
| |
| private static boolean regIdImplies(String reference, String target) { |
| |
| boolean rvalue = true; |
| |
| String[] refID = decomposeRegisID(reference); |
| String[] targetID = decomposeRegisID(target); |
| |
| if (refID[0] != null && !refID[0].equals(targetID[0])) { |
| rvalue = false; |
| } else if (refID[1] != null && !refID[1].equals(targetID[1])) { |
| rvalue = false; |
| } |
| return rvalue; |
| } |
| |
| /* will return some extra listeners. iow, effected listeners could be reduced |
| * by removing any associated with a provider registration id that is |
| * more specific than the one being added or removed.l |
| */ |
| private static Map<String, List<RegistrationListener>> getEffectedListeners(String regisID) { |
| Map<String, List<RegistrationListener>> effectedListeners = new HashMap<>(); |
| Set<String> listenerRegistrations = new HashSet<>(id2RegisListenersMap.keySet()); |
| |
| for (String listenerID : listenerRegistrations) { |
| if (regIdImplies(regisID, listenerID)) { |
| if (!effectedListeners.containsKey(listenerID)) { |
| effectedListeners.put(listenerID, new ArrayList<RegistrationListener>()); |
| } |
| effectedListeners.get(listenerID).addAll(id2RegisListenersMap.remove(listenerID)); |
| } |
| } |
| return effectedListeners; |
| } |
| |
| private static void notifyListeners(Map<String, List<RegistrationListener>> map) { |
| Set<Map.Entry<String, List<RegistrationListener>>> entrySet = map.entrySet(); |
| for (Map.Entry<String, List<RegistrationListener>> entry : entrySet) { |
| List<RegistrationListener> listeners = map.get(entry.getKey()); |
| |
| if (listeners != null && listeners.size() > 0) { |
| String[] dIds = decomposeRegisID(entry.getKey()); |
| |
| for (RegistrationListener listener : listeners) { |
| listener.notify(dIds[0], dIds[1]); |
| } |
| } |
| } |
| } |
| } |