/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2006-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://oss.oracle.com/licenses/CDDL+GPL-1.1 * or LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.security.auth.message.config; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; import java.security.AccessController; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.message.*; import javax.security.auth.message.config.*; import javax.security.auth.message.module.*; import javax.security.auth.Subject; /** * This interface is implemented by objects that can be used to obtain * authentication context configuration objects, that is, * <code>ClientAuthConfig</code> or <code>ServerAuthConfig</code> objects. * * <p> Authentication context configuration objects serve as sources of * the authentication context objects, that is, <code>ClientAuthContext</code> or * <code>ServerAuthContext</code> objects, for a specific message layer * and messaging context. * * <p> Authentication context objects encapsulate the initialization, * configuration, and invocation of authentication modules, that is, * <code>ClientAuthModule</code> or <code>ServerAuthModule</code> objects, for * a specific message exchange within a specific message layer and * messaging context. * * <p> Callers do not directly operate on authentication modules. * Instead, they rely on a ClientAuthContext or ServerAuthContext * to manage the invocation of modules. A caller obtains an instance * of ClientAuthContext or ServerAuthContext by calling the respective * <code>getAuthContext</code> method on a <code>ClientAuthConfig</code> * or <code>ServerAuthConfig</code> object obtained from an * AuthConfigProvider. * * <p> The following represents a typical sequence of calls for obtaining * a client authentication context object, and then using it to secure * a request. * <ol> * <li>AuthConfigProvider provider; * <li>ClientAuthConfig config = provider.getClientAuthConfig(layer,appID,cbh); * <li>String authContextID = config.getAuthContextID(messageInfo); * <li>ClientAuthContext context = config.getAuthContext(authContextID,subject,properties); * <li>context.secureRequest(messageInfo,subject); * </ol> * * <p> Every implementation of this interface must offer a public, * two argument constructor with the following signature: * <pre> * <code> * public AuthConfigProviderImpl(Map properties, AuthConfigFactory factory); * </code> *</pre> * where the properties argument may be null, and where all values and * keys occurring in a non-null properties argument must be of type String. * When the factory argument is not null, it indicates that the * provider is to self-register at the factory by calling the following * method on the factory: * <pre> * <code> * public String * registerConfigProvider(AuthConfigProvider provider, String layer, * String appContext, String description); * </code> * </pre> * @version %I%, %G% * * @see ClientAuthContext * @see ServerAuthContext * @see AuthConfigFactory */ public class ServletAuthConfigProvider implements AuthConfigProvider { private static ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); private static Lock rLock = rwLock.readLock();; private static Lock wLock = rwLock.writeLock(); private static HashMap authConfigMap = new HashMap(); private static HashMap defaultAuthConfigMap; private static String HTTP_SERVLET_LAYER = "HttpServlet"; private static String MANDATORY_KEY = "javax.security.auth.message.MessagePolicy.isMandatory"; private static String MANDATORY_CONTEXT_ID = "mandatory"; private static String OPTIONAL_CONTEXT_ID = "optional"; private static String CONTEXTS_KEY = "AppContextIDs"; private static String MODULE_KEY = "ServerAuthModule"; private static String defaultModule = null; private static Map defaultModuleOptions = null; /** * initialization properties * * ServerAuthModule=ClassName * AppContextIDs=x,y,z * */ public ServletAuthConfigProvider (Map properties, AuthConfigFactory factory) throws AuthException { if (properties == null) { throw new AuthException("properties required for construction"); } String module = (String) properties.get(MODULE_KEY); if (module == null) { throw new AuthException("ServerAuthModule property is required"); } HashMap options = new HashMap(properties); options.remove(MODULE_KEY); String[] contextID = parseStringValue ((String) properties.get(CONTEXTS_KEY)); options.remove(CONTEXTS_KEY); System.err.println("constructing ServletAuthConfigProvider: " + module); if (contextID != null) { for (String appContext : contextID) { System.err.println("constructing ServletServerAuthConfig: " + appContext); if (appContext != null && appContext.length() > 0) { ServerAuthConfig sAC = new ServletServerAuthConfig (appContext,module,options); try { wLock.lock(); authConfigMap.put(appContext,sAC); } finally { wLock.unlock(); } if (factory != null) { factory.registerConfigProvider (this,HTTP_SERVLET_LAYER,appContext,module); } } } } else { // record defaults to handle registration for all appcontexts try { wLock.lock(); defaultModule = module; defaultModuleOptions = options; defaultAuthConfigMap = new HashMap(); } finally { wLock.unlock(); } } } private static String[] parseStringValue(String value) { String[] rvalue = null; if (value != null) { // removed blank String delim = new String(":,;"); StringTokenizer tokenizer = new StringTokenizer(value,delim); int count = tokenizer.countTokens(); if (count > 0) { rvalue = new String[count]; for (int i = 0; i < count; i++) { rvalue[i] = tokenizer.nextToken(); } } } return rvalue; } public ClientAuthConfig getClientAuthConfig (String layer, String appContext, CallbackHandler handler) throws AuthException { throw new AuthException("Not implemented"); } public ServerAuthConfig getServerAuthConfig (String layer, String appContext, CallbackHandler handler) throws AuthException { if (!HTTP_SERVLET_LAYER.equals(layer)) { throw new AuthException("Layer Not implemented"); } if (handler == null) { throw new AuthException("default handler Not implemented"); } // reuse config for a given layer and appcontext, handler will be // set on first access. ServletServerAuthConfig sSAC = null; try { rLock.lock(); sSAC = (ServletServerAuthConfig) authConfigMap.get(appContext); if (sSAC == null) { if (defaultAuthConfigMap != null) { sSAC = (ServletServerAuthConfig) defaultAuthConfigMap.get(appContext); } } if (sSAC != null) { sSAC.setHandlerIfNotSet(handler); } } finally { rLock.unlock(); } if (sSAC == null) { try { wLock.lock(); if (defaultAuthConfigMap != null) { sSAC = (ServletServerAuthConfig) defaultAuthConfigMap.get(appContext); } if (sSAC == null) { sSAC = new ServletServerAuthConfig (appContext,defaultModule,defaultModuleOptions); defaultAuthConfigMap.put(appContext,sSAC); } } finally { rLock.unlock(); } if (sSAC != null) { sSAC.setHandlerIfNotSet(handler); } } if (sSAC == null) { throw new AuthException("context: " + appContext + " not configured"); } return sSAC; } public void refresh() { } static class ServletServerAuthConfig implements ServerAuthConfig { static final Class[] PARAMS = { }; static final Object[] ARGS = { }; Lock rLockConfig; Lock wLockConfig; String appContext; CallbackHandler cbh; ServerAuthModule modules[] = null; ServerAuthContext mandatoryContext; ServerAuthContext optionalContext; Map options; static MessagePolicy mandatoryPolicy = new MessagePolicy ( new MessagePolicy.TargetPolicy[] { new MessagePolicy.TargetPolicy ( (MessagePolicy.Target[]) null, new ServletProtectionPolicy()) } , true); static MessagePolicy optionalPolicy = new MessagePolicy ( new MessagePolicy.TargetPolicy[] { new MessagePolicy.TargetPolicy ((MessagePolicy.Target[]) null, new ServletProtectionPolicy()) }, false); ServletServerAuthConfig (String appContext, final String clazz, Map options) throws AuthException { ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); rLockConfig = rwLock.readLock();; wLockConfig = rwLock.writeLock(); this.appContext = appContext; this.options = options; try { modules = (ServerAuthModule[]) AccessController.doPrivileged (new java.security.PrivilegedExceptionAction() { public Object run() throws java.lang.ClassNotFoundException, java.lang.NoSuchMethodException, java.lang.InstantiationException, java.lang.IllegalAccessException, java.lang.reflect.InvocationTargetException { ClassLoader loader = Thread.currentThread().getContextClassLoader(); Class c = Class.forName(clazz, true, loader); java.lang.reflect.Constructor constructor = c.getConstructor(PARAMS); return new ServerAuthModule[] { (ServerAuthModule) constructor.newInstance(ARGS), (ServerAuthModule) constructor.newInstance(ARGS) }; } }); } catch (java.security.PrivilegedActionException pae) { AuthException ae = new AuthException(); ae.initCause(pae.getCause()); throw ae; } System.out.println("created ServletServerAuthConfig: " + appContext + " " + clazz); mandatoryContext = null; optionalContext = null; } public ServerAuthContext getAuthContext(String authContextID, Subject serviceSubject, Map properties) throws AuthException { boolean mandatory = false; if (MANDATORY_CONTEXT_ID.equals(authContextID)) { mandatory = true; } else if (!OPTIONAL_CONTEXT_ID.equals(authContextID)) { throw new AuthException("invalid AuthContext ID"); } ServerAuthContext rvalue = null; try { rLockConfig.lock(); if (mandatory) { rvalue = mandatoryContext; } else { rvalue = optionalContext; } } finally { rLockConfig.unlock(); } if (rvalue == null) { try { wLockConfig.lock(); if (options != null && properties != null) { properties = new HashMap(properties); properties.putAll(options); } if (mandatory) { mandatoryContext = new ServletServerAuthContext (modules[1],mandatoryPolicy,cbh,properties); rvalue = mandatoryContext; } else { optionalContext = new ServletServerAuthContext (modules[0],optionalPolicy,cbh,properties); rvalue = optionalContext; } } finally { wLockConfig.unlock(); } } return rvalue; } public String getMessageLayer() { return HTTP_SERVLET_LAYER; } public String getAppContext() { return this.appContext; } public String getAuthContextID(MessageInfo messageInfo) { if (messageInfo.getMap().containsKey(MANDATORY_KEY)) { return MANDATORY_CONTEXT_ID; } else { return OPTIONAL_CONTEXT_ID; } } public void refresh() { } public boolean isProtected() { return true; } boolean setHandlerIfNotSet(CallbackHandler handler) { try { wLockConfig.lock(); if (this.cbh == null && handler != null) { this.cbh = handler; return true; } else { return false; } } finally { wLockConfig.unlock(); } } static class ServletProtectionPolicy implements MessagePolicy.ProtectionPolicy { ServletProtectionPolicy() { } public String getID() { return MessagePolicy.ProtectionPolicy.AUTHENTICATE_SENDER; } } } static class ServletServerAuthContext implements ServerAuthContext { ServerAuthModule module; ServletServerAuthContext (ServerAuthModule module, MessagePolicy requestPolicy, CallbackHandler cbh, Map options) throws AuthException { module.initialize(requestPolicy,null,cbh,options); this.module = module; } public AuthStatus validateRequest (MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException { return module.validateRequest (messageInfo,clientSubject,serviceSubject); } public AuthStatus secureResponse (MessageInfo messageInfo, Subject serviceSubject) throws AuthException { return module.secureResponse(messageInfo,serviceSubject); } public void cleanSubject(MessageInfo messageInfo, Subject subject) throws AuthException { module.cleanSubject(messageInfo,subject); } } }