utils: refactor and aggregate methods in SAMLUtils Signed-off-by: Rohit Yadav <rohit.ya...@shapeblue.com>
Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/6bbed76b Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/6bbed76b Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/6bbed76b Branch: refs/heads/saml2 Commit: 6bbed76b86376d2b23e626ca83827f994b54240e Parents: 2144386 Author: Rohit Yadav <rohit.ya...@shapeblue.com> Authored: Sun Aug 24 15:50:07 2014 +0200 Committer: Rohit Yadav <rohit.ya...@shapeblue.com> Committed: Sun Aug 24 15:50:07 2014 +0200 ---------------------------------------------------------------------- utils/pom.xml | 5 + .../apache/cloudstack/utils/auth/SAMLUtils.java | 162 +++++++++++++++++++ 2 files changed, 167 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6bbed76b/utils/pom.xml ---------------------------------------------------------------------- diff --git a/utils/pom.xml b/utils/pom.xml index 27eeee3..7dafbba 100755 --- a/utils/pom.xml +++ b/utils/pom.xml @@ -144,6 +144,11 @@ </exclusions> </dependency> <dependency> + <groupId>org.opensaml</groupId> + <artifactId>opensaml</artifactId> + <version>${cs.opensaml.version}</version> + </dependency> + <dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>3.3</version> http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6bbed76b/utils/src/org/apache/cloudstack/utils/auth/SAMLUtils.java ---------------------------------------------------------------------- diff --git a/utils/src/org/apache/cloudstack/utils/auth/SAMLUtils.java b/utils/src/org/apache/cloudstack/utils/auth/SAMLUtils.java new file mode 100644 index 0000000..bc39eaf --- /dev/null +++ b/utils/src/org/apache/cloudstack/utils/auth/SAMLUtils.java @@ -0,0 +1,162 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.utils.auth; + +import org.apache.log4j.Logger; +import org.joda.time.DateTime; +import org.opensaml.Configuration; +import org.opensaml.DefaultBootstrap; +import org.opensaml.common.SAMLVersion; +import org.opensaml.common.xml.SAMLConstants; +import org.opensaml.saml2.core.AuthnContextClassRef; +import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration; +import org.opensaml.saml2.core.AuthnRequest; +import org.opensaml.saml2.core.Issuer; +import org.opensaml.saml2.core.NameIDPolicy; +import org.opensaml.saml2.core.NameIDType; +import org.opensaml.saml2.core.RequestedAuthnContext; +import org.opensaml.saml2.core.Response; +import org.opensaml.saml2.core.impl.AuthnContextClassRefBuilder; +import org.opensaml.saml2.core.impl.AuthnRequestBuilder; +import org.opensaml.saml2.core.impl.IssuerBuilder; +import org.opensaml.saml2.core.impl.NameIDPolicyBuilder; +import org.opensaml.saml2.core.impl.RequestedAuthnContextBuilder; +import org.opensaml.xml.ConfigurationException; +import org.opensaml.xml.io.Marshaller; +import org.opensaml.xml.io.MarshallingException; +import org.opensaml.xml.io.Unmarshaller; +import org.opensaml.xml.io.UnmarshallerFactory; +import org.opensaml.xml.io.UnmarshallingException; +import org.opensaml.xml.util.Base64; +import org.opensaml.xml.util.XMLHelper; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.FactoryConfigurationError; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringWriter; +import java.net.URLEncoder; +import java.util.zip.Deflater; +import java.util.zip.DeflaterOutputStream; + +public class SAMLUtils { + public static final Logger s_logger = Logger.getLogger(SAMLUtils.class); + + public static final String SAML_NS = "saml-"; + + public static String createSAMLId(String uid) { + return SAML_NS + uid; + } + + public static Boolean checkSAMLUserId(String uuid) { + return uuid.startsWith(SAML_NS); + } + + public static AuthnRequest buildAuthnRequestObject(String authnId, String spId, String idpUrl, String consumerUrl) { + // Issuer object + IssuerBuilder issuerBuilder = new IssuerBuilder(); + Issuer issuer = issuerBuilder.buildObject(); + issuer.setValue(spId); + + // NameIDPolicy + NameIDPolicyBuilder nameIdPolicyBuilder = new NameIDPolicyBuilder(); + NameIDPolicy nameIdPolicy = nameIdPolicyBuilder.buildObject(); + nameIdPolicy.setFormat(NameIDType.PERSISTENT); + nameIdPolicy.setSPNameQualifier("Apache CloudStack"); + nameIdPolicy.setAllowCreate(true); + + // AuthnContextClass + AuthnContextClassRefBuilder authnContextClassRefBuilder = new AuthnContextClassRefBuilder(); + AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject( + SAMLConstants.SAML20_NS, + "AuthnContextClassRef", "saml"); + authnContextClassRef.setAuthnContextClassRef("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"); + + // AuthnContex + RequestedAuthnContextBuilder requestedAuthnContextBuilder = new RequestedAuthnContextBuilder(); + RequestedAuthnContext requestedAuthnContext = requestedAuthnContextBuilder.buildObject(); + requestedAuthnContext + .setComparison(AuthnContextComparisonTypeEnumeration.MINIMUM); + requestedAuthnContext.getAuthnContextClassRefs().add( + authnContextClassRef); + + // Creation of AuthRequestObject + AuthnRequestBuilder authRequestBuilder = new AuthnRequestBuilder(); + AuthnRequest authnRequest = authRequestBuilder.buildObject(); + authnRequest.setID(authnId); + authnRequest.setDestination(idpUrl); + authnRequest.setVersion(SAMLVersion.VERSION_20); + authnRequest.setForceAuthn(true); + authnRequest.setIsPassive(false); + authnRequest.setIssuer(issuer); + authnRequest.setIssueInstant(new DateTime()); + authnRequest.setProviderName("Apache CloudStack"); + authnRequest.setProtocolBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI); + authnRequest.setAssertionConsumerServiceURL(consumerUrl); + authnRequest.setNameIDPolicy(nameIdPolicy); + authnRequest.setRequestedAuthnContext(requestedAuthnContext); + + return authnRequest; + } + + public static String encodeSAMLRequest(AuthnRequest authnRequest) + throws MarshallingException, IOException { + Marshaller marshaller = Configuration.getMarshallerFactory() + .getMarshaller(authnRequest); + Element authDOM = marshaller.marshall(authnRequest); + StringWriter requestWriter = new StringWriter(); + XMLHelper.writeNode(authDOM, requestWriter); + String requestMessage = requestWriter.toString(); + Deflater deflater = new Deflater(Deflater.DEFLATED, true); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(byteArrayOutputStream, deflater); + deflaterOutputStream.write(requestMessage.getBytes()); + deflaterOutputStream.close(); + String encodedRequestMessage = Base64.encodeBytes(byteArrayOutputStream.toByteArray(), Base64.DONT_BREAK_LINES); + encodedRequestMessage = URLEncoder.encode(encodedRequestMessage, "UTF-8").trim(); + return encodedRequestMessage; + } + + public static Response decodeSAMLResponse(String responseMessage) + throws ConfigurationException, ParserConfigurationException, + SAXException, IOException, UnmarshallingException { + try { + DefaultBootstrap.bootstrap(); + } catch (ConfigurationException | FactoryConfigurationError e) { + s_logger.error("SAML response message decoding error: " + e.getMessage()); + } + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + DocumentBuilder docBuilder = documentBuilderFactory.newDocumentBuilder(); + byte[] base64DecodedResponse = Base64.decode(responseMessage); + Document document = docBuilder.parse(new ByteArrayInputStream(base64DecodedResponse)); + Element element = document.getDocumentElement(); + UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory(); + Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(element); + return (Response) unmarshaller.unmarshall(element); + } + +}