/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.security.auth.login;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import javax.security.auth.AuthPermission;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import org.jboss.security.PicketBoxLogger;
import org.jboss.security.PicketBoxMessages;
import org.jboss.security.config.ApplicationPolicy;
import org.jboss.security.config.ApplicationPolicyRegistration;
import org.jboss.security.config.PolicyConfig;
import org.jboss.security.config.SecurityConfiguration;
import org.jboss.security.config.parser.StaxBasedConfigParser;
/**
* An concrete implementation of the javax.security.auth.login.Configuration class that parses an xml configuration of
* the form:
*
* <policy> <application-policy name = "test-domain"> <authentication> <login-module code =
* "org.jboss.security.plugins.samples.IdentityLoginModule" flag = "required"> <module-option name = "principal">starksm</module-option>
* </login-module> </authentication> </application-policy> </policy>
*
* @see javax.security.auth.login.Configuration
*
* @author Scott.Stark@jboss.org
* @author Anil.Saldhana@jboss.org
* @version $Revision: 57482 $
*/
@SuppressWarnings({"rawtypes","unchecked"})
public class XMLLoginConfigImpl extends Configuration implements Serializable, ApplicationPolicyRegistration
{
/** The serialVersionUID */
private static final long serialVersionUID = -8965860493224188277L;
private static final String DEFAULT_APP_CONFIG_NAME = "other";
private static final AuthPermission REFRESH_PERM = new AuthPermission("refreshLoginConfiguration");
transient PolicyConfig appConfigs = new PolicyConfig();
/** The URL to the XML or Sun login configuration */
protected URL loginConfigURL;
/** The inherited configuration we delegate to */
protected Configuration parentConfig;
/** A flag indicating if XML configs should be validated */
private boolean validateDTD = true;
private static final XMLLoginConfigImpl instance = new XMLLoginConfigImpl();
/**
* <p>
* Private constructor to implement the singleton pattern.
* </p>
*/
private XMLLoginConfigImpl()
{
}
/**
* <p>
* Obtains a reference to the singleton.
* </p>
*
* @return a reference to the singleton {@code XMLLoginConfigImpl} instance.
*/
public static XMLLoginConfigImpl getInstance()
{
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission(XMLLoginConfigImpl.class.getName() + ".getInstance"));
}
return instance;
}
// --- Begin Configuration method overrrides
@Override
public void refresh()
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(REFRESH_PERM);
appConfigs.clear();
loadConfig();
}
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String appName)
{
if (PicketBoxLogger.LOGGER.isTraceEnabled())
{
PicketBoxLogger.LOGGER.traceBeginGetAppConfigEntry(appName, appConfigs.size());
}
// Load the config if PolicyConfig is empty
if (this.appConfigs.size() == 0)
this.loadConfig();
AppConfigurationEntry[] entry = null;
ApplicationPolicy aPolicy = this.getApplicationPolicy(appName);
BaseAuthenticationInfo authInfo = null;
if (aPolicy != null)
authInfo = aPolicy.getAuthenticationInfo();
if (authInfo == null)
{
if (PicketBoxLogger.LOGGER.isTraceEnabled())
{
PicketBoxLogger.LOGGER.traceGetAppConfigEntryViaParent(appName, parentConfig != null ? parentConfig.toString() : null);
}
if (parentConfig != null)
entry = parentConfig.getAppConfigurationEntry(appName);
if (entry == null)
{
PicketBoxLogger.LOGGER.traceGetAppConfigEntryViaDefault(appName, DEFAULT_APP_CONFIG_NAME);
ApplicationPolicy defPolicy = appConfigs.get(DEFAULT_APP_CONFIG_NAME);
authInfo = defPolicy != null ? (AuthenticationInfo) defPolicy.getAuthenticationInfo() : null;
}
}
if (authInfo != null)
{
if (PicketBoxLogger.LOGGER.isTraceEnabled())
{
PicketBoxLogger.LOGGER.traceEndGetAppConfigEntryWithSuccess(appName, authInfo.toString());
}
// Make a copy of the authInfo object
final BaseAuthenticationInfo theAuthInfo = authInfo;
PrivilegedAction<AppConfigurationEntry[]> action = new PrivilegedAction<AppConfigurationEntry[]>()
{
public AppConfigurationEntry[] run()
{
return theAuthInfo.copyAppConfigurationEntry();
}
};
entry = AccessController.doPrivileged(action);
}
else
{
PicketBoxLogger.LOGGER.traceEndGetAppConfigEntryWithFailure(appName);
}
return entry;
}
// --- End Configuration method overrrides
/**
* Set the URL of the XML login configuration file that should be loaded by this mbean on startup.
*/
public URL getConfigURL()
{
return loginConfigURL;
}
/**
* Set the URL of the XML login configuration file that should be loaded by this mbean on startup.
*/
public void setConfigURL(URL loginConfigURL)
{
this.loginConfigURL = loginConfigURL;
}
public void setConfigResource(String resourceName) throws IOException
{
ClassLoader tcl = SecurityActions.getContextClassLoader();
loginConfigURL = tcl.getResource(resourceName);
if (loginConfigURL == null)
throw PicketBoxMessages.MESSAGES.failedToFindResource(resourceName);
}
public void setParentConfig(Configuration parentConfig)
{
this.parentConfig = parentConfig;
}
/**
* Get whether the login config xml document is validated againsts its DTD
*/
public boolean getValidateDTD()
{
return this.validateDTD;
}
/**
* Set whether the login config xml document is validated againsts its DTD
*/
public void setValidateDTD(boolean flag)
{
this.validateDTD = flag;
}
/**
* @see ApplicationPolicyRegistration#addApplicationPolicy(String, ApplicationPolicy)
*/
public void addApplicationPolicy(String appName, ApplicationPolicy aPolicy)
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(REFRESH_PERM);
appConfigs.add(aPolicy);
handleJASPIDelegation(aPolicy);
SecurityConfiguration.addApplicationPolicy(aPolicy);
}
/**
* Add an application configuration
*/
public void addAppConfig(String appName, AppConfigurationEntry[] entries)
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(REFRESH_PERM);
AuthenticationInfo authInfo = new AuthenticationInfo(appName);
authInfo.setAppConfigurationEntry(entries);
if (PicketBoxLogger.LOGGER.isTraceEnabled())
{
PicketBoxLogger.LOGGER.traceAddAppConfig(appName, authInfo.toString());
}
ApplicationPolicy aPolicy = new ApplicationPolicy(appName, authInfo);
appConfigs.add(aPolicy);
SecurityConfiguration.addApplicationPolicy(aPolicy);
}
public void copy(PolicyConfig policyConfig)
{
this.appConfigs.copy(policyConfig);
}
/**
* @deprecated
* @see #removeApplicationPolicy(String)
* @param appName
*/
@Deprecated
public void removeAppConfig(String appName)
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(REFRESH_PERM);
appConfigs.remove(appName);
SecurityConfiguration.removeApplicationPolicy(appName);
}
/**
* @see ApplicationPolicyRegistration#getApplicationPolicy(String)
*/
public ApplicationPolicy getApplicationPolicy(String domainName)
{
if (appConfigs == null || appConfigs.size() == 0)
loadConfig();
ApplicationPolicy aPolicy = null;
if(appConfigs != null )
aPolicy = appConfigs.get(domainName);
if (aPolicy != null)
SecurityConfiguration.addApplicationPolicy(aPolicy);
return aPolicy;
}
/**
* @see ApplicationPolicyRegistration#removeApplicationPolicy(String)
*/
public boolean removeApplicationPolicy(String appName)
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(REFRESH_PERM);
PicketBoxLogger.LOGGER.traceRemoveAppConfig(appName);
appConfigs.remove(appName);
SecurityConfiguration.removeApplicationPolicy(appName);
return true;
}
/**
* Method that returns the parsed AuthenticationInfo needed by the JASPI framework until a seperate Configuration
* mechanism for JASPI is established
*
* @return the parsed AuthenticationInfo object
*/
public BaseAuthenticationInfo getAuthenticationInfo(String domainName)
{
ApplicationPolicy aPolicy = getApplicationPolicy(domainName);
return aPolicy != null ? aPolicy.getAuthenticationInfo() : null;
}
public void clear()
{
}
/**
* Called to try to load the config from the java.security.auth.login.config property value when there is no
* loginConfigURL.
*/
@SuppressWarnings("deprecation")
public void loadConfig()
{
// Try to load the java.security.auth.login.config property
String loginConfig = System.getProperty("java.security.auth.login.config");
if (loginConfig == null)
loginConfig = "login-config.xml";
// If there is no loginConfigURL build it from the loginConfig
if (loginConfigURL == null)
{
try
{
// Try as a URL
loginConfigURL = new URL(loginConfig);
}
catch (MalformedURLException e)
{
// Try as a resource
try
{
setConfigResource(loginConfig);
}
catch (IOException ignore)
{
// Try as a file
File configFile = new File(loginConfig);
try
{
setConfigURL(configFile.toURL());
}
catch (MalformedURLException ignore2)
{
}
}
}
}
if (loginConfigURL == null)
{
PicketBoxLogger.LOGGER.warnFailureToFindConfig(loginConfig);
return;
}
PicketBoxLogger.LOGGER.traceBeginLoadConfig(loginConfigURL);
// Try to load the config if found
try
{
loadConfig(loginConfigURL);
PicketBoxLogger.LOGGER.traceEndLoadConfigWithSuccess(loginConfigURL);
}
catch (Exception e)
{
PicketBoxLogger.LOGGER.warnEndLoadConfigWithFailure(loginConfigURL, e);
}
}
protected String[] loadConfig(URL config) throws Exception
{
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(REFRESH_PERM);
ArrayList configNames = new ArrayList();
PicketBoxLogger.LOGGER.debugLoadConfigAsXML(config);
try
{
loadXMLConfig(config, configNames);
}
catch (Throwable e)
{
PicketBoxLogger.LOGGER.debugLoadConfigAsSun(config, e);
loadSunConfig(config, configNames);
}
String[] names = new String[configNames.size()];
configNames.toArray(names);
return names;
}
/**
* Handle the case when JASPI Info may have login module stack holder which delegates to a login module stack
*
* @param aPolicy
*/
private void handleJASPIDelegation(ApplicationPolicy aPolicy)
{
BaseAuthenticationInfo bai = aPolicy.getAuthenticationInfo();
if (bai instanceof JASPIAuthenticationInfo)
{
JASPIAuthenticationInfo jai = (JASPIAuthenticationInfo) bai;
LoginModuleStackHolder[] lmsharr = jai.getLoginModuleStackHolder();
for (LoginModuleStackHolder lmsh : lmsharr)
{
this.addAppConfig(lmsh.getName(), lmsh.getAppConfigurationEntry());
}
}
}
private void loadSunConfig(URL sunConfig, ArrayList configNames) throws Exception
{
InputStream is = null;
InputStreamReader configFile = null;
try
{
is = sunConfig.openStream();
configFile = new InputStreamReader(is);
SunConfigParser.doParse(configFile, this, PicketBoxLogger.LOGGER.isTraceEnabled());
}
finally
{
safeClose(configFile);
safeClose(is);
}
}
private void loadXMLConfig(URL loginConfigURL, ArrayList configNames) throws Exception
{
InputStream is = null;
try
{
is = loginConfigURL.openStream();
StaxBasedConfigParser parser = new StaxBasedConfigParser();
parser.parse(is);
}
finally
{
safeClose(is);
}
}
private void safeClose(InputStream fis)
{
try
{
if(fis != null)
{
fis.close();
}
}
catch(Exception e)
{}
}
private void safeClose(InputStreamReader fis)
{
try
{
if(fis != null)
{
fis.close();
}
}
catch(Exception e)
{}
}
}