saml2: Add GetServiceProviderMetaDataCmd that returns SP metadata XML

This adds GetServiceProviderMetaDataCmd which returns SP metadata XML, since
this information should be public for IdPs to discover, we implement this as a
login/cmd api so this does not require any kind of authentication to GET this

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/99ae373b
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/99ae373b
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/99ae373b

Branch: refs/heads/saml2
Commit: 99ae373b9eb7dbe74e4802ab6e3bb8b2b2f60c1a
Parents: b84b2c1
Author: Rohit Yadav <rohit.ya...@shapeblue.com>
Authored: Mon Aug 25 00:13:32 2014 +0200
Committer: Rohit Yadav <rohit.ya...@shapeblue.com>
Committed: Mon Aug 25 17:33:28 2014 +0200

----------------------------------------------------------------------
 .../command/GetServiceProviderMetaDataCmd.java  | 202 +++++++++++++++++++
 .../api/response/SAMLMetaDataResponse.java      |  40 ++++
 .../cloudstack/saml/SAML2AuthManagerImpl.java   |   2 +
 3 files changed, 244 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99ae373b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmd.java
----------------------------------------------------------------------
diff --git 
a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmd.java
 
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmd.java
new file mode 100644
index 0000000..16ee088
--- /dev/null
+++ 
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/GetServiceProviderMetaDataCmd.java
@@ -0,0 +1,202 @@
+// 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.api.command;
+
+import com.cloud.api.response.ApiResponseSerializer;
+import com.cloud.user.Account;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.ApiServerService;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.auth.APIAuthenticationType;
+import org.apache.cloudstack.api.auth.APIAuthenticator;
+import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator;
+import org.apache.cloudstack.api.response.SAMLMetaDataResponse;
+import org.apache.cloudstack.saml.SAML2AuthManager;
+import org.apache.log4j.Logger;
+import org.opensaml.Configuration;
+import org.opensaml.DefaultBootstrap;
+import org.opensaml.common.xml.SAMLConstants;
+import org.opensaml.saml2.core.NameIDType;
+import org.opensaml.saml2.metadata.AssertionConsumerService;
+import org.opensaml.saml2.metadata.EntityDescriptor;
+import org.opensaml.saml2.metadata.KeyDescriptor;
+import org.opensaml.saml2.metadata.NameIDFormat;
+import org.opensaml.saml2.metadata.SPSSODescriptor;
+import org.opensaml.saml2.metadata.SingleLogoutService;
+import org.opensaml.saml2.metadata.impl.AssertionConsumerServiceBuilder;
+import org.opensaml.saml2.metadata.impl.EntityDescriptorBuilder;
+import org.opensaml.saml2.metadata.impl.KeyDescriptorBuilder;
+import org.opensaml.saml2.metadata.impl.NameIDFormatBuilder;
+import org.opensaml.saml2.metadata.impl.SPSSODescriptorBuilder;
+import org.opensaml.saml2.metadata.impl.SingleLogoutServiceBuilder;
+import org.opensaml.xml.ConfigurationException;
+import org.opensaml.xml.io.Marshaller;
+import org.opensaml.xml.io.MarshallingException;
+import org.opensaml.xml.security.SecurityException;
+import org.opensaml.xml.security.credential.UsageType;
+import org.opensaml.xml.security.keyinfo.KeyInfoGenerator;
+import org.opensaml.xml.security.x509.BasicX509Credential;
+import org.opensaml.xml.security.x509.X509KeyInfoGeneratorFactory;
+import org.w3c.dom.Document;
+
+import javax.inject.Inject;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.List;
+import java.util.Map;
+
+@APICommand(name = "getSPMetadata", description = "Returns SAML2 CloudStack 
Service Provider MetaData", responseObject = SAMLMetaDataResponse.class, 
entityType = {})
+public class GetServiceProviderMetaDataCmd extends BaseCmd implements 
APIAuthenticator {
+    public static final Logger s_logger = 
Logger.getLogger(GetServiceProviderMetaDataCmd.class.getName());
+    private static final String s_name = "spmetadataresponse";
+
+    @Inject
+    ApiServerService _apiServer;
+
+    SAML2AuthManager _samlAuthManager;
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_TYPE_NORMAL;
+    }
+
+    @Override
+    public void execute() throws ServerApiException {
+        throw new ServerApiException(ApiErrorCode.METHOD_NOT_ALLOWED, "This is 
an authentication plugin api, cannot be used directly");
+    }
+
+    @Override
+    public String authenticate(String command, Map<String, Object[]> params, 
HttpSession session, String remoteAddress, String responseType, StringBuilder 
auditTrailSb, HttpServletResponse resp) throws ServerApiException {
+        SAMLMetaDataResponse response = new SAMLMetaDataResponse();
+        response.setResponseName(getCommandName());
+
+        try {
+            DefaultBootstrap.bootstrap();
+        } catch (ConfigurationException | FactoryConfigurationError e) {
+            s_logger.error("OpenSAML Bootstrapping error: " + e.getMessage());
+            throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, 
_apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                    "OpenSAML Bootstrapping error while creating SP MetaData",
+                    params, responseType));
+        }
+
+        EntityDescriptor spEntityDescriptor = new 
EntityDescriptorBuilder().buildObject();
+        
spEntityDescriptor.setEntityID(_samlAuthManager.getServiceProviderId());
+
+        SPSSODescriptor spSSODescriptor = new 
SPSSODescriptorBuilder().buildObject();
+        spSSODescriptor.setWantAssertionsSigned(true);
+        spSSODescriptor.setAuthnRequestsSigned(false);
+
+        X509KeyInfoGeneratorFactory keyInfoGeneratorFactory = new 
X509KeyInfoGeneratorFactory();
+        keyInfoGeneratorFactory.setEmitEntityCertificate(true);
+        KeyInfoGenerator keyInfoGenerator = 
keyInfoGeneratorFactory.newInstance();
+
+        KeyDescriptor encKeyDescriptor = new 
KeyDescriptorBuilder().buildObject();
+        encKeyDescriptor.setUse(UsageType.ENCRYPTION);
+
+        KeyDescriptor signKeyDescriptor = new 
KeyDescriptorBuilder().buildObject();
+        signKeyDescriptor.setUse(UsageType.SIGNING);
+
+        BasicX509Credential credential = new BasicX509Credential();
+        credential.setEntityCertificate(_samlAuthManager.getIdpSigningKey());
+        try {
+            encKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(credential));
+            
signKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(credential));
+            //TODO: generate own pub/priv keys
+            //spSSODescriptor.getKeyDescriptors().add(encKeyDescriptor);
+            //spSSODescriptor.getKeyDescriptors().add(signKeyDescriptor);
+        } catch (SecurityException ignored) {
+        }
+
+        NameIDFormat nameIDFormat = new NameIDFormatBuilder().buildObject();
+        nameIDFormat.setFormat(NameIDType.PERSISTENT);
+        spSSODescriptor.getNameIDFormats().add(nameIDFormat);
+
+        AssertionConsumerService assertionConsumerService = new 
AssertionConsumerServiceBuilder().buildObject();
+        assertionConsumerService.setIndex(0);
+        
assertionConsumerService.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
+        
assertionConsumerService.setLocation(_samlAuthManager.getSpSingleSignOnUrl());
+
+        SingleLogoutService ssoService = new 
SingleLogoutServiceBuilder().buildObject();
+        ssoService.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
+        ssoService.setLocation(_samlAuthManager.getSpSingleLogOutUrl());
+
+        spSSODescriptor.getSingleLogoutServices().add(ssoService);
+        
spSSODescriptor.getAssertionConsumerServices().add(assertionConsumerService);
+        spSSODescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS);
+        spEntityDescriptor.getRoleDescriptors().add(spSSODescriptor);
+
+        try {
+            DocumentBuilderFactory factory = 
DocumentBuilderFactory.newInstance();
+            DocumentBuilder builder = factory.newDocumentBuilder();
+            Document document = builder.newDocument();
+            Marshaller out = 
Configuration.getMarshallerFactory().getMarshaller(spEntityDescriptor);
+            out.marshall(spEntityDescriptor, document);
+
+            Transformer transformer = 
TransformerFactory.newInstance().newTransformer();
+            StringWriter stringWriter = new StringWriter();
+            StreamResult streamResult = new StreamResult(stringWriter);
+            DOMSource source = new DOMSource(document);
+            transformer.transform(source, streamResult);
+            stringWriter.close();
+            response.setMetadata(stringWriter.toString());
+        } catch (ParserConfigurationException | IOException | 
MarshallingException | TransformerException e) {
+            response.setMetadata("Error creating Service Provider MetaData 
XML: " + e.getMessage());
+        }
+
+        return ApiResponseSerializer.toSerializedString(response, 
responseType);
+    }
+
+    @Override
+    public APIAuthenticationType getAPIType() {
+        return APIAuthenticationType.LOGIN_API;
+    }
+
+    @Override
+    public void setAuthenticators(List<PluggableAPIAuthenticator> 
authenticators) {
+        for (PluggableAPIAuthenticator authManager: authenticators) {
+            if (authManager instanceof SAML2AuthManager) {
+                _samlAuthManager = (SAML2AuthManager) authManager;
+            }
+        }
+        if (_samlAuthManager == null) {
+            s_logger.error("No suitable Pluggable Authentication Manager found 
for SAML2 Login Cmd");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99ae373b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/SAMLMetaDataResponse.java
----------------------------------------------------------------------
diff --git 
a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/SAMLMetaDataResponse.java
 
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/SAMLMetaDataResponse.java
new file mode 100644
index 0000000..e091ea4
--- /dev/null
+++ 
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/SAMLMetaDataResponse.java
@@ -0,0 +1,40 @@
+// 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.api.response;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+import org.apache.cloudstack.api.BaseResponse;
+
+public class SAMLMetaDataResponse extends BaseResponse {
+
+    @SerializedName("metadata")
+    @Param(description = "The Metadata XML")
+    private String metadata;
+
+    public SAMLMetaDataResponse() {
+        super();
+    }
+
+    public String getMetadata() {
+        return metadata;
+    }
+
+    public void setMetadata(String metadata) {
+        this.metadata = metadata;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/99ae373b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java
----------------------------------------------------------------------
diff --git 
a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java
 
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java
index 7ef126a..22d99cb 100644
--- 
a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java
+++ 
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2AuthManagerImpl.java
@@ -19,6 +19,7 @@ package org.apache.cloudstack.saml;
 import com.cloud.configuration.Config;
 import com.cloud.utils.component.AdapterBase;
 import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator;
+import org.apache.cloudstack.api.command.GetServiceProviderMetaDataCmd;
 import org.apache.cloudstack.api.command.SAML2LoginAPIAuthenticatorCmd;
 import org.apache.cloudstack.api.command.SAML2LogoutAPIAuthenticatorCmd;
 import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
@@ -147,6 +148,7 @@ public class SAML2AuthManagerImpl extends AdapterBase 
implements SAML2AuthManage
         List<Class<?>> cmdList = new ArrayList<Class<?>>();
         cmdList.add(SAML2LoginAPIAuthenticatorCmd.class);
         cmdList.add(SAML2LogoutAPIAuthenticatorCmd.class);
+        cmdList.add(GetServiceProviderMetaDataCmd.class);
         return cmdList;
     }
 

Reply via email to