cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bhais...@apache.org
Subject [3/3] git commit: updated refs/heads/4.5-samlfixes to b6782a0
Date Mon, 03 Aug 2015 19:46:29 GMT
CLOUDSTACK-8701: Allow SAML users to switch accounts

SAML authorized accounts might be across various domains, this allows for
switching of accounts only in case of SAML authenticated user accounts across
other accounts with the same SAML uid/username.

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/b6782a05
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/b6782a05
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/b6782a05

Branch: refs/heads/4.5-samlfixes
Commit: b6782a05fa34287a10551828f0f129a8f866703b
Parents: c067b51
Author: Rohit Yadav <rohit.yadav@shapeblue.com>
Authored: Mon Aug 3 12:36:14 2015 +0530
Committer: Rohit Yadav <rohit.yadav@shapeblue.com>
Committed: Tue Aug 4 01:16:09 2015 +0530

----------------------------------------------------------------------
 client/tomcatconf/commands.properties.in        |   1 +
 .../command/GetServiceProviderMetaDataCmd.java  |   2 +-
 .../command/ListAndSwitchSAMLAccountCmd.java    | 195 +++++++++++++++++++
 .../cloudstack/api/command/ListIdpsCmd.java     |   2 +-
 .../command/SAML2LoginAPIAuthenticatorCmd.java  |  36 +---
 .../api/response/SamlUserAccountResponse.java   |  99 ++++++++++
 .../cloudstack/saml/SAML2AuthManagerImpl.java   |   2 +
 .../cloudstack/saml/SAML2UserAuthenticator.java |  22 +--
 .../org/apache/cloudstack/saml/SAMLUtils.java   |  18 ++
 ui/index.jsp                                    |   7 -
 ui/scripts/ui-custom/login.js                   |   8 +-
 ui/scripts/ui/core.js                           |  77 ++++++++
 12 files changed, 401 insertions(+), 68 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b6782a05/client/tomcatconf/commands.properties.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in
index a66a3dc..a69605c 100644
--- a/client/tomcatconf/commands.properties.in
+++ b/client/tomcatconf/commands.properties.in
@@ -29,6 +29,7 @@ getSPMetadata=15
 listIdps=15
 authorizeSamlSso=7
 listSamlAuthorization=7
+listAndSwitchSamlAccount=15
 
 ### Account commands
 createAccount=7

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b6782a05/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
index ffaad7a..bed594f 100644
--- 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
@@ -266,7 +266,7 @@ public class GetServiceProviderMetaDataCmd extends BaseCmd implements
APIAuthent
             }
         }
         if (_samlAuthManager == null) {
-            s_logger.error("No suitable Pluggable Authentication Manager found for SAML2
Login Cmd");
+            s_logger.error("No suitable Pluggable Authentication Manager found for SAML2
getSPMetadata Cmd");
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b6782a05/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListAndSwitchSAMLAccountCmd.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListAndSwitchSAMLAccountCmd.java
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListAndSwitchSAMLAccountCmd.java
new file mode 100644
index 0000000..db0d6dc
--- /dev/null
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListAndSwitchSAMLAccountCmd.java
@@ -0,0 +1,195 @@
+// 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.domain.Domain;
+import com.cloud.domain.dao.DomainDao;
+import com.cloud.user.Account;
+import com.cloud.user.User;
+import com.cloud.user.UserAccount;
+import com.cloud.user.UserAccountVO;
+import com.cloud.user.dao.UserAccountDao;
+import com.cloud.user.dao.UserDao;
+import com.cloud.utils.HttpUtils;
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.ApiServerService;
+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.auth.PluggableAPIAuthenticator;
+import org.apache.cloudstack.api.response.DomainResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.LoginCmdResponse;
+import org.apache.cloudstack.api.response.SamlUserAccountResponse;
+import org.apache.cloudstack.api.response.SuccessResponse;
+import org.apache.cloudstack.api.response.UserResponse;
+import org.apache.cloudstack.saml.SAML2AuthManager;
+import org.apache.cloudstack.saml.SAMLUtils;
+import org.apache.log4j.Logger;
+
+import javax.inject.Inject;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@APICommand(name = "listAndSwitchSamlAccount", description = "Lists and switches to other
SAML accounts owned by the SAML user", responseObject = SuccessResponse.class, requestHasSensitiveInfo
= false, responseHasSensitiveInfo = false)
+public class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthenticator {
+    public static final Logger s_logger = Logger.getLogger(ListAndSwitchSAMLAccountCmd.class.getName());
+    private static final String s_name = "listandswitchsamlaccountresponse";
+
+    @Inject
+    ApiServerService _apiServer;
+
+    @Inject
+    private UserAccountDao _userAccountDao;
+    @Inject
+    private UserDao _userDao;
+    @Inject
+    private DomainDao _domainDao;
+
+    SAML2AuthManager _samlAuthManager;
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.USER_ID, type = CommandType.UUID, entityType = UserResponse.class,
required = false, description = "User uuid")
+    private Long userId;
+
+    @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class,
required = false, description = "Domain uuid")
+    private Long domainId;
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        return Account.ACCOUNT_ID_SYSTEM;
+    }
+
+    @Override
+    public void execute() {
+        throw new ServerApiException(ApiErrorCode.METHOD_NOT_ALLOWED, "This is an authentication
plugin api, cannot be used directly");
+    }
+
+    @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 HttpServletRequest req, final HttpServletResponse resp) throws ServerApiException
{
+        if (session == null || session.isNew()) {
+            throw new ServerApiException(ApiErrorCode.UNAUTHORIZED, _apiServer.getSerializedApiError(ApiErrorCode.UNAUTHORIZED.getHttpCode(),
+                    "Only authenticated saml users can request this API",
+                    params, responseType));
+        }
+
+        if (!HttpUtils.validateSessionKey(session, params, req.getCookies(), ApiConstants.SESSIONKEY))
{
+            throw new ServerApiException(ApiErrorCode.UNAUTHORIZED, _apiServer.getSerializedApiError(ApiErrorCode.UNAUTHORIZED.getHttpCode(),
+                    "Unauthorized session, please re-login",
+                    params, responseType));
+        }
+
+        final long currentUserId = (Long) session.getAttribute("userid");
+        final UserAccount currentUserAccount = _accountService.getUserAccountById(currentUserId);
+        if (currentUserAccount == null || currentUserAccount.getSource() != User.Source.SAML2)
{
+            throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                    "Only authenticated saml users can request this API",
+                    params, responseType));
+        }
+
+        String userUuid = null;
+        String domainUuid = null;
+        if (params.containsKey(ApiConstants.USER_ID)) {
+            userUuid = ((String[])params.get(ApiConstants.USER_ID))[0];
+        }
+        if (params.containsKey(ApiConstants.DOMAIN_ID)) {
+            domainUuid = ((String[])params.get(ApiConstants.DOMAIN_ID))[0];
+        }
+
+        if (userUuid != null && domainUuid != null) {
+            final User user = _userDao.findByUuid(userUuid);
+            final Domain domain = _domainDao.findByUuid(domainUuid);
+            final UserAccount nextUserAccount = _accountService.getUserAccountById(user.getId());
+            if (!nextUserAccount.getUsername().equals(currentUserAccount.getUsername())
+                    || !nextUserAccount.getExternalEntity().equals(currentUserAccount.getExternalEntity())
+                    || (nextUserAccount.getDomainId() != domain.getId())
+                    || (nextUserAccount.getSource() != User.Source.SAML2)) {
+                throw new ServerApiException(ApiErrorCode.PARAM_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(),
+                        "User account is not allowed to switch to the requested account",
+                        params, responseType));
+            }
+            try {
+                if (_apiServer.verifyUser(nextUserAccount.getId())) {
+                    final LoginCmdResponse loginResponse = (LoginCmdResponse) _apiServer.loginUser(session,
nextUserAccount.getUsername(), nextUserAccount.getUsername() + nextUserAccount.getSource().toString(),
+                            nextUserAccount.getDomainId(), null, remoteAddress, params);
+                    SAMLUtils.setupSamlUserCookies(loginResponse, resp);
+                    resp.sendRedirect(SAML2AuthManager.SAMLCloudStackRedirectionUrl.value());
+                    return ApiResponseSerializer.toSerializedString(loginResponse, responseType);
+                }
+            } catch (final Exception ignored) {
+            }
+        } else {
+            List<UserAccountVO> switchableAccounts = _userAccountDao.getAllUsersByNameAndEntity(currentUserAccount.getUsername(),
currentUserAccount.getExternalEntity());
+            if (switchableAccounts != null && switchableAccounts.size() > 0 &&
currentUserId != User.UID_SYSTEM) {
+                List<SamlUserAccountResponse> accountResponses = new ArrayList<SamlUserAccountResponse>();
+                for (UserAccountVO userAccount: switchableAccounts) {
+                    User user = _userDao.getUser(userAccount.getId());
+                    Domain domain = _domainService.getDomain(userAccount.getDomainId());
+                    SamlUserAccountResponse accountResponse = new SamlUserAccountResponse();
+                    accountResponse.setUserId(user.getUuid());
+                    accountResponse.setUserName(user.getUsername());
+                    accountResponse.setDomainId(domain.getUuid());
+                    accountResponse.setDomainName(domain.getName());
+                    accountResponse.setAccountName(userAccount.getAccountName());
+                    accountResponse.setIdpId(user.getExternalEntity());
+                    accountResponses.add(accountResponse);
+                }
+                ListResponse<SamlUserAccountResponse> response = new ListResponse<SamlUserAccountResponse>();
+                response.setResponses(accountResponses);
+                response.setResponseName(getCommandName());
+                return ApiResponseSerializer.toSerializedString(response, responseType);
+            }
+        }
+        throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
+                "Unable to switch to requested SAML account. Please make sure your user/account
is enabled. Please contact your administrator.",
+                params, responseType));
+    }
+
+    @Override
+    public APIAuthenticationType getAPIType() {
+        return APIAuthenticationType.READONLY_API;
+    }
+
+    @Override
+    public void setAuthenticators(List<PluggableAPIAuthenticator> authenticators) {
+        for (PluggableAPIAuthenticator authManager: authenticators) {
+            if (authManager != null && authManager instanceof SAML2AuthManager) {
+                _samlAuthManager = (SAML2AuthManager) authManager;
+            }
+        }
+        if (_samlAuthManager == null) {
+            s_logger.error("No suitable Pluggable Authentication Manager found for SAML2
listAndSwitchSamlAccount Cmd");
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b6782a05/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListIdpsCmd.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListIdpsCmd.java
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListIdpsCmd.java
index 43ecfc5..4771342 100644
--- a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListIdpsCmd.java
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/command/ListIdpsCmd.java
@@ -111,4 +111,4 @@ public class ListIdpsCmd extends BaseCmd implements APIAuthenticator {
             s_logger.error("No suitable Pluggable Authentication Manager found for SAML2
Login Cmd");
         }
     }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b6782a05/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
index d4940ec..188eb6b 100644
--- 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
@@ -17,13 +17,11 @@
 package org.apache.cloudstack.api.command;
 
 import com.cloud.api.response.ApiResponseSerializer;
-import com.cloud.exception.CloudAuthenticationException;
 import com.cloud.user.Account;
 import com.cloud.user.DomainManager;
 import com.cloud.user.UserAccount;
 import com.cloud.user.UserAccountVO;
 import com.cloud.user.dao.UserAccountDao;
-import com.cloud.utils.HttpUtils;
 import com.cloud.utils.db.EntityManager;
 import org.apache.cloudstack.api.APICommand;
 import org.apache.cloudstack.api.ApiConstants;
@@ -64,14 +62,12 @@ import org.opensaml.xml.validation.ValidationException;
 import org.xml.sax.SAXException;
 
 import javax.inject.Inject;
-import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.stream.FactoryConfigurationError;
 import java.io.IOException;
-import java.net.URLEncoder;
 import java.util.List;
 import java.util.Map;
 
@@ -195,7 +191,6 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements
APIAuthent
                 }
 
                 String username = null;
-                Long domainId = null;
                 Issuer issuer = processedSAMLResponse.getIssuer();
                 SAMLProviderMetadata spMetadata = _samlAuthManager.getSPMetadata();
                 SAMLProviderMetadata idpMetadata = _samlAuthManager.getIdPMetadata(issuer.getValue());
@@ -204,9 +199,6 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements
APIAuthent
                 s_logger.debug("Received SAMLResponse in response to id=" + responseToId);
                 SAMLTokenVO token = _samlAuthManager.getToken(responseToId);
                 if (token != null) {
-                    if (token.getDomainId() != null) {
-                        domainId = token.getDomainId();
-                    }
                     if (!(token.getEntity().equalsIgnoreCase(issuer.getValue()))) {
                         throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
                                 "The SAML response contains Issuer Entity ID that is different
from the original SAML request",
@@ -298,17 +290,9 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements
APIAuthent
                 UserAccount userAccount = null;
                 List<UserAccountVO> possibleUserAccounts = _userAccountDao.getAllUsersByNameAndEntity(username,
issuer.getValue());
                 if (possibleUserAccounts != null && possibleUserAccounts.size() >
0) {
-                    if (possibleUserAccounts.size() == 1) {
-                        userAccount = possibleUserAccounts.get(0);
-                    } else if (possibleUserAccounts.size() > 1) {
-                        if (domainId != null) {
-                            userAccount = _userAccountDao.getUserAccount(username, domainId);
-                        } else {
-                            throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(),
-                                    "You have accounts in multiple domains, please re-login
by specifying the domain you want to log into.",
-                                    params, responseType));
-                        }
-                    }
+                    // By default, log into the first user account
+                    // Users can switch to other allowed accounts later
+                    userAccount = possibleUserAccounts.get(0);
                 }
 
                 if (userAccount == null || userAccount.getExternalEntity() == null || !_samlAuthManager.isUserAuthorized(userAccount.getId(),
issuer.getValue())) {
@@ -322,21 +306,11 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements
APIAuthent
                         if (_apiServer.verifyUser(userAccount.getId())) {
                             LoginCmdResponse loginResponse = (LoginCmdResponse) _apiServer.loginUser(session,
userAccount.getUsername(), userAccount.getUsername() + userAccount.getSource().toString(),
                                     userAccount.getDomainId(), null, remoteAddress, params);
-                            resp.addCookie(new Cookie("userid", URLEncoder.encode(loginResponse.getUserId(),
HttpUtils.UTF_8)));
-                            resp.addCookie(new Cookie("domainid", URLEncoder.encode(loginResponse.getDomainId(),
HttpUtils.UTF_8)));
-                            resp.addCookie(new Cookie("role", URLEncoder.encode(loginResponse.getType(),
HttpUtils.UTF_8)));
-                            resp.addCookie(new Cookie("username", URLEncoder.encode(loginResponse.getUsername(),
HttpUtils.UTF_8)));
-                            resp.addCookie(new Cookie("account", URLEncoder.encode(loginResponse.getAccount(),
HttpUtils.UTF_8)));
-                            String timezone = loginResponse.getTimeZone();
-                            if (timezone != null) {
-                                resp.addCookie(new Cookie("timezone", URLEncoder.encode(timezone,
HttpUtils.UTF_8)));
-                            }
-                            resp.addCookie(new Cookie("userfullname", URLEncoder.encode(loginResponse.getFirstName()
+ " " + loginResponse.getLastName(), HttpUtils.UTF_8).replace("+", "%20")));
-                            resp.addHeader("SET-COOKIE", String.format("%s=%s;HttpOnly",
ApiConstants.SESSIONKEY, loginResponse.getSessionKey()));
+                            SAMLUtils.setupSamlUserCookies(loginResponse, resp);
                             resp.sendRedirect(SAML2AuthManager.SAMLCloudStackRedirectionUrl.value());
                             return ApiResponseSerializer.toSerializedString(loginResponse,
responseType);
                         }
-                    } catch (final CloudAuthenticationException ignored) {
+                    } catch (final Exception ignored) {
                     }
                 }
             }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b6782a05/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/SamlUserAccountResponse.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/SamlUserAccountResponse.java
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/SamlUserAccountResponse.java
new file mode 100644
index 0000000..f0927e3
--- /dev/null
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/api/response/SamlUserAccountResponse.java
@@ -0,0 +1,99 @@
+// 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;
+
+public class SamlUserAccountResponse extends AuthenticationCmdResponse {
+    @SerializedName("userId")
+    @Param(description = "The User Id")
+    private String userId;
+
+    @SerializedName("domainId")
+    @Param(description = "The Domain Id")
+    private String domainId;
+
+    @SerializedName("userName")
+    @Param(description = "The User Name")
+    private String userName;
+
+    @SerializedName("accountName")
+    @Param(description = "The Account Name")
+    private String accountName;
+
+    @SerializedName("domainName")
+    @Param(description = "The Domain Name")
+    private String domainName;
+
+    @SerializedName("idpId")
+    @Param(description = "The IDP ID")
+    private String idpId;
+
+    public SamlUserAccountResponse() {
+        super();
+        setObjectName("samluseraccount");
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public void setUserId(String userId) {
+        this.userId = userId;
+    }
+
+    public String getDomainId() {
+        return domainId;
+    }
+
+    public void setDomainId(String domainId) {
+        this.domainId = domainId;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public String getAccountName() {
+        return accountName;
+    }
+
+    public void setAccountName(String accountName) {
+        this.accountName = accountName;
+    }
+
+    public String getDomainName() {
+        return domainName;
+    }
+
+    public void setDomainName(String domainName) {
+        this.domainName = domainName;
+    }
+
+    public String getIdpId() {
+        return idpId;
+    }
+
+    public void setIdpId(String idpId) {
+        this.idpId = idpId;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b6782a05/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 7232ac9..46b1c40 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
@@ -26,6 +26,7 @@ import com.cloud.utils.component.AdapterBase;
 import org.apache.cloudstack.api.auth.PluggableAPIAuthenticator;
 import org.apache.cloudstack.api.command.AuthorizeSAMLSSOCmd;
 import org.apache.cloudstack.api.command.GetServiceProviderMetaDataCmd;
+import org.apache.cloudstack.api.command.ListAndSwitchSAMLAccountCmd;
 import org.apache.cloudstack.api.command.ListIdpsCmd;
 import org.apache.cloudstack.api.command.ListSamlAuthorizationCmd;
 import org.apache.cloudstack.api.command.SAML2LoginAPIAuthenticatorCmd;
@@ -491,6 +492,7 @@ public class SAML2AuthManagerImpl extends AdapterBase implements SAML2AuthManage
         cmdList.add(SAML2LogoutAPIAuthenticatorCmd.class);
         cmdList.add(GetServiceProviderMetaDataCmd.class);
         cmdList.add(ListIdpsCmd.class);
+        cmdList.add(ListAndSwitchSAMLAccountCmd.class);
         return cmdList;
     }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b6782a05/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
index 5c8a390..65a7959 100644
--- a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2UserAuthenticator.java
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAML2UserAuthenticator.java
@@ -23,18 +23,9 @@ import com.cloud.user.dao.UserDao;
 import com.cloud.utils.Pair;
 import org.apache.cxf.common.util.StringUtils;
 import org.apache.log4j.Logger;
-import org.opensaml.DefaultBootstrap;
-import org.opensaml.saml2.core.Response;
-import org.opensaml.saml2.core.StatusCode;
-import org.opensaml.xml.ConfigurationException;
-import org.opensaml.xml.io.UnmarshallingException;
-import org.xml.sax.SAXException;
 
 import javax.ejb.Local;
 import javax.inject.Inject;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.stream.FactoryConfigurationError;
-import java.io.IOException;
 import java.util.Map;
 
 @Local(value = {UserAuthenticator.class})
@@ -63,18 +54,7 @@ public class SAML2UserAuthenticator extends DefaultUserAuthenticator {
             return new Pair<Boolean, ActionOnFailedAuthentication>(false, null);
         } else {
             User user = _userDao.getUser(userAccount.getId());
-            if (user != null && requestParameters != null && requestParameters.containsKey(SAMLPluginConstants.SAML_RESPONSE))
{
-                final String samlResponse = ((String[])requestParameters.get(SAMLPluginConstants.SAML_RESPONSE))[0];
-                Response responseObject = null;
-                try {
-                    DefaultBootstrap.bootstrap();
-                    responseObject = SAMLUtils.decodeSAMLResponse(samlResponse);
-                } catch (ConfigurationException | FactoryConfigurationError | ParserConfigurationException
| SAXException | IOException | UnmarshallingException e) {
-                    return new Pair<Boolean, ActionOnFailedAuthentication>(false, null);
-                }
-                if (!responseObject.getStatus().getStatusCode().getValue().equals(StatusCode.SUCCESS_URI))
{
-                    return new Pair<Boolean, ActionOnFailedAuthentication>(false, null);
-                }
+            if (user != null && user.getSource() == User.Source.SAML2 &&
user.getExternalEntity() != null) {
                 return new Pair<Boolean, ActionOnFailedAuthentication>(true, null);
             }
         }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b6782a05/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLUtils.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLUtils.java
b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLUtils.java
index 0216ad7..fb2d960 100644
--- a/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLUtils.java
+++ b/plugins/user-authenticators/saml2/src/org/apache/cloudstack/saml/SAMLUtils.java
@@ -20,6 +20,8 @@
 package org.apache.cloudstack.saml;
 
 import com.cloud.utils.HttpUtils;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.response.LoginCmdResponse;
 import org.apache.log4j.Logger;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.x509.X509V1CertificateGenerator;
@@ -62,6 +64,8 @@ import org.w3c.dom.Element;
 import org.xml.sax.SAXException;
 
 import javax.security.auth.x500.X500Principal;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
@@ -351,4 +355,18 @@ public class SAMLUtils {
         return certGen.generate(keyPair.getPrivate(), "BC");
     }
 
+    public static void setupSamlUserCookies(final LoginCmdResponse loginResponse, final HttpServletResponse
resp) throws IOException {
+        resp.addCookie(new Cookie("userid", URLEncoder.encode(loginResponse.getUserId(),
HttpUtils.UTF_8)));
+        resp.addCookie(new Cookie("domainid", URLEncoder.encode(loginResponse.getDomainId(),
HttpUtils.UTF_8)));
+        resp.addCookie(new Cookie("role", URLEncoder.encode(loginResponse.getType(), HttpUtils.UTF_8)));
+        resp.addCookie(new Cookie("username", URLEncoder.encode(loginResponse.getUsername(),
HttpUtils.UTF_8)));
+        resp.addCookie(new Cookie("account", URLEncoder.encode(loginResponse.getAccount(),
HttpUtils.UTF_8)));
+        String timezone = loginResponse.getTimeZone();
+        if (timezone != null) {
+            resp.addCookie(new Cookie("timezone", URLEncoder.encode(timezone, HttpUtils.UTF_8)));
+        }
+        resp.addCookie(new Cookie("userfullname", URLEncoder.encode(loginResponse.getFirstName()
+ " " + loginResponse.getLastName(), HttpUtils.UTF_8).replace("+", "%20")));
+        resp.addHeader("SET-COOKIE", String.format("%s=%s;HttpOnly", ApiConstants.SESSIONKEY,
loginResponse.getSessionKey()));
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b6782a05/ui/index.jsp
----------------------------------------------------------------------
diff --git a/ui/index.jsp b/ui/index.jsp
index c3bae19..3b7d310 100644
--- a/ui/index.jsp
+++ b/ui/index.jsp
@@ -75,13 +75,6 @@
                             </div>
                         </div>
 
-                        <div id="saml-login">
-                            <div class="field domain">
-                                <label for="saml-domain"><fmt:message key="label.domain"/></label>
-                                <input id="saml-domain" type="text" name="saml-domain"
/>
-                            </div>
-                        </div>
-
                         <div id="login-submit">
                             <!-- Submit (login) -->
                             <input id="login-submit" type="submit" value="<fmt:message
key="label.login"/>" />

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b6782a05/ui/scripts/ui-custom/login.js
----------------------------------------------------------------------
diff --git a/ui/scripts/ui-custom/login.js b/ui/scripts/ui-custom/login.js
index e112958..5825529 100644
--- a/ui/scripts/ui-custom/login.js
+++ b/ui/scripts/ui-custom/login.js
@@ -122,8 +122,7 @@
             } else if (selectedLogin === 'saml') {
                 // SAML
                 args.samlLoginAction({
-                    data: {'idpid': $login.find('#login-options').find(':selected').val(),
-                          'domain': $login.find('#saml-domain').val()}
+                    data: {'idpid': $login.find('#login-options').find(':selected').val()}
                 });
             }
             return false;
@@ -133,16 +132,13 @@
         var toggleLoginView = function (selectedOption) {
             $login.find('#login-submit').show();
             if (selectedOption === '') {
-                    $login.find('#saml-login').hide();
                     $login.find('#cloudstack-login').hide();
                     $login.find('#login-submit').hide();
                     selectedLogin = 'none';
             } else if (selectedOption === 'cloudstack-login') {
-                    $login.find('#saml-login').hide();
                     $login.find('#cloudstack-login').show();
                     selectedLogin = 'cloudstack';
             } else {
-                    $login.find('#saml-login').show();
                     $login.find('#cloudstack-login').hide();
                     selectedLogin = 'saml';
             }
@@ -160,14 +156,12 @@
         $login.find('#login-dropdown').hide();
         $login.find('#login-submit').show();
         $login.find('#cloudstack-login').show();
-        $login.find('#saml-login').hide();
 
         // If any IdP servers were set, SAML is enabled
         if (g_idpList && g_idpList.length > 0) {
             $login.find('#login-dropdown').show();
             $login.find('#login-submit').hide();
             $login.find('#cloudstack-login').hide();
-            $login.find('#saml-login').hide();
 
             $login.find('#login-options')
                 .append($('<option>', {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b6782a05/ui/scripts/ui/core.js
----------------------------------------------------------------------
diff --git a/ui/scripts/ui/core.js b/ui/scripts/ui/core.js
index e64f386..26a5a1c 100644
--- a/ui/scripts/ui/core.js
+++ b/ui/scripts/ui/core.js
@@ -304,6 +304,83 @@
         })
             .appendTo($('#user'));
 
+        // Switch account option for SAML users
+        if (g_idpList) {
+            $.ajax({
+                url: createURL('listAndSwitchSamlAccount'),
+                success: function(json) {
+                    var accounts = json.listandswitchsamlaccountresponse.samluseraccount;
+                    if (accounts.length < 2) {
+                        return
+                    };
+                    var switchAccount = function() {
+                        var toReload = true;
+                        $.ajax({
+                            url: createURL('listAndSwitchSamlAccount'),
+                            type: 'POST',
+                            async: false,
+                            data: {
+                                userid: $(this).data("userId"),
+                                domainid: $(this).data("domainId")
+                            },
+                            success: function(data, textStatus) {
+                                document.location.reload(true);
+                            },
+                            error: function(data) {
+                               cloudStack.dialog.notice({
+                                    message: parseXMLHttpResponse(data)
+                                });
+                                if (data.status !== 200) {
+                                    toReload = false;
+                                }
+                            },
+                            complete: function() {
+                                if (toReload) {
+                                    document.location.reload(true);
+                                }
+                                toReload = true;
+                            }
+                        });
+                    };
+
+                    $('<a>').attr({
+                            href: '#'
+                        })
+                        .text('Switch Account')
+                        .appendTo($options).click(function() {
+                            var $switchSamlAccount = $('<div>');
+                            for (var i = 0; i < accounts.length; i++) {
+                                var item = $('<a>').html(accounts[i].accountName +
"/" + accounts[i].domainName);
+                                item.data("userId", accounts[i].userId);
+                                item.data("domainId", accounts[i].domainId);
+                                item.click(switchAccount);
+                                item.appendTo($switchSamlAccount);
+                                item.attr("href", "#");
+                                $('<br>').appendTo($switchSamlAccount);
+                            }
+                            $switchSamlAccount.dialog({
+                                modal: true,
+                                width: 400,
+                                title: 'Switch Account',
+                                closeOnEscape: false,
+                                dialogClass: 'dialog-about',
+                                buttons: {
+                                    'Close': function() {
+                                        $(this).dialog("close");
+                                        $(':ui-dialog, .overlay').remove();
+                                    }
+                                }
+                            }).closest('.ui-dialog').overlay();
+
+                            return false;
+                        });
+                },
+                error: function(data) {
+                }
+            });
+        }
+
+
         $(['label.logout', 'label.help', 'label.about']).each(function() {
             var $link = $('<a>')
                 .attr({


Mime
View raw message