saml: move refactor files from server to api module

- Move interfaces and classes from server to api module
- This can be then used for pluggable api authenticators

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

Branch: refs/heads/saml2
Commit: 636b6b5f0b945dfa05f941121a07b359b96fec5c
Parents: fc529a7
Author: Rohit Yadav <rohit.ya...@shapeblue.com>
Authored: Sun Aug 24 15:51:29 2014 +0200
Committer: Rohit Yadav <rohit.ya...@shapeblue.com>
Committed: Mon Aug 25 17:33:26 2014 +0200

----------------------------------------------------------------------
 .../apache/cloudstack/api/ApiServerService.java |  45 +++
 .../api/auth/APIAuthenticationManager.java      |  24 ++
 .../api/auth/APIAuthenticationType.java         |  21 ++
 .../cloudstack/api/auth/APIAuthenticator.java   |  41 +++
 .../cloudstack/SAML2UserAuthenticator.java      |  65 ----
 .../command/SAML2LoginAPIAuthenticatorCmd.java  | 352 +++++++++++++++++++
 .../command/SAML2LogoutAPIAuthenticatorCmd.java |  73 ++++
 .../cloudstack/saml/SAML2UserAuthenticator.java |  65 ++++
 server/src/com/cloud/api/ApiServerService.java  |  45 ---
 .../api/auth/APIAuthenticationManager.java      |  24 --
 .../cloud/api/auth/APIAuthenticationType.java   |  21 --
 .../com/cloud/api/auth/APIAuthenticator.java    |  41 ---
 .../api/auth/SAML2LoginAPIAuthenticatorCmd.java | 350 ------------------
 .../auth/SAML2LogoutAPIAuthenticatorCmd.java    |  71 ----
 14 files changed, 621 insertions(+), 617 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/636b6b5f/api/src/org/apache/cloudstack/api/ApiServerService.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/ApiServerService.java 
b/api/src/org/apache/cloudstack/api/ApiServerService.java
new file mode 100644
index 0000000..9c0cfa3
--- /dev/null
+++ b/api/src/org/apache/cloudstack/api/ApiServerService.java
@@ -0,0 +1,45 @@
+// 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;
+
+import com.cloud.exception.CloudAuthenticationException;
+import org.apache.cloudstack.api.ResponseObject;
+import org.apache.cloudstack.api.ServerApiException;
+
+import javax.servlet.http.HttpSession;
+import java.util.Map;
+
+public interface ApiServerService {
+    public boolean verifyRequest(Map<String, Object[]> requestParameters, Long 
userId) throws ServerApiException;
+
+    public Long fetchDomainId(String domainUUID);
+
+    public ResponseObject loginUser(HttpSession session, String username, 
String password, Long domainId, String domainPath, String loginIpAddress,
+                                    Map<String, Object[]> requestParameters) 
throws CloudAuthenticationException;
+
+    public void logoutUser(long userId);
+
+    public boolean verifyUser(Long userId);
+
+    public String getSerializedApiError(int errorCode, String errorText, 
Map<String, Object[]> apiCommandParams, String responseType);
+
+    public String getSerializedApiError(ServerApiException ex, Map<String, 
Object[]> apiCommandParams, String responseType);
+
+    public String handleRequest(Map params, String responseType, StringBuilder 
auditTrailSb) throws ServerApiException;
+
+    public Class<?> getCmdClass(String cmdName);
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/636b6b5f/api/src/org/apache/cloudstack/api/auth/APIAuthenticationManager.java
----------------------------------------------------------------------
diff --git 
a/api/src/org/apache/cloudstack/api/auth/APIAuthenticationManager.java 
b/api/src/org/apache/cloudstack/api/auth/APIAuthenticationManager.java
new file mode 100644
index 0000000..5d4d664
--- /dev/null
+++ b/api/src/org/apache/cloudstack/api/auth/APIAuthenticationManager.java
@@ -0,0 +1,24 @@
+// 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.auth;
+
+import com.cloud.utils.component.PluggableService;
+
+public interface APIAuthenticationManager extends PluggableService {
+    public APIAuthenticator getAPIAuthenticator(String name);
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/636b6b5f/api/src/org/apache/cloudstack/api/auth/APIAuthenticationType.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/auth/APIAuthenticationType.java 
b/api/src/org/apache/cloudstack/api/auth/APIAuthenticationType.java
new file mode 100644
index 0000000..e8c7f0f
--- /dev/null
+++ b/api/src/org/apache/cloudstack/api/auth/APIAuthenticationType.java
@@ -0,0 +1,21 @@
+// 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.auth;
+
+public enum APIAuthenticationType {
+    LOGIN_API, LOGOUT_API
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/636b6b5f/api/src/org/apache/cloudstack/api/auth/APIAuthenticator.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/auth/APIAuthenticator.java 
b/api/src/org/apache/cloudstack/api/auth/APIAuthenticator.java
new file mode 100644
index 0000000..20fe61f
--- /dev/null
+++ b/api/src/org/apache/cloudstack/api/auth/APIAuthenticator.java
@@ -0,0 +1,41 @@
+// 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.auth;
+
+import org.apache.cloudstack.api.ServerApiException;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.util.Map;
+
+/*
+* APIAuthenticator is an interface that defines method that
+* a class should implement that help with Authentication and accepts
+* a command string and an array of parameters. This should be used only
+* in the Servlet that is doing authentication.
+*
+* @param command The API command name such as login, logout etc
+* @param params An array of HTTP parameters
+* @param session HttpSession object
+* */
+public interface APIAuthenticator {
+    public String authenticate(String command, Map<String, Object[]> params,
+                               HttpSession session, String remoteAddress, 
String responseType,
+                               StringBuilder auditTrailSb, final 
HttpServletResponse resp) throws ServerApiException;
+    public APIAuthenticationType getAPIType();
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/636b6b5f/plugins/user-authenticators/saml2/src/org/apache/cloudstack/SAML2UserAuthenticator.java
----------------------------------------------------------------------
diff --git 
a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/SAML2UserAuthenticator.java
 
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/SAML2UserAuthenticator.java
deleted file mode 100644
index 4d4f1d3..0000000
--- 
a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/SAML2UserAuthenticator.java
+++ /dev/null
@@ -1,65 +0,0 @@
-//  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;
-
-import com.cloud.server.auth.DefaultUserAuthenticator;
-import com.cloud.server.auth.UserAuthenticator;
-import com.cloud.user.User;
-import com.cloud.user.UserAccount;
-import com.cloud.user.dao.UserAccountDao;
-import com.cloud.user.dao.UserDao;
-import com.cloud.utils.Pair;
-import org.apache.log4j.Logger;
-
-import javax.ejb.Local;
-import javax.inject.Inject;
-import java.util.Map;
-
-@Local(value = {UserAuthenticator.class})
-public class SAML2UserAuthenticator extends DefaultUserAuthenticator {
-    public static final Logger s_logger = 
Logger.getLogger(SAML2UserAuthenticator.class);
-
-    @Inject
-    private UserAccountDao _userAccountDao;
-    @Inject
-    private UserDao _userDao;
-
-    @Override
-    public Pair<Boolean, ActionOnFailedAuthentication> authenticate(String 
username, String password, Long domainId, Map<String, Object[]> 
requestParameters) {
-        if (s_logger.isDebugEnabled()) {
-            s_logger.debug("Trying SAML2 auth for user: " + username);
-        }
-        final UserAccount userAccount = 
_userAccountDao.getUserAccount(username, domainId);
-        if (userAccount == null) {
-            s_logger.debug("Unable to find user with " + username + " in 
domain " + domainId);
-            return new Pair<Boolean, ActionOnFailedAuthentication>(false, 
null);
-        } else {
-            User user = _userDao.getUser(userAccount.getId());
-            // TODO: check SAMLRequest, signature etc. from requestParameters
-            if (user != null && user.getUuid().startsWith("saml")) {
-                return new Pair<Boolean, ActionOnFailedAuthentication>(true, 
null);
-            }
-        }
-        // Deny all by default
-        return new Pair<Boolean, ActionOnFailedAuthentication>(false, 
ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
-    }
-
-    @Override
-    public String encode(final String password) {
-        // TODO: Complete method
-        StringBuilder sb = new StringBuilder(32);
-        return sb.toString();
-    }
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/636b6b5f/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java
----------------------------------------------------------------------
diff --git 
a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java
 
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java
new file mode 100644
index 0000000..611c69b
--- /dev/null
+++ 
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LoginAPIAuthenticatorCmd.java
@@ -0,0 +1,352 @@
+// 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 org.apache.cloudstack.api.ApiServerService;
+import com.cloud.api.response.ApiResponseSerializer;
+import com.cloud.exception.CloudAuthenticationException;
+import com.cloud.user.Account;
+import com.cloud.user.User;
+import com.cloud.utils.HttpUtils;
+import com.cloud.utils.db.EntityManager;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.Parameter;
+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.response.LoginCmdResponse;
+import org.apache.cloudstack.context.CallContext;
+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.Assertion;
+import org.opensaml.saml2.core.Attribute;
+import org.opensaml.saml2.core.AttributeStatement;
+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.NameID;
+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.StatusCode;
+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.XMLObject;
+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.signature.Signature;
+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.inject.Inject;
+import javax.servlet.http.Cookie;
+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 java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.math.BigInteger;
+import java.net.URLEncoder;
+import java.security.SecureRandom;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+
+@APICommand(name = "samlsso", description = "SP initiated SAML Single Sign 
On", requestHasSensitiveInfo = true, responseObject = LoginCmdResponse.class, 
entityType = {})
+public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements 
APIAuthenticator {
+    public static final Logger s_logger = 
Logger.getLogger(SAML2LoginAPIAuthenticatorCmd.class.getName());
+    private static final String s_name = "loginresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+    @Parameter(name = ApiConstants.IDP_URL, type = CommandType.STRING, 
description = "Identity Provider SSO HTTP-Redirect binding URL", required = 
true)
+    private String idpUrl;
+
+    @Inject
+    ApiServerService _apiServer;
+    @Inject
+    EntityManager _entityMgr;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public String getIdpUrl() {
+        return idpUrl;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_TYPE_NORMAL;
+    }
+
+    @Override
+    public void execute() throws ServerApiException {
+        // We should never reach here
+        throw new ServerApiException(ApiErrorCode.METHOD_NOT_ALLOWED, "This is 
an authentication api, cannot be used directly");
+    }
+
+    public String buildAuthnRequestUrl(String consumerUrl, String 
identityProviderUrl) {
+        String randomId = new BigInteger(130, new SecureRandom()).toString(32);
+        String spId = "org.apache.cloudstack";
+        String redirectUrl = "";
+        try {
+            DefaultBootstrap.bootstrap();
+            AuthnRequest authnRequest = this.buildAuthnRequestObject(randomId, 
spId, identityProviderUrl, consumerUrl);
+            redirectUrl = identityProviderUrl + "?SAMLRequest=" + 
encodeAuthnRequest(authnRequest);
+        } catch (ConfigurationException | FactoryConfigurationError | 
MarshallingException | IOException e) {
+            s_logger.error("SAML AuthnRequest message building error: " + 
e.getMessage());
+        }
+        return redirectUrl;
+    }
+
+    private 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;
+    }
+
+    private String encodeAuthnRequest(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 Response processSAMLResponse(String responseMessage) {
+        XMLObject responseObject = null;
+        try {
+            responseObject = this.unmarshall(responseMessage);
+
+        } catch (ConfigurationException | ParserConfigurationException | 
SAXException | IOException | UnmarshallingException e) {
+            s_logger.error("SAMLResponse processing error: " + e.getMessage());
+        }
+        return (Response) responseObject;
+    }
+
+    private XMLObject unmarshall(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 unmarshaller.unmarshall(element);
+    }
+
+    @Override
+    public String authenticate(final String command, final Map<String, 
Object[]> params, final HttpSession session, final String remoteAddress, final 
String responseType, final StringBuilder auditTrailSb, final 
HttpServletResponse resp) throws ServerApiException {
+        try {
+            if (!params.containsKey("SAMLResponse")) {
+                final String[] idps = (String[])params.get("idpurl");
+                String redirectUrl = 
buildAuthnRequestUrl("http://localhost:8080/client/api?command=samlsso";, 
idps[0]);
+                resp.sendRedirect(redirectUrl);
+                return "";
+            } else {
+                final String samlResponse = 
((String[])params.get("SAMLResponse"))[0];
+                Response processedSAMLResponse = 
processSAMLResponse(samlResponse);
+                String statusCode = 
processedSAMLResponse.getStatus().getStatusCode().getValue();
+                if (!statusCode.equals(StatusCode.SUCCESS_URI)) {
+                    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, 
_apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                            "Identity Provider send a non-successful 
authentication status code",
+                            params, responseType));
+                }
+
+                Signature sig = processedSAMLResponse.getSignature();
+                //SignatureValidator validator = new 
SignatureValidator(credential);
+                //validator.validate(sig);
+
+                String uniqueUserId = null;
+                String accountName = "admin"; //GET from config, try, fail
+                Long domainId = 1L; // GET from config, try, fail
+                String username = null;
+                String password = "";
+                String firstName = "";
+                String lastName = "";
+                String timeZone = "";
+                String email = "";
+
+                Assertion assertion = 
processedSAMLResponse.getAssertions().get(0);
+                NameID nameId = assertion.getSubject().getNameID();
+
+                if (nameId.getFormat().equals(NameIDType.PERSISTENT) || 
nameId.getFormat().equals(NameIDType.EMAIL)) {
+                    username = nameId.getValue();
+                    uniqueUserId = "saml-" + username;
+                    if (nameId.getFormat().equals(NameIDType.EMAIL)) {
+                        email = username;
+                    }
+                }
+
+                String issuer = assertion.getIssuer().getValue();
+                String audience = 
assertion.getConditions().getAudienceRestrictions().get(0).getAudiences().get(0).getAudienceURI();
+                AttributeStatement attributeStatement = 
assertion.getAttributeStatements().get(0);
+                List<Attribute> attributes = 
attributeStatement.getAttributes();
+
+                // Try capturing standard LDAP attributes
+                for (Attribute attribute: attributes) {
+                    String attributeName = attribute.getName();
+                    String attributeValue = 
attribute.getAttributeValues().get(0).getDOM().getTextContent();
+                    if (attributeName.equalsIgnoreCase("uid") && uniqueUserId 
== null) {
+                        username = attributeValue;
+                        uniqueUserId = "saml-" + username;
+                    } else if (attributeName.equalsIgnoreCase("givenName")) {
+                        firstName = attributeValue;
+                    } else if (attributeName.equalsIgnoreCase(("sn"))) {
+                        lastName = attributeValue;
+                    } else if (attributeName.equalsIgnoreCase("mail")) {
+                        email = attributeValue;
+                    }
+                }
+
+                User user = _entityMgr.findByUuid(User.class, uniqueUserId);
+                if (user == null && uniqueUserId != null && username != null
+                        && accountName != null && domainId != null) {
+                    CallContext.current().setEventDetails("UserName: " + 
username + ", FirstName :" + password + ", LastName: " + lastName);
+                    user = _accountService.createUser(username, password, 
firstName, lastName, email, timeZone, accountName, domainId, uniqueUserId);
+                }
+
+                if (user != null) {
+                    try {
+                        if (_apiServer.verifyUser(user.getId())) {
+                            LoginCmdResponse loginResponse = 
(LoginCmdResponse) _apiServer.loginUser(session, username, user.getPassword(), 
domainId, null, remoteAddress, params);
+                            resp.addCookie(new Cookie("userid", 
loginResponse.getUserId()));
+                            resp.addCookie(new Cookie("domainid", 
loginResponse.getDomainId()));
+                            resp.addCookie(new Cookie("role", 
loginResponse.getType()));
+                            resp.addCookie(new Cookie("username", 
URLEncoder.encode(loginResponse.getUsername(), HttpUtils.UTF_8)));
+                            resp.addCookie(new Cookie("sessionKey", 
URLEncoder.encode(loginResponse.getSessionKey(), HttpUtils.UTF_8)));
+                            resp.addCookie(new Cookie("account", 
URLEncoder.encode(loginResponse.getAccount(), HttpUtils.UTF_8)));
+                            resp.addCookie(new Cookie("timezone", 
URLEncoder.encode(loginResponse.getTimeZone(), HttpUtils.UTF_8)));
+                            resp.addCookie(new Cookie("userfullname", 
loginResponse.getFirstName() + "%20" + loginResponse.getLastName()));
+                            resp.sendRedirect("http://localhost:8080/client";);
+                            return 
ApiResponseSerializer.toSerializedString(loginResponse, responseType);
+
+                        }
+                    } catch (final CloudAuthenticationException ignored) {
+                    }
+                }
+            }
+        } catch (IOException e) {
+            auditTrailSb.append("SP initiated SAML authentication using HTTP 
redirection failed:");
+            auditTrailSb.append(e.getMessage());
+        }
+        throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, 
_apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                "Unable to authenticate or retrieve user while performing SAML 
based SSO",
+                params, responseType));
+    }
+
+    @Override
+    public APIAuthenticationType getAPIType() {
+        return APIAuthenticationType.LOGIN_API;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/636b6b5f/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LogoutAPIAuthenticatorCmd.java
----------------------------------------------------------------------
diff --git 
a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LogoutAPIAuthenticatorCmd.java
 
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LogoutAPIAuthenticatorCmd.java
new file mode 100644
index 0000000..b82f2c8
--- /dev/null
+++ 
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/SAML2LogoutAPIAuthenticatorCmd.java
@@ -0,0 +1,73 @@
+// 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.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.response.LogoutCmdResponse;
+import org.apache.log4j.Logger;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.util.Map;
+
+@APICommand(name = "samlslo", description = "SAML Single Log Out API", 
responseObject = LogoutCmdResponse.class, entityType = {})
+public class SAML2LogoutAPIAuthenticatorCmd extends BaseCmd implements 
APIAuthenticator {
+    public static final Logger s_logger = 
Logger.getLogger(SAML2LogoutAPIAuthenticatorCmd.class.getName());
+    private static final String s_name = "logoutresponse";
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_TYPE_NORMAL;
+    }
+
+    @Override
+    public void execute() throws ServerApiException {
+        // We should never reach here
+        throw new ServerApiException(ApiErrorCode.METHOD_NOT_ALLOWED, "This is 
an authentication api, cannot be used directly");
+    }
+
+    @Override
+    public String authenticate(String command, Map<String, Object[]> params, 
HttpSession session, String remoteAddress, String responseType, StringBuilder 
auditTrailSb, final HttpServletResponse resp) throws ServerApiException {
+        auditTrailSb.append("=== Logging out ===");
+        // TODO: check global config and do either local or global log out
+        LogoutCmdResponse response = new LogoutCmdResponse();
+        response.setDescription("success");
+        response.setResponseName(getCommandName());
+        return ApiResponseSerializer.toSerializedString(response, 
responseType);
+    }
+
+    @Override
+    public APIAuthenticationType getAPIType() {
+        return APIAuthenticationType.LOGOUT_API;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/636b6b5f/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2UserAuthenticator.java
----------------------------------------------------------------------
diff --git 
a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2UserAuthenticator.java
 
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2UserAuthenticator.java
new file mode 100644
index 0000000..1a37f65
--- /dev/null
+++ 
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2UserAuthenticator.java
@@ -0,0 +1,65 @@
+//  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.saml;
+
+import com.cloud.server.auth.DefaultUserAuthenticator;
+import com.cloud.server.auth.UserAuthenticator;
+import com.cloud.user.User;
+import com.cloud.user.UserAccount;
+import com.cloud.user.dao.UserAccountDao;
+import com.cloud.user.dao.UserDao;
+import com.cloud.utils.Pair;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+import javax.inject.Inject;
+import java.util.Map;
+
+@Local(value = {UserAuthenticator.class})
+public class SAML2UserAuthenticator extends DefaultUserAuthenticator {
+    public static final Logger s_logger = 
Logger.getLogger(SAML2UserAuthenticator.class);
+
+    @Inject
+    private UserAccountDao _userAccountDao;
+    @Inject
+    private UserDao _userDao;
+
+    @Override
+    public Pair<Boolean, ActionOnFailedAuthentication> authenticate(String 
username, String password, Long domainId, Map<String, Object[]> 
requestParameters) {
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("Trying SAML2 auth for user: " + username);
+        }
+        final UserAccount userAccount = 
_userAccountDao.getUserAccount(username, domainId);
+        if (userAccount == null) {
+            s_logger.debug("Unable to find user with " + username + " in 
domain " + domainId);
+            return new Pair<Boolean, ActionOnFailedAuthentication>(false, 
null);
+        } else {
+            User user = _userDao.getUser(userAccount.getId());
+            // TODO: check SAMLRequest, signature etc. from requestParameters
+            if (user != null && user.getUuid().startsWith("saml")) {
+                return new Pair<Boolean, ActionOnFailedAuthentication>(true, 
null);
+            }
+        }
+        // Deny all by default
+        return new Pair<Boolean, ActionOnFailedAuthentication>(false, 
ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT);
+    }
+
+    @Override
+    public String encode(final String password) {
+        // TODO: Complete method
+        StringBuilder sb = new StringBuilder(32);
+        return sb.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/636b6b5f/server/src/com/cloud/api/ApiServerService.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiServerService.java 
b/server/src/com/cloud/api/ApiServerService.java
deleted file mode 100644
index aa3b8f7..0000000
--- a/server/src/com/cloud/api/ApiServerService.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// 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 com.cloud.api;
-
-import com.cloud.exception.CloudAuthenticationException;
-import org.apache.cloudstack.api.ResponseObject;
-import org.apache.cloudstack.api.ServerApiException;
-
-import javax.servlet.http.HttpSession;
-import java.util.Map;
-
-public interface ApiServerService {
-    public boolean verifyRequest(Map<String, Object[]> requestParameters, Long 
userId) throws ServerApiException;
-
-    public Long fetchDomainId(String domainUUID);
-
-    public ResponseObject loginUser(HttpSession session, String username, 
String password, Long domainId, String domainPath, String loginIpAddress,
-                                    Map<String, Object[]> requestParameters) 
throws CloudAuthenticationException;
-
-    public void logoutUser(long userId);
-
-    public boolean verifyUser(Long userId);
-
-    public String getSerializedApiError(int errorCode, String errorText, 
Map<String, Object[]> apiCommandParams, String responseType);
-
-    public String getSerializedApiError(ServerApiException ex, Map<String, 
Object[]> apiCommandParams, String responseType);
-
-    public String handleRequest(Map params, String responseType, StringBuilder 
auditTrailSb) throws ServerApiException;
-
-    public Class<?> getCmdClass(String cmdName);
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/636b6b5f/server/src/com/cloud/api/auth/APIAuthenticationManager.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/auth/APIAuthenticationManager.java 
b/server/src/com/cloud/api/auth/APIAuthenticationManager.java
deleted file mode 100644
index 7fd4da9..0000000
--- a/server/src/com/cloud/api/auth/APIAuthenticationManager.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// 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 com.cloud.api.auth;
-
-import com.cloud.utils.component.PluggableService;
-
-public interface APIAuthenticationManager extends PluggableService {
-    public APIAuthenticator getAPIAuthenticator(String name);
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/636b6b5f/server/src/com/cloud/api/auth/APIAuthenticationType.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/auth/APIAuthenticationType.java 
b/server/src/com/cloud/api/auth/APIAuthenticationType.java
deleted file mode 100644
index bdd37ec..0000000
--- a/server/src/com/cloud/api/auth/APIAuthenticationType.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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 com.cloud.api.auth;
-
-public enum APIAuthenticationType {
-    LOGIN_API, LOGOUT_API
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/636b6b5f/server/src/com/cloud/api/auth/APIAuthenticator.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/auth/APIAuthenticator.java 
b/server/src/com/cloud/api/auth/APIAuthenticator.java
deleted file mode 100644
index 90cd7ec..0000000
--- a/server/src/com/cloud/api/auth/APIAuthenticator.java
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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 com.cloud.api.auth;
-
-import org.apache.cloudstack.api.ServerApiException;
-
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import java.util.Map;
-
-/*
-* APIAuthenticator is an interface that defines method that
-* a class should implement that help with Authentication and accepts
-* a command string and an array of parameters. This should be used only
-* in the Servlet that is doing authentication.
-*
-* @param command The API command name such as login, logout etc
-* @param params An array of HTTP parameters
-* @param session HttpSession object
-* */
-public interface APIAuthenticator {
-    public String authenticate(String command, Map<String, Object[]> params,
-                               HttpSession session, String remoteAddress, 
String responseType,
-                               StringBuilder auditTrailSb, final 
HttpServletResponse resp) throws ServerApiException;
-    public APIAuthenticationType getAPIType();
-
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/636b6b5f/server/src/com/cloud/api/auth/SAML2LoginAPIAuthenticatorCmd.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/auth/SAML2LoginAPIAuthenticatorCmd.java 
b/server/src/com/cloud/api/auth/SAML2LoginAPIAuthenticatorCmd.java
deleted file mode 100644
index ce97cfd..0000000
--- a/server/src/com/cloud/api/auth/SAML2LoginAPIAuthenticatorCmd.java
+++ /dev/null
@@ -1,350 +0,0 @@
-// 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 com.cloud.api.auth;
-
-import com.cloud.api.ApiServerService;
-import com.cloud.api.response.ApiResponseSerializer;
-import com.cloud.exception.CloudAuthenticationException;
-import com.cloud.user.Account;
-import com.cloud.user.User;
-import com.cloud.utils.HttpUtils;
-import com.cloud.utils.db.EntityManager;
-import org.apache.cloudstack.api.APICommand;
-import org.apache.cloudstack.api.ApiConstants;
-import org.apache.cloudstack.api.ApiErrorCode;
-import org.apache.cloudstack.api.BaseCmd;
-import org.apache.cloudstack.api.Parameter;
-import org.apache.cloudstack.api.ServerApiException;
-import org.apache.cloudstack.api.response.LoginCmdResponse;
-import org.apache.cloudstack.context.CallContext;
-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.Assertion;
-import org.opensaml.saml2.core.Attribute;
-import org.opensaml.saml2.core.AttributeStatement;
-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.NameID;
-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.StatusCode;
-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.XMLObject;
-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.signature.Signature;
-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.inject.Inject;
-import javax.servlet.http.Cookie;
-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 java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.math.BigInteger;
-import java.net.URLEncoder;
-import java.security.SecureRandom;
-import java.util.List;
-import java.util.Map;
-import java.util.zip.Deflater;
-import java.util.zip.DeflaterOutputStream;
-
-@APICommand(name = "samlsso", description = "SP initiated SAML Single Sign 
On", requestHasSensitiveInfo = true, responseObject = LoginCmdResponse.class, 
entityType = {})
-public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements 
APIAuthenticator {
-    public static final Logger s_logger = 
Logger.getLogger(SAML2LoginAPIAuthenticatorCmd.class.getName());
-    private static final String s_name = "loginresponse";
-
-    /////////////////////////////////////////////////////
-    //////////////// API parameters /////////////////////
-    /////////////////////////////////////////////////////
-    @Parameter(name = ApiConstants.IDP_URL, type = CommandType.STRING, 
description = "Identity Provider SSO HTTP-Redirect binding URL", required = 
true)
-    private String idpUrl;
-
-    @Inject
-    ApiServerService _apiServer;
-    @Inject
-    EntityManager _entityMgr;
-
-    /////////////////////////////////////////////////////
-    /////////////////// Accessors ///////////////////////
-    /////////////////////////////////////////////////////
-
-    public String getIdpUrl() {
-        return idpUrl;
-    }
-
-    /////////////////////////////////////////////////////
-    /////////////// API Implementation///////////////////
-    /////////////////////////////////////////////////////
-
-    @Override
-    public String getCommandName() {
-        return s_name;
-    }
-
-    @Override
-    public long getEntityOwnerId() {
-        return Account.ACCOUNT_TYPE_NORMAL;
-    }
-
-    @Override
-    public void execute() throws ServerApiException {
-        // We should never reach here
-        throw new ServerApiException(ApiErrorCode.METHOD_NOT_ALLOWED, "This is 
an authentication api, cannot be used directly");
-    }
-
-    public String buildAuthnRequestUrl(String consumerUrl, String 
identityProviderUrl) {
-        String randomId = new BigInteger(130, new SecureRandom()).toString(32);
-        String spId = "org.apache.cloudstack";
-        String redirectUrl = "";
-        try {
-            DefaultBootstrap.bootstrap();
-            AuthnRequest authnRequest = this.buildAuthnRequestObject(randomId, 
spId, identityProviderUrl, consumerUrl);
-            redirectUrl = identityProviderUrl + "?SAMLRequest=" + 
encodeAuthnRequest(authnRequest);
-        } catch (ConfigurationException | FactoryConfigurationError | 
MarshallingException | IOException e) {
-            s_logger.error("SAML AuthnRequest message building error: " + 
e.getMessage());
-        }
-        return redirectUrl;
-    }
-
-    private 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); 
//SAML2_ARTIFACT_BINDING_URI);
-        authnRequest.setAssertionConsumerServiceURL(consumerUrl);
-        authnRequest.setNameIDPolicy(nameIdPolicy);
-        authnRequest.setRequestedAuthnContext(requestedAuthnContext);
-
-        return authnRequest;
-    }
-
-    private String encodeAuthnRequest(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 Response processSAMLResponse(String responseMessage) {
-        XMLObject responseObject = null;
-        try {
-            responseObject = this.unmarshall(responseMessage);
-
-        } catch (ConfigurationException | ParserConfigurationException | 
SAXException | IOException | UnmarshallingException e) {
-            s_logger.error("SAMLResponse processing error: " + e.getMessage());
-        }
-        return (Response) responseObject;
-    }
-
-    private XMLObject unmarshall(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 unmarshaller.unmarshall(element);
-    }
-
-    @Override
-    public String authenticate(final String command, final Map<String, 
Object[]> params, final HttpSession session, final String remoteAddress, final 
String responseType, final StringBuilder auditTrailSb, final 
HttpServletResponse resp) throws ServerApiException {
-        try {
-            if (!params.containsKey("SAMLResponse")) {
-                final String[] idps = (String[])params.get("idpurl");
-                String redirectUrl = 
buildAuthnRequestUrl("http://localhost:8080/client/api?command=samlsso";, 
idps[0]);
-                resp.sendRedirect(redirectUrl);
-                return "";
-            } else {
-                final String samlResponse = 
((String[])params.get("SAMLResponse"))[0];
-                Response processedSAMLResponse = 
processSAMLResponse(samlResponse);
-                String statusCode = 
processedSAMLResponse.getStatus().getStatusCode().getValue();
-                if (!statusCode.equals(StatusCode.SUCCESS_URI)) {
-                    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, 
_apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
-                            "Identity Provider send a non-successful 
authentication status code",
-                            params, responseType));
-                }
-
-                Signature sig = processedSAMLResponse.getSignature();
-                //SignatureValidator validator = new 
SignatureValidator(credential);
-                //validator.validate(sig);
-
-                String uniqueUserId = null;
-                String accountName = "admin"; //GET from config, try, fail
-                Long domainId = 1L; // GET from config, try, fail
-                String username = null;
-                String password = "";
-                String firstName = "";
-                String lastName = "";
-                String timeZone = "";
-                String email = "";
-
-                Assertion assertion = 
processedSAMLResponse.getAssertions().get(0);
-                NameID nameId = assertion.getSubject().getNameID();
-
-                if (nameId.getFormat().equals(NameIDType.PERSISTENT) || 
nameId.getFormat().equals(NameIDType.EMAIL)) {
-                    username = nameId.getValue();
-                    uniqueUserId = "saml-" + username;
-                    if (nameId.getFormat().equals(NameIDType.EMAIL)) {
-                        email = username;
-                    }
-                }
-
-                String issuer = assertion.getIssuer().getValue();
-                String audience = 
assertion.getConditions().getAudienceRestrictions().get(0).getAudiences().get(0).getAudienceURI();
-                AttributeStatement attributeStatement = 
assertion.getAttributeStatements().get(0);
-                List<Attribute> attributes = 
attributeStatement.getAttributes();
-
-                // Try capturing standard LDAP attributes
-                for (Attribute attribute: attributes) {
-                    String attributeName = attribute.getName();
-                    String attributeValue = 
attribute.getAttributeValues().get(0).getDOM().getTextContent();
-                    if (attributeName.equalsIgnoreCase("uid") && uniqueUserId 
== null) {
-                        username = attributeValue;
-                        uniqueUserId = "saml-" + username;
-                    } else if (attributeName.equalsIgnoreCase("givenName")) {
-                        firstName = attributeValue;
-                    } else if (attributeName.equalsIgnoreCase(("sn"))) {
-                        lastName = attributeValue;
-                    } else if (attributeName.equalsIgnoreCase("mail")) {
-                        email = attributeValue;
-                    }
-                }
-
-                User user = _entityMgr.findByUuid(User.class, uniqueUserId);
-                if (user == null && uniqueUserId != null && username != null
-                        && accountName != null && domainId != null) {
-                    CallContext.current().setEventDetails("UserName: " + 
username + ", FirstName :" + password + ", LastName: " + lastName);
-                    user = _accountService.createUser(username, password, 
firstName, lastName, email, timeZone, accountName, domainId, uniqueUserId);
-                }
-
-                if (user != null) {
-                    try {
-                        if (_apiServer.verifyUser(user.getId())) {
-                            LoginCmdResponse loginResponse = 
(LoginCmdResponse) _apiServer.loginUser(session, username, user.getPassword(), 
domainId, null, remoteAddress, params);
-                            resp.addCookie(new Cookie("userid", 
loginResponse.getUserId()));
-                            resp.addCookie(new Cookie("domainid", 
loginResponse.getDomainId()));
-                            resp.addCookie(new Cookie("role", 
loginResponse.getType()));
-                            resp.addCookie(new Cookie("username", 
URLEncoder.encode(loginResponse.getUsername(), HttpUtils.UTF_8)));
-                            resp.addCookie(new Cookie("sessionKey", 
URLEncoder.encode(loginResponse.getSessionKey(), HttpUtils.UTF_8)));
-                            resp.addCookie(new Cookie("account", 
URLEncoder.encode(loginResponse.getAccount(), HttpUtils.UTF_8)));
-                            resp.addCookie(new Cookie("timezone", 
URLEncoder.encode(loginResponse.getTimeZone(), HttpUtils.UTF_8)));
-                            resp.addCookie(new Cookie("userfullname", 
loginResponse.getFirstName() + "%20" + loginResponse.getLastName()));
-                            resp.sendRedirect("http://localhost:8080/client";);
-                            return 
ApiResponseSerializer.toSerializedString(loginResponse, responseType);
-
-                        }
-                    } catch (final CloudAuthenticationException ignored) {
-                    }
-                }
-            }
-        } catch (IOException e) {
-            auditTrailSb.append("SP initiated SAML authentication using HTTP 
redirection failed:");
-            auditTrailSb.append(e.getMessage());
-        }
-        throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, 
_apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
-                "Unable to authenticate or retrieve user while performing SAML 
based SSO",
-                params, responseType));
-    }
-
-    @Override
-    public APIAuthenticationType getAPIType() {
-        return APIAuthenticationType.LOGIN_API;
-    }
-}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/636b6b5f/server/src/com/cloud/api/auth/SAML2LogoutAPIAuthenticatorCmd.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/auth/SAML2LogoutAPIAuthenticatorCmd.java 
b/server/src/com/cloud/api/auth/SAML2LogoutAPIAuthenticatorCmd.java
deleted file mode 100644
index 9119588..0000000
--- a/server/src/com/cloud/api/auth/SAML2LogoutAPIAuthenticatorCmd.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// 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 com.cloud.api.auth;
-
-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.BaseCmd;
-import org.apache.cloudstack.api.ServerApiException;
-import org.apache.cloudstack.api.response.LogoutCmdResponse;
-import org.apache.log4j.Logger;
-
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-import java.util.Map;
-
-@APICommand(name = "samlslo", description = "SAML Single Log Out API", 
responseObject = LogoutCmdResponse.class, entityType = {})
-public class SAML2LogoutAPIAuthenticatorCmd extends BaseCmd implements 
APIAuthenticator {
-    public static final Logger s_logger = 
Logger.getLogger(SAML2LogoutAPIAuthenticatorCmd.class.getName());
-    private static final String s_name = "logoutresponse";
-
-    /////////////////////////////////////////////////////
-    /////////////// API Implementation///////////////////
-    /////////////////////////////////////////////////////
-
-    @Override
-    public String getCommandName() {
-        return s_name;
-    }
-
-    @Override
-    public long getEntityOwnerId() {
-        return Account.ACCOUNT_TYPE_NORMAL;
-    }
-
-    @Override
-    public void execute() throws ServerApiException {
-        // We should never reach here
-        throw new ServerApiException(ApiErrorCode.METHOD_NOT_ALLOWED, "This is 
an authentication api, cannot be used directly");
-    }
-
-    @Override
-    public String authenticate(String command, Map<String, Object[]> params, 
HttpSession session, String remoteAddress, String responseType, StringBuilder 
auditTrailSb, final HttpServletResponse resp) throws ServerApiException {
-        auditTrailSb.append("=== Logging out ===");
-        // TODO: check global config and do either local or global log out
-        LogoutCmdResponse response = new LogoutCmdResponse();
-        response.setDescription("success");
-        response.setResponseName(getCommandName());
-        return ApiResponseSerializer.toSerializedString(response, 
responseType);
-    }
-
-    @Override
-    public APIAuthenticationType getAPIType() {
-        return APIAuthenticationType.LOGOUT_API;
-    }
-}

Reply via email to