/* * $Id$ * * Copyright 2006, The jCoderZ.org Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * Neither the name of the jCoderZ.org Project nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jcoderz.commons.connector; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Iterator; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.resource.spi.ConnectionRequestInfo; import javax.resource.spi.ManagedConnectionFactory; import javax.resource.spi.SecurityException; import javax.resource.spi.security.GenericCredential; import javax.resource.spi.security.PasswordCredential; import javax.security.auth.Subject; /** * Utility provides method to extract user name and password from a set of * security relevant objects such as * {@link javax.resource.spi.security.PasswordCredential}, * {@link javax.resource.spi.ConnectionRequestInfo} and so on. * */ public final class SecurityUtil { /** The full qualified name of this class. */ private static final String CLASSNAME = SecurityUtil.class.getName(); /** The logger to use. */ private static final Logger logger = Logger.getLogger(CLASSNAME); private SecurityUtil () { } /** * Extracts the user name and password from the given set of objects as * specified in the JCA 1.0 Chapter 8.2.6. * * @param subject Current Security Subject passed by the application server. * @param mcf The managed connection factory * @param cri Connection reques info * * @return UserPassword instance. If no user name / password can be * extracted, this method returns {@link UserPassword#EMPTY_USER_PASSWORD}. * * @throws SecurityException Thown if no applicable credentials found in * the Subject. */ public static UserPassword getUserPassword (Subject subject, ManagedConnectionFactory mcf, ConnectionRequestInfo cri) throws SecurityException { final String method = "getUserPassword"; final boolean finer = logger.isLoggable(Level.FINER); if (finer) { logger.entering(CLASSNAME, method, new Object [] {subject, mcf, cri}); } UserPassword result = UserPassword.EMPTY_USER_PASSWORD; if (subject != null) { // JCA 1.0 8.2.6 Contract for RA (Option A) final PasswordCredential pc = PrivilegedExecutor.getPasswordCredential( subject, mcf); if (pc != null) { result = UserPassword.fromUserPassword(pc.getUserName(), new String( pc.getPassword())); } else { final SecurityException rse = new SecurityException( "No applicable credentials found in the Subject passed by the" + " application server."); logger.throwing(CLASSNAME, method, rse); throw rse; // TODO Implements this path if required. (required ?) // JCA 1.0 8.2.6 Contract for RA (Option B) /* final GenericCredential gc = PrivilegedExecutor.getGenericCredential(subject, mcf); if (gc == null) { throw new SecurityException("No applicable credentials found in" + " the Subject passed by the application server."); } */ } } else if (cri != null) { // JCA 1.0 8.2.6 Contract for RA (Option C) if (!(cri instanceof ConnectionRequestInfoBase)) { final SecurityException rse = new SecurityException("No applicable " + "ConnectionRequestInfo passed by the application server."); logger.throwing(CLASSNAME, method, rse); throw rse; } result = UserPassword.fromUserPassword( ((ConnectionRequestInfoBase) cri).getUserPassword()); } if (finer) { logger.exiting(CLASSNAME, method, result); } return result; } /** * This class is intended to execute a privileged action. * The security contract assumes that a resource adapter has the necessary * security permissions to extract a private credential set from a Subject * instance. */ private static class PrivilegedExecutor implements PrivilegedAction { private final Subject mSubject; private final ManagedConnectionFactory mMcf; private final boolean mPcAction; PrivilegedExecutor (Subject subject, ManagedConnectionFactory mcf, boolean pcAction) { mSubject = subject; mMcf = mcf; mPcAction = pcAction; } /** {@inheritDoc} */ public Object run () { final Object result; if (mPcAction) { result = getPwdCredential(); } else { result = getGenCredential(); } return result; } private Object getPwdCredential () { // This method implements the behavior of a Resource Adapter specified // in the JCA 1.0 8.2.6 Contract for RA (Option A) // The resource adapter explicitly checks whether the passed Subject // instance carries a PasswordCredential instance using the // Subject.getPrivateCredentials method. final Set creds = mSubject.getPrivateCredentials( PasswordCredential.class); PasswordCredential pc = null; // Since a Subject instance can carry multiple PasswordCredential // instances, a Managed- ConnectionFactory should only use a // PasswordCredential instance that has been specifically passed to // it through the security contract. final Iterator credentials = creds.iterator(); while (credentials.hasNext()) { final PasswordCredential pcCurrent = (PasswordCredential) credentials.next(); // The ManagedConnectionFactory implementation uses the equals // method to compare itself with the passed instance. if (pcCurrent.getManagedConnectionFactory().equals(mMcf)) { pc = pcCurrent; break; } } return pc; } private Object getGenCredential () { // This method implements the behavior of a Resource Adapter specified // in the JCA 1.0 8.2.6 Contract for RA (Option B) // The resource adapter explicitly checks whether passed Subject // instance carries a GenericCredential instance using the methods // getPrivateCredentials and getPublicCredentials defined on the // Subject interface. final Set creds = mSubject.getPrivateCredentials( GenericCredential.class); Object result = null; final Iterator credentials = creds.iterator(); while (credentials.hasNext()) { final GenericCredential c = (GenericCredential) credentials.next(); result = c; break; } return result; } /** * Tries to extract a PasswordCredential from the Subject * <code>subject</code> applicable to the managed connection factory * <code>mcf</code>. * Executes a privileged action. * * * @param subject Current Security Subject passed by the * application server. * @param mcf The managed connection factory * * @return a PasswordCredential or null. */ static PasswordCredential getPasswordCredential (Subject subject, ManagedConnectionFactory mcf) { return (PasswordCredential) AccessController.doPrivileged( new PrivilegedExecutor(subject, mcf, true)); } /** * Tries to extract a GenericCredential from the Subject * <code>subject</code> applicable to the managed connection factory * <code>mcf</code>. * Executes a privileged action. * * @param subject Current Security Subject passed by the application * server. * * @param mcf The managed connection factory * * @return a GenericCredential or null. */ static GenericCredential getGenericCredential (Subject subject, ManagedConnectionFactory mcf) { return (GenericCredential) AccessController.doPrivileged( new PrivilegedExecutor(subject, mcf, false)); } } }