* @author Walter Hoehn
*/
public interface IdentityProvider {
- public String getId();
+ public String getProviderId();
+ public Credential getResponseSigningCredential();
+ public Credential getAssertionSigningCredential();
+
}
public class RelyingParty implements ServiceProvider {
private ShibbolethOriginConfig originConfig;
- private Properties partyOverrides;
- private String id;
- private String signingCredentialId;
-
- public RelyingParty(Element partyConfig, ShibbolethOriginConfig globalConfig) {
+ private Properties partyOverrides = new Properties();
+ //TODO stub
+ private String id = "test:id";
+ private RelyingPartyIdentityProvider identityProvider;
+
+ public RelyingParty(Element partyConfig, ShibbolethOriginConfig globalConfig, Credentials credentials) {
this.originConfig = globalConfig;
//TODO setup things
+
+ //TODO this is just a stub... has to come from configuration
+ partyOverrides.setProperty(
+ "edu.internet2.middleware.shibboleth.hs.HandleServlet.responseSigningCredential",
+ "foo");
+
+ identityProvider =
+ new RelyingPartyIdentityProvider(
+ getConfigProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.providerId"),
+ credentials.getCredential(
+ getConfigProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.responseSigningCredential")));
}
public String getProviderId() {
return id;
}
- String getSigingCredentialId() {
- return signingCredentialId;
- }
-
public String getConfigProperty(String key) {
if (partyOverrides.containsKey(key)) {
return partyOverrides.getProperty(key);
}
return originConfig.getConfigProperty(key);
}
+
+ public boolean isLegacyProvider() {
+ //TODO implement
+ return true;
+ }
+
+ public String getHSNameFormatId() {
+ return null;
+ }
+
+ public RelyingPartyIdentityProvider getIdentityProvider() {
+ return identityProvider;
+ }
+}
+
+class RelyingPartyIdentityProvider implements IdentityProvider {
+
+ private String providerId;
+ private Credential responseSigningCredential;
+
+ RelyingPartyIdentityProvider(String providerId, Credential responseSigningCred) {
+ this.providerId = providerId;
+ this.responseSigningCredential = responseSigningCred;
+ }
+ public String getProviderId() {
+ return providerId;
+ }
+
+ public Credential getResponseSigningCredential() {
+ return responseSigningCredential;
+ }
+
+ public Credential getAssertionSigningCredential() {
+ return null;
+ }
+
}
* @param bindings
* Set of SAML authorities the relying party may contact
* (optional)
- * @param responseCredential
- * Credential to use for signing the SAML Response
- * @param assertaionCredential
- * Credential to use for signing any SAML Assertions contained
- * in the Response
* @return SAML response to send to accepting site
* @exception SAMLException
* Base class of exceptions that may be thrown during
String subjectIP,
String authMethod,
Date authInstant,
- Collection bindings,
- Credential responseCredential,
- Credential assertionCredential)
+ Collection bindings)
throws SAMLException {
- if (responseCredential == null || responseCredential.getPrivateKey() == null) {
+ if (relyingParty.getIdentityProvider().getResponseSigningCredential() == null
+ || relyingParty.getIdentityProvider().getResponseSigningCredential().getPrivateKey() == null) {
throw new InvalidCryptoException(
SAMLException.RESPONDER,
"ShibPOSTProfile.prepare() requires a response key.");
}
-
+
String responseAlgorithm;
- if (responseCredential.getCredentialType() == Credential.RSA) {
+ if (relyingParty.getIdentityProvider().getResponseSigningCredential().getCredentialType() == Credential.RSA) {
responseAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
- } else if (responseCredential.getCredentialType() == Credential.DSA) {
+ } else if (
+ relyingParty.getIdentityProvider().getResponseSigningCredential().getCredentialType() == Credential.DSA) {
responseAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_DSA;
} else {
throw new InvalidCryptoException(
ArrayList audiences = new ArrayList();
audiences.add(relyingParty.getProviderId());
- audiences.add(relyingParty.getName());
+ if (!relyingParty.getProviderId().equals(relyingParty.getName()))
+ audiences.add(relyingParty.getName());
+
+ String issuer;
+ if (relyingParty.isLegacyProvider()) {
+ //TODO must resolve this
+ issuer = "fooIssuer";
+ } else {
+ issuer = relyingParty.getProviderId();
+ }
SAMLResponse r =
- SAMLPOSTProfile.prepare(
- recipient,
- relyingParty.getConfigProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.issuer"),
- audiences,
- nameId,
- subjectIP,
- authMethod,
- authInstant,
- bindings);
+ SAMLPOSTProfile.prepare(recipient, issuer, audiences, nameId, subjectIP, authMethod, authInstant, bindings);
r.toDOM(doc);
- if (assertionCredential != null & assertionCredential.getPrivateKey() != null) {
+ if (relyingParty.getIdentityProvider().getAssertionSigningCredential() != null
+ && relyingParty.getIdentityProvider().getAssertionSigningCredential().getPrivateKey() != null) {
String assertionAlgorithm;
- if (assertionCredential.getCredentialType() == Credential.RSA) {
+ if (relyingParty.getIdentityProvider().getAssertionSigningCredential().getCredentialType()
+ == Credential.RSA) {
assertionAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
- } else if (assertionCredential.getCredentialType() == Credential.DSA) {
+ } else if (
+ relyingParty.getIdentityProvider().getAssertionSigningCredential().getCredentialType()
+ == Credential.DSA) {
assertionAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_DSA;
} else {
throw new InvalidCryptoException(
((SAMLAssertion) r.getAssertions().next()).sign(
assertionAlgorithm,
- assertionCredential.getPrivateKey(),
- Arrays.asList(assertionCredential.getX509CertificateChain()));
+ relyingParty.getIdentityProvider().getAssertionSigningCredential().getPrivateKey(),
+ Arrays.asList(
+ relyingParty.getIdentityProvider().getAssertionSigningCredential().getX509CertificateChain()));
}
r.sign(
responseAlgorithm,
- responseCredential.getPrivateKey(),
- Arrays.asList(responseCredential.getX509CertificateChain()));
+ relyingParty.getIdentityProvider().getResponseSigningCredential().getPrivateKey(),
+ Arrays.asList(relyingParty.getIdentityProvider().getResponseSigningCredential().getX509CertificateChain()));
return r;
}
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
-import java.util.Enumeration;
import java.util.Properties;
import org.apache.log4j.Logger;
*
*/
public class ShibbolethOriginConfig {
-
+
private static Logger log = Logger.getLogger(ShibbolethOriginConfig.class.getName());
- protected Properties properties;
-
+ protected Properties properties = new Properties();
+
public ShibbolethOriginConfig(Element config) throws HSConfigurationException {
-// Set defaults
- Properties defaultProps = new Properties();
- defaultProps.setProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.username", "REMOTE_USER");
- defaultProps.setProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.maxThreads", "5");
- defaultProps.setProperty(
- "edu.internet2.middleware.shibboleth.hs.HandleRepository.implementation",
- "edu.internet2.middleware.shibboleth.hs.provider.MemoryHandleRepository");
- defaultProps.setProperty("edu.internet2.middleware.shibboleth.hs.BaseHandleRepository.handleTTL", "1800000");
- defaultProps.setProperty(
- "edu.internet2.middleware.shibboleth.hs.provider.CryptoHandleRepository.keyStorePath",
- "/conf/handle.jks");
- defaultProps.setProperty("edu.internet2.middleware.shibboleth.audiences", "urn:mace:inqueue");
- defaultProps.setProperty(
- "edu.internet2.middleware.shibboleth.hs.HandleServlet.authMethod",
- SAMLAuthenticationStatement.AuthenticationMethod_Unspecified);
- //Load from file
- properties = new Properties(defaultProps);
- //TODO fix this!!!
- String propertiesFileLocation = "foo";//= getInitParameter("OriginPropertiesFile");
-
- if (propertiesFileLocation == null) {
- propertiesFileLocation = "/conf/origin.properties";
- }
- try {
- log.debug("Loading Configuration from (" + propertiesFileLocation + ").");
- properties.load(new ShibResource(propertiesFileLocation, this.getClass()).getInputStream());
+ if (!config.getTagName().equals("ShibbolethOriginConfig")) {
+ throw new HSConfigurationException("Unexpected configuration data. <ShibbolethOriginConfig> is needed.");
+ }
- //Make sure we have all required parameters
- StringBuffer missingProperties = new StringBuffer();
- String[] requiredProperties =
- {
- "edu.internet2.middleware.shibboleth.hs.HandleServlet.issuer",
- "edu.internet2.middleware.shibboleth.hs.HandleServlet.siteName",
- "edu.internet2.middleware.shibboleth.hs.HandleServlet.AAUrl",
- "edu.internet2.middleware.shibboleth.hs.HandleServlet.keyStorePath",
- "edu.internet2.middleware.shibboleth.hs.HandleServlet.keyStorePassword",
- "edu.internet2.middleware.shibboleth.hs.HandleServlet.keyStoreKeyAlias",
- "edu.internet2.middleware.shibboleth.hs.HandleServlet.keyStoreKeyPassword",
- "edu.internet2.middleware.shibboleth.hs.HandleServlet.authMethod",
- "edu.internet2.middleware.shibboleth.audiences" };
+ //Set defaults
+ //TODO need a way to set this
+ properties.setProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.username", "REMOTE_USER");
+ //TODO need a way to set this, remember to test for number format
+ properties.setProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.maxThreads", "5");
+ //TODO need a way to set this
+ properties.setProperty(
+ "edu.internet2.middleware.shibboleth.hs.HandleServlet.authMethod",
+ SAMLAuthenticationStatement.AuthenticationMethod_Unspecified);
- for (int i = 0; i < requiredProperties.length; i++) {
- if (properties.getProperty(requiredProperties[i]) == null) {
- missingProperties.append("\"");
- missingProperties.append(requiredProperties[i]);
- missingProperties.append("\" ");
- }
- }
- if (missingProperties.length() > 0) {
- log.error(
- "Missing configuration data. The following configuration properites have not been set: "
- + missingProperties.toString());
- throw new HSConfigurationException("Missing configuration data.");
- }
+ //TODO default relying party group
- } catch (IOException e) {
- log.error("Could not load HS servlet configuration: " + e);
- throw new HSConfigurationException("Could not load HS servlet configuration.");
- }
+ log.debug("Loading global configuration properties.");
- if (log.isDebugEnabled()) {
- ByteArrayOutputStream debugStream = new ByteArrayOutputStream();
- PrintStream debugPrinter = new PrintStream(debugStream);
- properties.list(debugPrinter);
- log.debug(
- "Runtime configuration parameters: " + System.getProperty("line.separator") + debugStream.toString());
- try {
- debugStream.close();
- } catch (IOException e) {
- log.error("Encountered a problem cleaning up resources: could not close debug stream.");
- }
- }
-
- //Be nice and trim "extra" whitespace from config properties
- Enumeration propNames = properties.propertyNames();
- while (propNames.hasMoreElements()) {
- String propName = (String) propNames.nextElement();
- if (properties.getProperty(propName, "").matches(".+\\s$")) {
- log.debug(
- "The configuration property ("
- + propName
- + ") contains trailing whitespace. Trimming... ");
- properties.setProperty(propName, properties.getProperty(propName).trim());
- }
- }
+ String attribute = ((Element) config).getAttribute("providerId");
+ if (attribute == null || attribute.equals("")) {
+ log.error("Global providerId not set. Add a (providerId) attribute to <ShibbolethOriginConfig>.");
+ throw new HSConfigurationException("Required configuration not specified.");
+ }
+ properties.setProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.providerId", attribute);
+ attribute = ((Element) config).getAttribute("AAUrl");
+ if (attribute == null || attribute.equals("")) {
+ log.error("Global providerId not set. Add a (AAUrl) attribute to <ShibbolethOriginConfig>.");
+ throw new HSConfigurationException("Required configuration not specified.");
+ }
+ properties.setProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.AAUrl", attribute);
+
+ if (log.isDebugEnabled()) {
+ ByteArrayOutputStream debugStream = new ByteArrayOutputStream();
+ PrintStream debugPrinter = new PrintStream(debugStream);
+ properties.list(debugPrinter);
+ log.debug(
+ "Global Runtime configuration parameters: "
+ + System.getProperty("line.separator")
+ + debugStream.toString());
+ try {
+ debugStream.close();
+ } catch (IOException e) {
+ log.error("Encountered a problem cleaning up resources: could not close debug stream.");
+ }
+ }
}
-
+
public String getConfigProperty(String key) {
return properties.getProperty(key);
}
package edu.internet2.middleware.shibboleth.hs;
import java.io.IOException;
+import java.io.InputStream;
import java.util.Collections;
import java.util.Date;
+import java.util.StringTokenizer;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import org.opensaml.SAMLResponse;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
import sun.misc.BASE64Decoder;
import edu.internet2.middleware.shibboleth.common.AuthNPrincipal;
import edu.internet2.middleware.shibboleth.common.Credentials;
+import edu.internet2.middleware.shibboleth.common.IdentityProvider;
import edu.internet2.middleware.shibboleth.common.NameIdentifierMapping;
import edu.internet2.middleware.shibboleth.common.NameIdentifierMappingException;
import edu.internet2.middleware.shibboleth.common.RelyingParty;
import edu.internet2.middleware.shibboleth.common.ShibPOSTProfile;
import edu.internet2.middleware.shibboleth.common.ShibResource;
import edu.internet2.middleware.shibboleth.common.ShibbolethOriginConfig;
-import edu.internet2.middleware.shibboleth.common.ShibResource.ResourceNotAvailableException;
public class HandleServlet extends HttpServlet {
private Semaphore throttle;
private ShibbolethOriginConfig configuration;
private Credentials credentials;
- private HSNameMapper nameMapper = new HSNameMapper();
+ private HSNameMapper nameMapper;
private ShibPOSTProfile postProfile = new ShibPOSTProfile();
-
- //TODO this is temporary, until we have the mapper
- private RelyingParty relyingParty;
+ private ServiceProviderMapper targetMapper = new ServiceProviderMapper();
protected void loadConfiguration() throws HSConfigurationException {
- //TODO This should be setup to do schema checking
- DOMParser parser = new DOMParser();
+ DOMParser parser = loadParser(true);
+
String originConfigFile = getInitParameter("OriginConfigFile");
+ if (originConfigFile == null) {
+ originConfigFile = "/conf/origin.xml";
+ }
log.debug("Loading Configuration from (" + originConfigFile + ").");
+
try {
parser.parse(new InputSource(new ShibResource(originConfigFile, this.getClass()).getInputStream()));
- } catch (ResourceNotAvailableException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
} catch (SAXException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ log.error("Error while parsing origin configuration: " + e);
+ throw new HSConfigurationException("Error while parsing origin configuration.");
} catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ log.error("Could not load origin configuration: " + e);
+ throw new HSConfigurationException("Could not load origin configuration.");
}
//Load global configuration properties
}
if (itemElements.getLength() > 1) {
- log.error("Multiple Credentials specifications, using first.");
+ log.error("Multiple Credentials specifications found, using first.");
}
credentials = new Credentials((Element) itemElements.item(0));
for (int i = 0; i < itemElements.getLength(); i++) {
try {
nameMapper.addNameMapping((Element) itemElements.item(i));
- } catch (NameIdentifierMappingException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
+ } catch (NameIdentifierMappingException e) {
+ log.error("Name Identifier mapping could not be loaded: " + e);
}
}
+ }
+
+ private DOMParser loadParser(boolean schemaChecking) throws HSConfigurationException {
+
+ DOMParser parser = new DOMParser();
+
+ if (!schemaChecking) {
+ return parser;
+ }
- //TODO this is temporary, until we have the mapper
- relyingParty = new RelyingParty(null, configuration);
+ try {
+ parser.setFeature("http://xml.org/sax/features/validation", true);
+ parser.setFeature("http://apache.org/xml/features/validation/schema", true);
+
+ parser.setEntityResolver(new EntityResolver() {
+ public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
+ log.debug("Resolving entity for System ID: " + systemId);
+ if (systemId != null) {
+ StringTokenizer tokenString = new StringTokenizer(systemId, "/");
+ String xsdFile = "";
+ while (tokenString.hasMoreTokens()) {
+ xsdFile = tokenString.nextToken();
+ }
+ if (xsdFile.endsWith(".xsd")) {
+ InputStream stream;
+ try {
+ stream = new ShibResource("/schemas/" + xsdFile, this.getClass()).getInputStream();
+ } catch (IOException ioe) {
+ log.error("Error loading schema: " + xsdFile + ": " + ioe);
+ return null;
+ }
+ if (stream != null) {
+ return new InputSource(stream);
+ }
+ }
+ }
+ return null;
+ }
+ });
+
+ parser.setErrorHandler(new ErrorHandler() {
+ public void error(SAXParseException arg0) throws SAXException {
+ throw new SAXException("Error parsing xml file: " + arg0);
+ }
+ public void fatalError(SAXParseException arg0) throws SAXException {
+ throw new SAXException("Error parsing xml file: " + arg0);
+ }
+ public void warning(SAXParseException arg0) throws SAXException {
+ throw new SAXException("Error parsing xml file: " + arg0);
+ }
+ });
+ } catch (SAXException e) {
+ log.error("Unable to setup a workable XML parser: " + e);
+ throw new HSConfigurationException("Unable to setup a workable XML parser.");
+ }
+ return parser;
}
public void init() throws ServletException {
try {
log.info("Initializing Handle Service.");
+ nameMapper = new HSNameMapper();
loadConfiguration();
throttle =
req.setAttribute("shire", req.getParameter("shire"));
req.setAttribute("target", req.getParameter("target"));
- //TODO this is temporary, the first thing to do here is to lookup
- // the relyingParty
+ RelyingParty relyingParty = targetMapper.getRelyingParty(req.getParameter("providerId"));
String header =
relyingParty.getConfigProperty("edu.internet2.middleware.shibboleth.hs.HandleServlet.username");
String username = header.equalsIgnoreCase("REMOTE_USER") ? req.getRemoteUser() : req.getHeader(header);
- //TODO get right data in here
SAMLNameIdentifier nameId =
- nameMapper.getNameIdentifierName(null, new AuthNPrincipal(username), relyingParty, null);
+ nameMapper.getNameIdentifierName(
+ relyingParty.getHSNameFormatId(),
+ new AuthNPrincipal(username),
+ relyingParty,
+ relyingParty.getIdentityProvider());
//Print out something better here
//log.info("Issued Handle (" + handle + ") to (" + username +
// ")");
+ //TODO decide what to do about authMethod
byte[] buf =
generateAssertion(
+ relyingParty,
nameId,
req.getParameter("shire"),
req.getRemoteAddr(),
}
protected byte[] generateAssertion(
+ RelyingParty relyingParty,
SAMLNameIdentifier nameId,
String shireURL,
String clientAddress,
clientAddress,
authType,
new Date(System.currentTimeMillis()),
- Collections.singleton(binding),
- credentials.getCredential(
- relyingParty.getConfigProperty(
- "edu.internet2.middleware.shibboleth.hs.HandleServlet.responseCredential")),
- null);
+ Collections.singleton(binding));
return r.toBase64();
}
notify();
}
}
+ //TODO This is just a stub... and should be moved out when meat is added
+ class ServiceProviderMapper {
+
+ /**
+ * @param providerIdFromTarget
+ * @return
+ */
+ public RelyingParty getRelyingParty(String providerIdFromTarget) {
+
+ return new RelyingParty(null, configuration, credentials);
+ }
+ }
}