mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-11-04 00:02:37 +01:00 
			
		
		
		
	4.19 fix saml account selector (#10311)
This commit is contained in:
		
							parent
							
								
									99ea77dc83
								
							
						
					
					
						commit
						f13cf597a2
					
				@ -133,10 +133,12 @@ public class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthentic
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (userUuid != null && domainUuid != null) {
 | 
					        if (userUuid != null && domainUuid != null) {
 | 
				
			||||||
 | 
					            s_logger.debug("User [" + currentUserAccount.getUsername() + "] is requesting to switch from user profile [" + currentUserAccount.getId() + "] to useraccount [" + userUuid + "] in domain [" + domainUuid + "]");
 | 
				
			||||||
            final User user = _userDao.findByUuid(userUuid);
 | 
					            final User user = _userDao.findByUuid(userUuid);
 | 
				
			||||||
            final Domain domain = _domainDao.findByUuid(domainUuid);
 | 
					            final Domain domain = _domainDao.findByUuid(domainUuid);
 | 
				
			||||||
            final UserAccount nextUserAccount = _accountService.getUserAccountById(user.getId());
 | 
					            final UserAccount nextUserAccount = _accountService.getUserAccountById(user.getId());
 | 
				
			||||||
            if (nextUserAccount != null && !nextUserAccount.getAccountState().equals(Account.State.ENABLED.toString())) {
 | 
					            if (nextUserAccount != null && !nextUserAccount.getAccountState().equals(Account.State.ENABLED.toString())) {
 | 
				
			||||||
 | 
					                s_logger.warn("User [" + currentUserAccount.getUsername() + "] is requesting to switch from user profile [" + currentUserId + "] to user profile [" + userUuid + "] in domain [" + domainUuid + "] but the associated target account [" + nextUserAccount.getAccountName() + "] is not enabled");
 | 
				
			||||||
                throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(),
 | 
					                throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(),
 | 
				
			||||||
                        "The requested user account is locked and cannot be switched to, please contact your administrator.",
 | 
					                        "The requested user account is locked and cannot be switched to, please contact your administrator.",
 | 
				
			||||||
                        params, responseType));
 | 
					                        params, responseType));
 | 
				
			||||||
@ -147,20 +149,26 @@ public class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthentic
 | 
				
			|||||||
                    || !nextUserAccount.getExternalEntity().equals(currentUserAccount.getExternalEntity())
 | 
					                    || !nextUserAccount.getExternalEntity().equals(currentUserAccount.getExternalEntity())
 | 
				
			||||||
                    || (nextUserAccount.getDomainId() != domain.getId())
 | 
					                    || (nextUserAccount.getDomainId() != domain.getId())
 | 
				
			||||||
                    || (nextUserAccount.getSource() != User.Source.SAML2)) {
 | 
					                    || (nextUserAccount.getSource() != User.Source.SAML2)) {
 | 
				
			||||||
 | 
					                s_logger.warn("User [" + currentUserAccount.getUsername() + "] is requesting to switch from user profile [" + currentUserId + "] to user profile [" + userUuid + "] in domain [" + domainUuid + "] but the associated target account is not found or invalid");
 | 
				
			||||||
                throw new ServerApiException(ApiErrorCode.PARAM_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(),
 | 
					                throw new ServerApiException(ApiErrorCode.PARAM_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(),
 | 
				
			||||||
                        "User account is not allowed to switch to the requested account",
 | 
					                        "User account is not allowed to switch to the requested account",
 | 
				
			||||||
                        params, responseType));
 | 
					                        params, responseType));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                if (_apiServer.verifyUser(nextUserAccount.getId())) {
 | 
					                if (_apiServer.verifyUser(nextUserAccount.getId())) {
 | 
				
			||||||
 | 
					                    s_logger.info("User [" + currentUserAccount.getUsername() + "] user profile switch is accepted: from [" + currentUserId + "] to user profile [" + userUuid + "] in domain [" + domainUuid + "] with account [" + nextUserAccount.getAccountName() + "]");
 | 
				
			||||||
 | 
					                    // need to set a sessoin variable to inform the login function of the specific user to login as, rather than using email only (which could have multiple matches)
 | 
				
			||||||
 | 
					                    session.setAttribute("nextUserId", user.getId());
 | 
				
			||||||
                    final LoginCmdResponse loginResponse = (LoginCmdResponse) _apiServer.loginUser(session, nextUserAccount.getUsername(), nextUserAccount.getUsername() + nextUserAccount.getSource().toString(),
 | 
					                    final LoginCmdResponse loginResponse = (LoginCmdResponse) _apiServer.loginUser(session, nextUserAccount.getUsername(), nextUserAccount.getUsername() + nextUserAccount.getSource().toString(),
 | 
				
			||||||
                            nextUserAccount.getDomainId(), null, remoteAddress, params);
 | 
					                            nextUserAccount.getDomainId(), null, remoteAddress, params);
 | 
				
			||||||
                    SAMLUtils.setupSamlUserCookies(loginResponse, resp);
 | 
					                    SAMLUtils.setupSamlUserCookies(loginResponse, resp);
 | 
				
			||||||
                    resp.sendRedirect(SAML2AuthManager.SAMLCloudStackRedirectionUrl.value());
 | 
					                    session.removeAttribute("nextUserId");
 | 
				
			||||||
 | 
					                    s_logger.debug("User [" + currentUserAccount.getUsername() + "] user profile switch cookies set: from [" + currentUserId + "] to user profile [" + userUuid + "] in domain [" + domainUuid + "] with account [" + nextUserAccount.getAccountName() + "]");
 | 
				
			||||||
 | 
					                    //resp.sendRedirect(SAML2AuthManager.SAMLCloudStackRedirectionUrl.value());
 | 
				
			||||||
                    return ApiResponseSerializer.toSerializedString(loginResponse, responseType);
 | 
					                    return ApiResponseSerializer.toSerializedString(loginResponse, responseType);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } catch (CloudAuthenticationException | IOException exception) {
 | 
					            } catch (CloudAuthenticationException | IOException exception) {
 | 
				
			||||||
                s_logger.debug("Failed to switch to request SAML user account due to: " + exception.getMessage());
 | 
					                s_logger.debug("User [" + currentUserAccount.getUsername() + "] user profile switch cookies set FAILED: from [" + currentUserId + "] to user profile [" + userUuid + "] in domain [" + domainUuid + "] with account [" + nextUserAccount.getAccountName() + "]", exception);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            List<UserAccountVO> switchableAccounts = _userAccountDao.getAllUsersByNameAndEntity(currentUserAccount.getUsername(), currentUserAccount.getExternalEntity());
 | 
					            List<UserAccountVO> switchableAccounts = _userAccountDao.getAllUsersByNameAndEntity(currentUserAccount.getUsername(), currentUserAccount.getExternalEntity());
 | 
				
			||||||
@ -178,6 +186,9 @@ public class ListAndSwitchSAMLAccountCmd extends BaseCmd implements APIAuthentic
 | 
				
			|||||||
                    accountResponse.setAccountName(userAccount.getAccountName());
 | 
					                    accountResponse.setAccountName(userAccount.getAccountName());
 | 
				
			||||||
                    accountResponse.setIdpId(user.getExternalEntity());
 | 
					                    accountResponse.setIdpId(user.getExternalEntity());
 | 
				
			||||||
                    accountResponses.add(accountResponse);
 | 
					                    accountResponses.add(accountResponse);
 | 
				
			||||||
 | 
					                    if (s_logger.isDebugEnabled()) {
 | 
				
			||||||
 | 
					                        s_logger.debug("Returning available useraccount for [" + currentUserAccount.getUsername() + "]: UserUUID: [" + user.getUuid() + "], DomainUUID: [" + domain.getUuid() + "], Account: [" + userAccount.getAccountName() + "]");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                ListResponse<SamlUserAccountResponse> response = new ListResponse<SamlUserAccountResponse>();
 | 
					                ListResponse<SamlUserAccountResponse> response = new ListResponse<SamlUserAccountResponse>();
 | 
				
			||||||
                response.setResponses(accountResponses);
 | 
					                response.setResponses(accountResponses);
 | 
				
			||||||
 | 
				
			|||||||
@ -192,7 +192,7 @@ public class SAML2LoginAPIAuthenticatorCmd extends BaseCmd implements APIAuthent
 | 
				
			|||||||
                String authnId = SAMLUtils.generateSecureRandomId();
 | 
					                String authnId = SAMLUtils.generateSecureRandomId();
 | 
				
			||||||
                samlAuthManager.saveToken(authnId, domainPath, idpMetadata.getEntityId());
 | 
					                samlAuthManager.saveToken(authnId, domainPath, idpMetadata.getEntityId());
 | 
				
			||||||
                s_logger.debug("Sending SAMLRequest id=" + authnId);
 | 
					                s_logger.debug("Sending SAMLRequest id=" + authnId);
 | 
				
			||||||
                String redirectUrl = SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value());
 | 
					                String redirectUrl = SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value(), SAML2AuthManager.SAMLRequirePasswordLogin.value());
 | 
				
			||||||
                resp.sendRedirect(redirectUrl);
 | 
					                resp.sendRedirect(redirectUrl);
 | 
				
			||||||
                return "";
 | 
					                return "";
 | 
				
			||||||
            } if (params.containsKey("SAMLart")) {
 | 
					            } if (params.containsKey("SAMLart")) {
 | 
				
			||||||
 | 
				
			|||||||
@ -79,6 +79,10 @@ public interface SAML2AuthManager extends PluggableAPIAuthenticator, PluggableSe
 | 
				
			|||||||
    ConfigKey<String> SAMLUserSessionKeyPathAttribute = new ConfigKey<String>("Advanced", String.class, "saml2.user.sessionkey.path", "",
 | 
					    ConfigKey<String> SAMLUserSessionKeyPathAttribute = new ConfigKey<String>("Advanced", String.class, "saml2.user.sessionkey.path", "",
 | 
				
			||||||
            "The Path attribute of sessionkey cookie when SAML users have logged in. If not set, it will be set to the path of SAML redirection URL (saml2.redirect.url).", true);
 | 
					            "The Path attribute of sessionkey cookie when SAML users have logged in. If not set, it will be set to the path of SAML redirection URL (saml2.redirect.url).", true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ConfigKey<Boolean> SAMLRequirePasswordLogin = new ConfigKey<Boolean>("Advanced", Boolean.class, "saml2.require.password", "true",
 | 
				
			||||||
 | 
					    "When enabled SAML2 will validate that the SAML login was performed with a password.  If disabled, other forms of authentication are allowed (two-factor, certificate, etc) on the SAML Authentication Provider", true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SAMLProviderMetadata getSPMetadata();
 | 
					    SAMLProviderMetadata getSPMetadata();
 | 
				
			||||||
    SAMLProviderMetadata getIdPMetadata(String entityId);
 | 
					    SAMLProviderMetadata getIdPMetadata(String entityId);
 | 
				
			||||||
    Collection<SAMLProviderMetadata> getAllIdPMetadata();
 | 
					    Collection<SAMLProviderMetadata> getAllIdPMetadata();
 | 
				
			||||||
 | 
				
			|||||||
@ -543,6 +543,6 @@ public class SAML2AuthManagerImpl extends AdapterBase implements SAML2AuthManage
 | 
				
			|||||||
                SAMLCloudStackRedirectionUrl, SAMLUserAttributeName,
 | 
					                SAMLCloudStackRedirectionUrl, SAMLUserAttributeName,
 | 
				
			||||||
                SAMLIdentityProviderMetadataURL, SAMLDefaultIdentityProviderId,
 | 
					                SAMLIdentityProviderMetadataURL, SAMLDefaultIdentityProviderId,
 | 
				
			||||||
                SAMLSignatureAlgorithm, SAMLAppendDomainSuffix, SAMLTimeout, SAMLCheckSignature,
 | 
					                SAMLSignatureAlgorithm, SAMLAppendDomainSuffix, SAMLTimeout, SAMLCheckSignature,
 | 
				
			||||||
                SAMLForceAuthn, SAMLUserSessionKeyPathAttribute};
 | 
					                SAMLForceAuthn, SAMLUserSessionKeyPathAttribute, SAMLRequirePasswordLogin};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -151,11 +151,11 @@ public class SAMLUtils {
 | 
				
			|||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static String buildAuthnRequestUrl(final String authnId, final SAMLProviderMetadata spMetadata, final SAMLProviderMetadata idpMetadata, final String signatureAlgorithm) {
 | 
					    public static String buildAuthnRequestUrl(final String authnId, final SAMLProviderMetadata spMetadata, final SAMLProviderMetadata idpMetadata, final String signatureAlgorithm, boolean requirePasswordAuthentication) {
 | 
				
			||||||
        String redirectUrl = "";
 | 
					        String redirectUrl = "";
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            DefaultBootstrap.bootstrap();
 | 
					            DefaultBootstrap.bootstrap();
 | 
				
			||||||
            AuthnRequest authnRequest = SAMLUtils.buildAuthnRequestObject(authnId, spMetadata.getEntityId(), idpMetadata.getSsoUrl(), spMetadata.getSsoUrl());
 | 
					            AuthnRequest authnRequest = SAMLUtils.buildAuthnRequestObject(authnId, spMetadata.getEntityId(), idpMetadata.getSsoUrl(), spMetadata.getSsoUrl(), requirePasswordAuthentication);
 | 
				
			||||||
            PrivateKey privateKey = null;
 | 
					            PrivateKey privateKey = null;
 | 
				
			||||||
            if (spMetadata.getKeyPair() != null) {
 | 
					            if (spMetadata.getKeyPair() != null) {
 | 
				
			||||||
                privateKey = spMetadata.getKeyPair().getPrivate();
 | 
					                privateKey = spMetadata.getKeyPair().getPrivate();
 | 
				
			||||||
@ -168,13 +168,36 @@ public class SAMLUtils {
 | 
				
			|||||||
        return redirectUrl;
 | 
					        return redirectUrl;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static AuthnRequest buildAuthnRequestObject(final String authnId, final String spId, final String idpUrl, final String consumerUrl) {
 | 
					    public static AuthnRequest buildAuthnRequestObject(final String authnId, final String spId, final String idpUrl, final String consumerUrl, boolean requirePasswordAuthentication) {
 | 
				
			||||||
        // Issuer object
 | 
					        // Issuer object
 | 
				
			||||||
        IssuerBuilder issuerBuilder = new IssuerBuilder();
 | 
					        IssuerBuilder issuerBuilder = new IssuerBuilder();
 | 
				
			||||||
        Issuer issuer = issuerBuilder.buildObject();
 | 
					        Issuer issuer = issuerBuilder.buildObject();
 | 
				
			||||||
        issuer.setValue(spId);
 | 
					        issuer.setValue(spId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // AuthnContextClass
 | 
					        // Creation of AuthRequestObject
 | 
				
			||||||
 | 
					        AuthnRequestBuilder authRequestBuilder = new AuthnRequestBuilder();
 | 
				
			||||||
 | 
					        AuthnRequest authnRequest = authRequestBuilder.buildObject();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // AuthnContextClass.  When this is false, the authentication requirements are defered to the SAML IDP and its default or configured workflow
 | 
				
			||||||
 | 
					        if (requirePasswordAuthentication) {
 | 
				
			||||||
 | 
					            setRequestedAuthnContext(authnRequest, requirePasswordAuthentication);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        authnRequest.setID(authnId);
 | 
				
			||||||
 | 
					        authnRequest.setDestination(idpUrl);
 | 
				
			||||||
 | 
					        authnRequest.setVersion(SAMLVersion.VERSION_20);
 | 
				
			||||||
 | 
					        authnRequest.setForceAuthn(SAML2AuthManager.SAMLForceAuthn.value());
 | 
				
			||||||
 | 
					        authnRequest.setIsPassive(false);
 | 
				
			||||||
 | 
					        authnRequest.setIssueInstant(new DateTime());
 | 
				
			||||||
 | 
					        authnRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
 | 
				
			||||||
 | 
					        authnRequest.setAssertionConsumerServiceURL(consumerUrl);
 | 
				
			||||||
 | 
					        authnRequest.setProviderName(spId);
 | 
				
			||||||
 | 
					        authnRequest.setIssuer(issuer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return authnRequest;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void setRequestedAuthnContext(AuthnRequest authnRequest, boolean requirePasswordAuthentication) {
 | 
				
			||||||
        AuthnContextClassRefBuilder authnContextClassRefBuilder = new AuthnContextClassRefBuilder();
 | 
					        AuthnContextClassRefBuilder authnContextClassRefBuilder = new AuthnContextClassRefBuilder();
 | 
				
			||||||
        AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject(
 | 
					        AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject(
 | 
				
			||||||
                SAMLConstants.SAML20_NS,
 | 
					                SAMLConstants.SAML20_NS,
 | 
				
			||||||
@ -186,23 +209,7 @@ public class SAMLUtils {
 | 
				
			|||||||
        RequestedAuthnContext requestedAuthnContext = requestedAuthnContextBuilder.buildObject();
 | 
					        RequestedAuthnContext requestedAuthnContext = requestedAuthnContextBuilder.buildObject();
 | 
				
			||||||
        requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT);
 | 
					        requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT);
 | 
				
			||||||
        requestedAuthnContext.getAuthnContextClassRefs().add(authnContextClassRef);
 | 
					        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(SAML2AuthManager.SAMLForceAuthn.value());
 | 
					 | 
				
			||||||
        authnRequest.setIsPassive(false);
 | 
					 | 
				
			||||||
        authnRequest.setIssueInstant(new DateTime());
 | 
					 | 
				
			||||||
        authnRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
 | 
					 | 
				
			||||||
        authnRequest.setAssertionConsumerServiceURL(consumerUrl);
 | 
					 | 
				
			||||||
        authnRequest.setProviderName(spId);
 | 
					 | 
				
			||||||
        authnRequest.setIssuer(issuer);
 | 
					 | 
				
			||||||
        authnRequest.setRequestedAuthnContext(requestedAuthnContext);
 | 
					        authnRequest.setRequestedAuthnContext(requestedAuthnContext);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        return authnRequest;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static LogoutRequest buildLogoutRequest(String logoutUrl, String spId, String nameIdString) {
 | 
					    public static LogoutRequest buildLogoutRequest(String logoutUrl, String spId, String nameIdString) {
 | 
				
			||||||
@ -284,23 +291,6 @@ public class SAMLUtils {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static void setupSamlUserCookies(final LoginCmdResponse loginResponse, final HttpServletResponse resp) throws IOException {
 | 
					    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)));
 | 
					 | 
				
			||||||
        resp.addCookie(new Cookie("isSAML", URLEncoder.encode("true", HttpUtils.UTF_8)));
 | 
					 | 
				
			||||||
        resp.addCookie(new Cookie("twoFaEnabled", URLEncoder.encode(loginResponse.is2FAenabled(), HttpUtils.UTF_8)));
 | 
					 | 
				
			||||||
        String providerFor2FA = loginResponse.getProviderFor2FA();
 | 
					 | 
				
			||||||
        if (StringUtils.isNotEmpty(providerFor2FA)) {
 | 
					 | 
				
			||||||
            resp.addCookie(new Cookie("twoFaProvider", URLEncoder.encode(loginResponse.getProviderFor2FA(), 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")));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        String redirectUrl = SAML2AuthManager.SAMLCloudStackRedirectionUrl.value();
 | 
					        String redirectUrl = SAML2AuthManager.SAMLCloudStackRedirectionUrl.value();
 | 
				
			||||||
        String path = SAML2AuthManager.SAMLUserSessionKeyPathAttribute.value();
 | 
					        String path = SAML2AuthManager.SAMLUserSessionKeyPathAttribute.value();
 | 
				
			||||||
        String domain = null;
 | 
					        String domain = null;
 | 
				
			||||||
@ -316,6 +306,18 @@ public class SAMLUtils {
 | 
				
			|||||||
        } catch (URISyntaxException ex) {
 | 
					        } catch (URISyntaxException ex) {
 | 
				
			||||||
            throw new CloudRuntimeException("Invalid URI: " + redirectUrl);
 | 
					            throw new CloudRuntimeException("Invalid URI: " + redirectUrl);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        addBaseCookies(loginResponse, resp, domain, path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String providerFor2FA = loginResponse.getProviderFor2FA();
 | 
				
			||||||
 | 
					        if (StringUtils.isNotEmpty(providerFor2FA)) {
 | 
				
			||||||
 | 
					            resp.addCookie(newCookie(domain, path,"twoFaProvider", URLEncoder.encode(loginResponse.getProviderFor2FA(), HttpUtils.UTF_8)));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        String timezone = loginResponse.getTimeZone();
 | 
				
			||||||
 | 
					        if (timezone != null) {
 | 
				
			||||||
 | 
					            resp.addCookie(newCookie(domain, path,"timezone", URLEncoder.encode(timezone, HttpUtils.UTF_8)));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        String sameSite = ApiServlet.getApiSessionKeySameSite();
 | 
					        String sameSite = ApiServlet.getApiSessionKeySameSite();
 | 
				
			||||||
        String sessionKeyCookie = String.format("%s=%s;Domain=%s;Path=%s;%s", ApiConstants.SESSIONKEY, loginResponse.getSessionKey(), domain, path, sameSite);
 | 
					        String sessionKeyCookie = String.format("%s=%s;Domain=%s;Path=%s;%s", ApiConstants.SESSIONKEY, loginResponse.getSessionKey(), domain, path, sameSite);
 | 
				
			||||||
        s_logger.debug("Adding sessionkey cookie to response: " + sessionKeyCookie);
 | 
					        s_logger.debug("Adding sessionkey cookie to response: " + sessionKeyCookie);
 | 
				
			||||||
@ -323,6 +325,24 @@ public class SAMLUtils {
 | 
				
			|||||||
        resp.addHeader("SET-COOKIE", String.format("%s=%s;HttpOnly;Path=/client/api;%s", ApiConstants.SESSIONKEY, loginResponse.getSessionKey(), sameSite));
 | 
					        resp.addHeader("SET-COOKIE", String.format("%s=%s;HttpOnly;Path=/client/api;%s", ApiConstants.SESSIONKEY, loginResponse.getSessionKey(), sameSite));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static void addBaseCookies(final LoginCmdResponse loginResponse, final HttpServletResponse resp, String domain, String path) throws IOException {
 | 
				
			||||||
 | 
					        resp.addCookie(newCookie(domain, path, "userid", URLEncoder.encode(loginResponse.getUserId(), HttpUtils.UTF_8)));
 | 
				
			||||||
 | 
					        resp.addCookie(newCookie(domain, path,"domainid", URLEncoder.encode(loginResponse.getDomainId(), HttpUtils.UTF_8)));
 | 
				
			||||||
 | 
					        resp.addCookie(newCookie(domain, path,"role", URLEncoder.encode(loginResponse.getType(), HttpUtils.UTF_8)));
 | 
				
			||||||
 | 
					        resp.addCookie(newCookie(domain, path,"username", URLEncoder.encode(loginResponse.getUsername(), HttpUtils.UTF_8)));
 | 
				
			||||||
 | 
					        resp.addCookie(newCookie(domain, path,"account", URLEncoder.encode(loginResponse.getAccount(), HttpUtils.UTF_8)));
 | 
				
			||||||
 | 
					        resp.addCookie(newCookie(domain, path,"isSAML", URLEncoder.encode("true", HttpUtils.UTF_8)));
 | 
				
			||||||
 | 
					        resp.addCookie(newCookie(domain, path,"twoFaEnabled", URLEncoder.encode(loginResponse.is2FAenabled(), HttpUtils.UTF_8)));
 | 
				
			||||||
 | 
					        resp.addCookie(newCookie(domain, path,"userfullname", URLEncoder.encode(loginResponse.getFirstName() + " " + loginResponse.getLastName(), HttpUtils.UTF_8).replace("+", "%20")));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static Cookie newCookie(final String domain, final String path, final String name, final String value) {
 | 
				
			||||||
 | 
					        Cookie cookie = new Cookie(name, value);
 | 
				
			||||||
 | 
					        cookie.setDomain(domain);
 | 
				
			||||||
 | 
					        cookie.setPath(path);
 | 
				
			||||||
 | 
					        return cookie;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Returns base64 encoded PublicKey
 | 
					     * Returns base64 encoded PublicKey
 | 
				
			||||||
     * @param key PublicKey
 | 
					     * @param key PublicKey
 | 
				
			||||||
 | 
				
			|||||||
@ -58,7 +58,7 @@ public class SAMLUtilsTest extends TestCase {
 | 
				
			|||||||
        String idpUrl = "http://idp.domain.example";
 | 
					        String idpUrl = "http://idp.domain.example";
 | 
				
			||||||
        String spId = "cloudstack";
 | 
					        String spId = "cloudstack";
 | 
				
			||||||
        String authnId = SAMLUtils.generateSecureRandomId();
 | 
					        String authnId = SAMLUtils.generateSecureRandomId();
 | 
				
			||||||
        AuthnRequest req = SAMLUtils.buildAuthnRequestObject(authnId, spId, idpUrl, consumerUrl);
 | 
					        AuthnRequest req = SAMLUtils.buildAuthnRequestObject(authnId, spId, idpUrl, consumerUrl, true);
 | 
				
			||||||
        assertEquals(req.getAssertionConsumerServiceURL(), consumerUrl);
 | 
					        assertEquals(req.getAssertionConsumerServiceURL(), consumerUrl);
 | 
				
			||||||
        assertEquals(req.getDestination(), idpUrl);
 | 
					        assertEquals(req.getDestination(), idpUrl);
 | 
				
			||||||
        assertEquals(req.getIssuer().getValue(), spId);
 | 
					        assertEquals(req.getIssuer().getValue(), spId);
 | 
				
			||||||
@ -86,7 +86,7 @@ public class SAMLUtilsTest extends TestCase {
 | 
				
			|||||||
        idpMetadata.setSsoUrl(idpUrl);
 | 
					        idpMetadata.setSsoUrl(idpUrl);
 | 
				
			||||||
        idpMetadata.setEntityId(idpId);
 | 
					        idpMetadata.setEntityId(idpId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value()));
 | 
					        URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value(), true));
 | 
				
			||||||
        assertThat(redirectUrl).hasScheme(urlScheme).hasHost(idpDomain).hasParameter("SAMLRequest");
 | 
					        assertThat(redirectUrl).hasScheme(urlScheme).hasHost(idpDomain).hasParameter("SAMLRequest");
 | 
				
			||||||
        assertEquals(urlScheme, redirectUrl.getScheme());
 | 
					        assertEquals(urlScheme, redirectUrl.getScheme());
 | 
				
			||||||
        assertEquals(idpDomain, redirectUrl.getHost());
 | 
					        assertEquals(idpDomain, redirectUrl.getHost());
 | 
				
			||||||
@ -115,7 +115,7 @@ public class SAMLUtilsTest extends TestCase {
 | 
				
			|||||||
        idpMetadata.setSsoUrl(idpUrl);
 | 
					        idpMetadata.setSsoUrl(idpUrl);
 | 
				
			||||||
        idpMetadata.setEntityId(idpId);
 | 
					        idpMetadata.setEntityId(idpId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value()));
 | 
					        URI redirectUrl = new URI(SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value(), true));
 | 
				
			||||||
        assertThat(redirectUrl).hasScheme(urlScheme).hasHost(idpDomain).hasParameter("idpid").hasParameter("SAMLRequest");
 | 
					        assertThat(redirectUrl).hasScheme(urlScheme).hasHost(idpDomain).hasParameter("idpid").hasParameter("SAMLRequest");
 | 
				
			||||||
        assertEquals(urlScheme, redirectUrl.getScheme());
 | 
					        assertEquals(urlScheme, redirectUrl.getScheme());
 | 
				
			||||||
        assertEquals(idpDomain, redirectUrl.getHost());
 | 
					        assertEquals(idpDomain, redirectUrl.getHost());
 | 
				
			||||||
 | 
				
			|||||||
@ -213,7 +213,6 @@ public class ListAndSwitchSAMLAccountCmdTest extends TestCase {
 | 
				
			|||||||
        loginCmdResponse.set2FAenabled("false");
 | 
					        loginCmdResponse.set2FAenabled("false");
 | 
				
			||||||
        Mockito.when(apiServer.loginUser(nullable(HttpSession.class), nullable(String.class), nullable(String.class),
 | 
					        Mockito.when(apiServer.loginUser(nullable(HttpSession.class), nullable(String.class), nullable(String.class),
 | 
				
			||||||
                nullable(Long.class), nullable(String.class), nullable(InetAddress.class), nullable(Map.class))).thenReturn(loginCmdResponse);
 | 
					                nullable(Long.class), nullable(String.class), nullable(InetAddress.class), nullable(Map.class))).thenReturn(loginCmdResponse);
 | 
				
			||||||
        Mockito.doNothing().when(resp).sendRedirect(nullable(String.class));
 | 
					 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            cmd.authenticate("command", params, session, null, HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), req, resp);
 | 
					            cmd.authenticate("command", params, session, null, HttpUtils.RESPONSE_TYPE_JSON, new StringBuilder(), req, resp);
 | 
				
			||||||
        } catch (ServerApiException exception) {
 | 
					        } catch (ServerApiException exception) {
 | 
				
			||||||
@ -221,7 +220,6 @@ public class ListAndSwitchSAMLAccountCmdTest extends TestCase {
 | 
				
			|||||||
        } finally {
 | 
					        } finally {
 | 
				
			||||||
            // accountService should have been called 4 times by now, for this case twice and 2 for cases above
 | 
					            // accountService should have been called 4 times by now, for this case twice and 2 for cases above
 | 
				
			||||||
            Mockito.verify(accountService, Mockito.times(4)).getUserAccountById(Mockito.anyLong());
 | 
					            Mockito.verify(accountService, Mockito.times(4)).getUserAccountById(Mockito.anyLong());
 | 
				
			||||||
            Mockito.verify(resp, Mockito.times(1)).sendRedirect(anyString());
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1159,7 +1159,14 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
 | 
				
			|||||||
            domainId = userDomain.getId();
 | 
					            domainId = userDomain.getId();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        UserAccount userAcct = accountMgr.authenticateUser(username, password, domainId, loginIpAddress, requestParameters);
 | 
					        Long userId = (Long)session.getAttribute("nextUserId");
 | 
				
			||||||
 | 
					        UserAccount userAcct = null;
 | 
				
			||||||
 | 
					        if (userId != null) {
 | 
				
			||||||
 | 
					            userAcct = accountMgr.getUserAccountById(userId);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            userAcct = accountMgr.authenticateUser(username, password, domainId, loginIpAddress, requestParameters);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (userAcct != null) {
 | 
					        if (userAcct != null) {
 | 
				
			||||||
            final String timezone = userAcct.getTimezone();
 | 
					            final String timezone = userAcct.getTimezone();
 | 
				
			||||||
            float offsetInHrs = 0f;
 | 
					            float offsetInHrs = 0f;
 | 
				
			||||||
 | 
				
			|||||||
@ -372,6 +372,14 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
 | 
				
			|||||||
            "totp",
 | 
					            "totp",
 | 
				
			||||||
            "The default user two factor authentication provider. Eg. totp, staticpin", true, ConfigKey.Scope.Domain);
 | 
					            "The default user two factor authentication provider. Eg. totp, staticpin", true, ConfigKey.Scope.Domain);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static ConfigKey<Boolean> userAllowMultipleAccounts = new ConfigKey<>("Advanced",
 | 
				
			||||||
 | 
					            Boolean.class,
 | 
				
			||||||
 | 
					            "user.allow.multiple.accounts",
 | 
				
			||||||
 | 
					            "false",
 | 
				
			||||||
 | 
					            "Determines if the same username can be added to more than one account in the same domain (SAML-only).",
 | 
				
			||||||
 | 
					            true,
 | 
				
			||||||
 | 
					            ConfigKey.Scope.Domain);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected AccountManagerImpl() {
 | 
					    protected AccountManagerImpl() {
 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -1252,8 +1260,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
 | 
				
			|||||||
        // Check permissions
 | 
					        // Check permissions
 | 
				
			||||||
        checkAccess(getCurrentCallingAccount(), domain);
 | 
					        checkAccess(getCurrentCallingAccount(), domain);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!_userAccountDao.validateUsernameInDomain(userName, domainId)) {
 | 
					        if (!userAllowMultipleAccounts.valueInDomain(domainId) && !_userAccountDao.validateUsernameInDomain(userName, domainId)) {
 | 
				
			||||||
            throw new InvalidParameterValueException("The user " + userName + " already exists in domain " + domainId);
 | 
					            throw new CloudRuntimeException("The user " + userName + " already exists in domain " + domainId);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (networkDomain != null && networkDomain.length() > 0) {
 | 
					        if (networkDomain != null && networkDomain.length() > 0) {
 | 
				
			||||||
@ -1436,9 +1444,16 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
 | 
				
			|||||||
            throw new PermissionDeniedException("Account id : " + account.getId() + " is a system account, can't add a user to it");
 | 
					            throw new PermissionDeniedException("Account id : " + account.getId() + " is a system account, can't add a user to it");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!_userAccountDao.validateUsernameInDomain(userName, domainId)) {
 | 
					        if (!userAllowMultipleAccounts.valueInDomain(domainId) && !_userAccountDao.validateUsernameInDomain(userName, domainId)) {
 | 
				
			||||||
            throw new CloudRuntimeException("The user " + userName + " already exists in domain " + domainId);
 | 
					            throw new CloudRuntimeException("The user " + userName + " already exists in domain " + domainId);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        List<UserVO> duplicatedUsers = _userDao.findUsersByName(userName);
 | 
				
			||||||
 | 
					        for (UserVO duplicatedUser : duplicatedUsers) {
 | 
				
			||||||
 | 
					            // users can't exist in same account
 | 
				
			||||||
 | 
					            assertUserNotAlreadyInAccount(duplicatedUser, account);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        UserVO user = null;
 | 
					        UserVO user = null;
 | 
				
			||||||
        user = createUser(account.getId(), userName, password, firstName, lastName, email, timeZone, userUUID, source);
 | 
					        user = createUser(account.getId(), userName, password, firstName, lastName, email, timeZone, userUUID, source);
 | 
				
			||||||
        return user;
 | 
					        return user;
 | 
				
			||||||
@ -1564,7 +1579,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
 | 
				
			|||||||
     *  <li> The username must be unique in each domain. Therefore, if there is already another user with the same username, an {@link InvalidParameterValueException} is thrown.
 | 
					     *  <li> The username must be unique in each domain. Therefore, if there is already another user with the same username, an {@link InvalidParameterValueException} is thrown.
 | 
				
			||||||
     * </ul>
 | 
					     * </ul>
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected void validateAndUpdateUsernameIfNeeded(UpdateUserCmd updateUserCmd, UserVO user, Account account) {
 | 
					    protected void validateAndUpdateUsernameIfNeeded(UpdateUserCmd updateUserCmd, UserVO newUser, Account newAccount) {
 | 
				
			||||||
        String userName = updateUserCmd.getUsername();
 | 
					        String userName = updateUserCmd.getUsername();
 | 
				
			||||||
        if (userName == null) {
 | 
					        if (userName == null) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
@ -1572,18 +1587,21 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
 | 
				
			|||||||
        if (StringUtils.isBlank(userName)) {
 | 
					        if (StringUtils.isBlank(userName)) {
 | 
				
			||||||
            throw new InvalidParameterValueException("Username cannot be empty.");
 | 
					            throw new InvalidParameterValueException("Username cannot be empty.");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        List<UserVO> duplicatedUsers = _userDao.findUsersByName(userName);
 | 
					        List<UserVO> existingUsers = _userDao.findUsersByName(userName);
 | 
				
			||||||
        for (UserVO duplicatedUser : duplicatedUsers) {
 | 
					        for (UserVO existingUser : existingUsers) {
 | 
				
			||||||
            if (duplicatedUser.getId() == user.getId()) {
 | 
					            if (existingUser.getId() == newUser.getId()) {
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Account duplicatedUserAccountWithUserThatHasTheSameUserName = _accountDao.findById(duplicatedUser.getAccountId());
 | 
					
 | 
				
			||||||
            if (duplicatedUserAccountWithUserThatHasTheSameUserName.getDomainId() == account.getDomainId()) {
 | 
					            // duplicate usernames cannot exist in same domain unless explicitly configured
 | 
				
			||||||
                DomainVO domain = _domainDao.findById(duplicatedUserAccountWithUserThatHasTheSameUserName.getDomainId());
 | 
					            if (!userAllowMultipleAccounts.valueInDomain(newAccount.getDomainId())) {
 | 
				
			||||||
                throw new InvalidParameterValueException(String.format("Username [%s] already exists in domain [id=%s,name=%s]", duplicatedUser.getUsername(), domain.getUuid(), domain.getName()));
 | 
					                assertUserNotAlreadyInDomain(existingUser, newAccount);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // can't rename a username to an existing one in the same account
 | 
				
			||||||
 | 
					            assertUserNotAlreadyInAccount(existingUser, newAccount);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        user.setUsername(userName);
 | 
					        newUser.setUsername(userName);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@ -1820,7 +1838,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // make sure the account is enabled too
 | 
					        // make sure the account is enabled too
 | 
				
			||||||
        // if the user is either locked already or disabled already, don't change state...only lock currently enabled
 | 
					        // if the user is either locked already or disabled already, don't change state...only lock currently enabled
 | 
				
			||||||
// users
 | 
					        // users
 | 
				
			||||||
        boolean success = true;
 | 
					        boolean success = true;
 | 
				
			||||||
        if (user.getState().equals(State.LOCKED)) {
 | 
					        if (user.getState().equals(State.LOCKED)) {
 | 
				
			||||||
            // already locked...no-op
 | 
					            // already locked...no-op
 | 
				
			||||||
@ -3317,7 +3335,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
 | 
				
			|||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public ConfigKey<?>[] getConfigKeys() {
 | 
					    public ConfigKey<?>[] getConfigKeys() {
 | 
				
			||||||
        return new ConfigKey<?>[] {UseSecretKeyInResponse, enableUserTwoFactorAuthentication,
 | 
					        return new ConfigKey<?>[] {UseSecretKeyInResponse, enableUserTwoFactorAuthentication,
 | 
				
			||||||
                userTwoFactorAuthenticationDefaultProvider, mandateUserTwoFactorAuthentication, userTwoFactorAuthenticationIssuer};
 | 
					                userTwoFactorAuthenticationDefaultProvider, mandateUserTwoFactorAuthentication, userTwoFactorAuthenticationIssuer,
 | 
				
			||||||
 | 
					                userAllowMultipleAccounts};
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public List<UserTwoFactorAuthenticator> getUserTwoFactorAuthenticationProviders() {
 | 
					    public List<UserTwoFactorAuthenticator> getUserTwoFactorAuthenticationProviders() {
 | 
				
			||||||
@ -3502,4 +3521,21 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
 | 
				
			|||||||
            return userAccountVO;
 | 
					            return userAccountVO;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void assertUserNotAlreadyInAccount(User existingUser, Account newAccount) {
 | 
				
			||||||
 | 
					        System.out.println(existingUser.getAccountId());
 | 
				
			||||||
 | 
					        System.out.println(newAccount.getId());
 | 
				
			||||||
 | 
					        if (existingUser.getAccountId() == newAccount.getId()) {
 | 
				
			||||||
 | 
					            AccountVO existingAccount = _accountDao.findById(newAccount.getId());
 | 
				
			||||||
 | 
					            throw new InvalidParameterValueException(String.format("Username [%s] already exists in account [id=%s,name=%s]", existingUser.getUsername(), existingAccount.getUuid(), existingAccount.getAccountName()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void assertUserNotAlreadyInDomain(User existingUser, Account originalAccount) {
 | 
				
			||||||
 | 
					        Account existingAccount = _accountDao.findById(existingUser.getAccountId());
 | 
				
			||||||
 | 
					        if (existingAccount.getDomainId() == originalAccount.getDomainId()) {
 | 
				
			||||||
 | 
					            DomainVO existingDomain = _domainDao.findById(existingAccount.getDomainId());
 | 
				
			||||||
 | 
					            throw new InvalidParameterValueException(String.format("Username [%s] already exists in domain [id=%s,name=%s] user account [id=%s,name=%s]", existingUser.getUsername(), existingDomain.getUuid(), existingDomain.getName(), existingAccount.getUuid(), existingAccount.getAccountName()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1270,4 +1270,75 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
 | 
				
			|||||||
        Assert.assertNull(updatedUser.getUser2faProvider());
 | 
					        Assert.assertNull(updatedUser.getUser2faProvider());
 | 
				
			||||||
        Assert.assertNull(updatedUser.getKeyFor2fa());
 | 
					        Assert.assertNull(updatedUser.getKeyFor2fa());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = InvalidParameterValueException.class)
 | 
				
			||||||
 | 
					    public void testAssertUserNotAlreadyInAccount_UserExistsInAccount() {
 | 
				
			||||||
 | 
					        User existingUser = new UserVO();
 | 
				
			||||||
 | 
					        existingUser.setUsername("testuser");
 | 
				
			||||||
 | 
					        existingUser.setAccountId(1L);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Account newAccount = Mockito.mock(Account.class);
 | 
				
			||||||
 | 
					        Mockito.when(newAccount.getId()).thenReturn(1L);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        AccountVO existingAccount = Mockito.mock(AccountVO.class);
 | 
				
			||||||
 | 
					        Mockito.when(existingAccount.getUuid()).thenReturn("existing-account-uuid");
 | 
				
			||||||
 | 
					        Mockito.when(existingAccount.getAccountName()).thenReturn("existing-account");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Mockito.when(_accountDao.findById(1L)).thenReturn(existingAccount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        accountManagerImpl.assertUserNotAlreadyInAccount(existingUser, newAccount);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void testAssertUserNotAlreadyInAccount_UserExistsInDiffAccount() {
 | 
				
			||||||
 | 
					        User existingUser = new UserVO();
 | 
				
			||||||
 | 
					        existingUser.setUsername("testuser");
 | 
				
			||||||
 | 
					        existingUser.setAccountId(2L);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Account newAccount = Mockito.mock(Account.class);
 | 
				
			||||||
 | 
					        Mockito.when(newAccount.getId()).thenReturn(1L);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        accountManagerImpl.assertUserNotAlreadyInAccount(existingUser, newAccount);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test(expected = InvalidParameterValueException.class)
 | 
				
			||||||
 | 
					    public void testAssertUserNotAlreadyInDomain_UserExistsInDomain() {
 | 
				
			||||||
 | 
					        User existingUser = new UserVO();
 | 
				
			||||||
 | 
					        existingUser.setUsername("testuser");
 | 
				
			||||||
 | 
					        existingUser.setAccountId(1L);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Account originalAccount = Mockito.mock(Account.class);
 | 
				
			||||||
 | 
					        Mockito.when(originalAccount.getDomainId()).thenReturn(1L);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        AccountVO existingAccount = Mockito.mock(AccountVO.class);
 | 
				
			||||||
 | 
					        Mockito.when(existingAccount.getDomainId()).thenReturn(1L);
 | 
				
			||||||
 | 
					        Mockito.when(existingAccount.getUuid()).thenReturn("existing-account-uuid");
 | 
				
			||||||
 | 
					        Mockito.when(existingAccount.getAccountName()).thenReturn("existing-account");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        DomainVO existingDomain = Mockito.mock(DomainVO.class);
 | 
				
			||||||
 | 
					        Mockito.when(existingDomain.getUuid()).thenReturn("existing-domain-uuid");
 | 
				
			||||||
 | 
					        Mockito.when(existingDomain.getName()).thenReturn("existing-domain");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Mockito.when(_accountDao.findById(1L)).thenReturn(existingAccount);
 | 
				
			||||||
 | 
					        Mockito.when(_domainDao.findById(1L)).thenReturn(existingDomain);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        accountManagerImpl.assertUserNotAlreadyInDomain(existingUser, originalAccount);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void testAssertUserNotAlreadyInDomain_UserExistsInDiffDomain() {
 | 
				
			||||||
 | 
					        User existingUser = new UserVO();
 | 
				
			||||||
 | 
					        existingUser.setUsername("testuser");
 | 
				
			||||||
 | 
					        existingUser.setAccountId(1L);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Account originalAccount = Mockito.mock(Account.class);
 | 
				
			||||||
 | 
					        Mockito.when(originalAccount.getDomainId()).thenReturn(1L);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        AccountVO existingAccount = Mockito.mock(AccountVO.class);
 | 
				
			||||||
 | 
					        Mockito.when(existingAccount.getDomainId()).thenReturn(2L);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Mockito.when(_accountDao.findById(1L)).thenReturn(existingAccount);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        accountManagerImpl.assertUserNotAlreadyInDomain(existingUser, originalAccount);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -88,6 +88,7 @@ export default {
 | 
				
			|||||||
            this.showSwitcher = false
 | 
					            this.showSwitcher = false
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					          this.samlAccounts = samlAccounts
 | 
				
			||||||
          this.samlAccounts = _.orderBy(samlAccounts, ['domainPath'], ['asc'])
 | 
					          this.samlAccounts = _.orderBy(samlAccounts, ['domainPath'], ['asc'])
 | 
				
			||||||
          const currentAccount = this.samlAccounts.filter(x => {
 | 
					          const currentAccount = this.samlAccounts.filter(x => {
 | 
				
			||||||
            return x.userId === store.getters.userInfo.id
 | 
					            return x.userId === store.getters.userInfo.id
 | 
				
			||||||
@ -109,6 +110,8 @@ export default {
 | 
				
			|||||||
          this.$message.success(`Switched to "${account.accountName} (${account.domainPath})"`)
 | 
					          this.$message.success(`Switched to "${account.accountName} (${account.domainPath})"`)
 | 
				
			||||||
          this.$router.go()
 | 
					          this.$router.go()
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					      }).else(error => {
 | 
				
			||||||
 | 
					        console.log('error refreshing with new user context: ' + error)
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -290,7 +290,7 @@ const user = {
 | 
				
			|||||||
          commit('SET_CUSTOM_COLUMNS', cachedCustomColumns)
 | 
					          commit('SET_CUSTOM_COLUMNS', cachedCustomColumns)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          // Ensuring we get the user info so that store.getters.user is never empty when the page is freshly loaded
 | 
					          // Ensuring we get the user info so that store.getters.user is never empty when the page is freshly loaded
 | 
				
			||||||
          api('listUsers', { username: Cookies.get('username'), listall: true }).then(response => {
 | 
					          api('listUsers', { id: Cookies.get('userid'), listall: true }).then(response => {
 | 
				
			||||||
            const result = response.listusersresponse.user[0]
 | 
					            const result = response.listusersresponse.user[0]
 | 
				
			||||||
            commit('SET_INFO', result)
 | 
					            commit('SET_INFO', result)
 | 
				
			||||||
            commit('SET_NAME', result.firstname + ' ' + result.lastname)
 | 
					            commit('SET_NAME', result.firstname + ' ' + result.lastname)
 | 
				
			||||||
@ -331,7 +331,7 @@ const user = {
 | 
				
			|||||||
          })
 | 
					          })
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        api('listUsers', { username: Cookies.get('username') }).then(response => {
 | 
					        api('listUsers', { id: Cookies.get('userid') }).then(response => {
 | 
				
			||||||
          const result = response.listusersresponse.user[0]
 | 
					          const result = response.listusersresponse.user[0]
 | 
				
			||||||
          commit('SET_INFO', result)
 | 
					          commit('SET_INFO', result)
 | 
				
			||||||
          commit('SET_NAME', result.firstname + ' ' + result.lastname)
 | 
					          commit('SET_NAME', result.firstname + ' ' + result.lastname)
 | 
				
			||||||
 | 
				
			|||||||
@ -143,7 +143,7 @@ const vueConfig = {
 | 
				
			|||||||
        ws: false,
 | 
					        ws: false,
 | 
				
			||||||
        changeOrigin: true,
 | 
					        changeOrigin: true,
 | 
				
			||||||
        proxyTimeout: 10 * 60 * 1000, // 10 minutes
 | 
					        proxyTimeout: 10 * 60 * 1000, // 10 minutes
 | 
				
			||||||
        cookieDomainRewrite: '*',
 | 
					        cookieDomainRewrite: process.env.CS_COOKIE_HOST || 'localhost',
 | 
				
			||||||
        cookiePathRewrite: {
 | 
					        cookiePathRewrite: {
 | 
				
			||||||
          '/client': '/'
 | 
					          '/client': '/'
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user