Search in sources :

Example 1 with SignatureValidator

use of org.opensaml.xml.signature.SignatureValidator in project cloudstack by apache.

the class SAML2LoginAPIAuthenticatorCmd method authenticate.

@Override
public String authenticate(final String command, final Map<String, Object[]> params, final HttpSession session, final InetAddress remoteAddress, final String responseType, final StringBuilder auditTrailSb, final HttpServletRequest req, final HttpServletResponse resp) throws ServerApiException {
    try {
        if (!params.containsKey(SAMLPluginConstants.SAML_RESPONSE) && !params.containsKey("SAMLart")) {
            String idpId = null;
            String domainPath = null;
            if (params.containsKey(ApiConstants.IDP_ID)) {
                idpId = ((String[]) params.get(ApiConstants.IDP_ID))[0];
            }
            if (params.containsKey(ApiConstants.DOMAIN)) {
                domainPath = ((String[]) params.get(ApiConstants.DOMAIN))[0];
            }
            if (domainPath != null && !domainPath.isEmpty()) {
                if (!domainPath.startsWith("/")) {
                    domainPath = "/" + domainPath;
                }
                if (!domainPath.endsWith("/")) {
                    domainPath = domainPath + "/";
                }
            }
            SAMLProviderMetadata spMetadata = _samlAuthManager.getSPMetadata();
            SAMLProviderMetadata idpMetadata = _samlAuthManager.getIdPMetadata(idpId);
            if (idpMetadata == null) {
                throw new ServerApiException(ApiErrorCode.PARAM_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(), "IdP ID (" + idpId + ") is not found in our list of supported IdPs, cannot proceed.", params, responseType));
            }
            if (idpMetadata.getSsoUrl() == null || idpMetadata.getSsoUrl().isEmpty()) {
                throw new ServerApiException(ApiErrorCode.PARAM_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.PARAM_ERROR.getHttpCode(), "IdP ID (" + idpId + ") has no Single Sign On URL defined please contact " + idpMetadata.getContactPersonName() + " <" + idpMetadata.getContactPersonEmail() + ">, cannot proceed.", params, responseType));
            }
            String authnId = SAMLUtils.generateSecureRandomId();
            _samlAuthManager.saveToken(authnId, domainPath, idpMetadata.getEntityId());
            s_logger.debug("Sending SAMLRequest id=" + authnId);
            String redirectUrl = SAMLUtils.buildAuthnRequestUrl(authnId, spMetadata, idpMetadata, SAML2AuthManager.SAMLSignatureAlgorithm.value());
            resp.sendRedirect(redirectUrl);
            return "";
        }
        if (params.containsKey("SAMLart")) {
            throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.UNSUPPORTED_ACTION_ERROR.getHttpCode(), "SAML2 HTTP Artifact Binding is not supported", params, responseType));
        } else {
            final String samlResponse = ((String[]) params.get(SAMLPluginConstants.SAML_RESPONSE))[0];
            Response processedSAMLResponse = this.processSAMLResponse(samlResponse);
            String statusCode = processedSAMLResponse.getStatus().getStatusCode().getValue();
            if (!statusCode.equals(StatusCode.SUCCESS_URI)) {
                throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(), "Identity Provider send a non-successful authentication status code", params, responseType));
            }
            String username = null;
            Issuer issuer = processedSAMLResponse.getIssuer();
            SAMLProviderMetadata spMetadata = _samlAuthManager.getSPMetadata();
            SAMLProviderMetadata idpMetadata = _samlAuthManager.getIdPMetadata(issuer.getValue());
            String responseToId = processedSAMLResponse.getInResponseTo();
            s_logger.debug("Received SAMLResponse in response to id=" + responseToId);
            SAMLTokenVO token = _samlAuthManager.getToken(responseToId);
            if (token != null) {
                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", params, responseType));
                }
            } else {
                throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(), "Received SAML response for a SSO request that we may not have made or has expired, please try logging in again", params, responseType));
            }
            // Set IdpId for this session
            session.setAttribute(SAMLPluginConstants.SAML_IDPID, issuer.getValue());
            Signature sig = processedSAMLResponse.getSignature();
            if (idpMetadata.getSigningCertificate() != null && sig != null) {
                BasicX509Credential credential = new BasicX509Credential();
                credential.setEntityCertificate(idpMetadata.getSigningCertificate());
                SignatureValidator validator = new SignatureValidator(credential);
                try {
                    validator.validate(sig);
                } catch (ValidationException e) {
                    s_logger.error("SAML Response's signature failed to be validated by IDP signing key:" + e.getMessage());
                    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(), "SAML Response's signature failed to be validated by IDP signing key", params, responseType));
                }
            }
            if (username == null) {
                username = SAMLUtils.getValueFromAssertions(processedSAMLResponse.getAssertions(), SAML2AuthManager.SAMLUserAttributeName.value());
            }
            for (Assertion assertion : processedSAMLResponse.getAssertions()) {
                if (assertion != null && assertion.getSubject() != null && assertion.getSubject().getNameID() != null) {
                    session.setAttribute(SAMLPluginConstants.SAML_NAMEID, assertion.getSubject().getNameID().getValue());
                    break;
                }
            }
            if (idpMetadata.getEncryptionCertificate() != null && spMetadata != null && spMetadata.getKeyPair() != null && spMetadata.getKeyPair().getPrivate() != null) {
                Credential credential = SecurityHelper.getSimpleCredential(idpMetadata.getEncryptionCertificate().getPublicKey(), spMetadata.getKeyPair().getPrivate());
                StaticKeyInfoCredentialResolver keyInfoResolver = new StaticKeyInfoCredentialResolver(credential);
                EncryptedKeyResolver keyResolver = new InlineEncryptedKeyResolver();
                Decrypter decrypter = new Decrypter(null, keyInfoResolver, keyResolver);
                decrypter.setRootInNewDocument(true);
                List<EncryptedAssertion> encryptedAssertions = processedSAMLResponse.getEncryptedAssertions();
                if (encryptedAssertions != null) {
                    for (EncryptedAssertion encryptedAssertion : encryptedAssertions) {
                        Assertion assertion = null;
                        try {
                            assertion = decrypter.decrypt(encryptedAssertion);
                        } catch (DecryptionException e) {
                            s_logger.warn("SAML EncryptedAssertion error: " + e.toString());
                        }
                        if (assertion == null) {
                            continue;
                        }
                        Signature encSig = assertion.getSignature();
                        if (idpMetadata.getSigningCertificate() != null && encSig != null) {
                            BasicX509Credential sigCredential = new BasicX509Credential();
                            sigCredential.setEntityCertificate(idpMetadata.getSigningCertificate());
                            SignatureValidator validator = new SignatureValidator(sigCredential);
                            try {
                                validator.validate(encSig);
                            } catch (ValidationException e) {
                                s_logger.error("SAML Response's signature failed to be validated by IDP signing key:" + e.getMessage());
                                throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(), "SAML Response's signature failed to be validated by IDP signing key", params, responseType));
                            }
                        }
                        if (assertion.getSubject() != null && assertion.getSubject().getNameID() != null) {
                            session.setAttribute(SAMLPluginConstants.SAML_NAMEID, assertion.getSubject().getNameID().getValue());
                        }
                        if (username == null) {
                            username = SAMLUtils.getValueFromAttributeStatements(assertion.getAttributeStatements(), SAML2AuthManager.SAMLUserAttributeName.value());
                        }
                    }
                }
            }
            if (username == null) {
                throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(), "Failed to find admin configured username attribute in the SAML Response. Please ask your administrator to check SAML user attribute name.", params, responseType));
            }
            UserAccount userAccount = null;
            List<UserAccountVO> possibleUserAccounts = _userAccountDao.getAllUsersByNameAndEntity(username, issuer.getValue());
            if (possibleUserAccounts != null && possibleUserAccounts.size() > 0) {
                // Users can switch to other allowed accounts later
                for (UserAccountVO possibleUserAccount : possibleUserAccounts) {
                    if (possibleUserAccount.getAccountState().equals(Account.State.enabled.toString())) {
                        userAccount = possibleUserAccount;
                        break;
                    }
                }
            }
            if (userAccount == null || userAccount.getExternalEntity() == null || !_samlAuthManager.isUserAuthorized(userAccount.getId(), issuer.getValue())) {
                throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(), "Your authenticated user is not authorized for SAML Single Sign-On, please contact your administrator", params, responseType));
            }
            try {
                if (_apiServer.verifyUser(userAccount.getId())) {
                    LoginCmdResponse loginResponse = (LoginCmdResponse) _apiServer.loginUser(session, userAccount.getUsername(), userAccount.getUsername() + userAccount.getSource().toString(), userAccount.getDomainId(), null, remoteAddress, params);
                    SAMLUtils.setupSamlUserCookies(loginResponse, resp);
                    resp.sendRedirect(SAML2AuthManager.SAMLCloudStackRedirectionUrl.value());
                    return ApiResponseSerializer.toSerializedString(loginResponse, responseType);
                }
            } catch (CloudAuthenticationException | IOException exception) {
                s_logger.debug("SAML Login failed to log in the user due to: " + exception.getMessage());
            }
        }
    } catch (IOException e) {
        auditTrailSb.append("SP initiated SAML authentication using HTTP redirection failed:");
        auditTrailSb.append(e.getMessage());
    }
    throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(), "Unable to authenticate user while performing SAML based SSO. Please make sure your user/account has been added, enable and authorized by the admin before you can authenticate. Please contact your administrator.", params, responseType));
}
Also used : ValidationException(org.opensaml.xml.validation.ValidationException) Issuer(org.opensaml.saml2.core.Issuer) SAMLTokenVO(org.apache.cloudstack.saml.SAMLTokenVO) CloudAuthenticationException(com.cloud.exception.CloudAuthenticationException) StaticKeyInfoCredentialResolver(org.opensaml.xml.security.keyinfo.StaticKeyInfoCredentialResolver) ServerApiException(org.apache.cloudstack.api.ServerApiException) SAMLProviderMetadata(org.apache.cloudstack.saml.SAMLProviderMetadata) BasicX509Credential(org.opensaml.xml.security.x509.BasicX509Credential) Credential(org.opensaml.xml.security.credential.Credential) Assertion(org.opensaml.saml2.core.Assertion) EncryptedAssertion(org.opensaml.saml2.core.EncryptedAssertion) Decrypter(org.opensaml.saml2.encryption.Decrypter) IOException(java.io.IOException) LoginCmdResponse(org.apache.cloudstack.api.response.LoginCmdResponse) Response(org.opensaml.saml2.core.Response) HttpServletResponse(javax.servlet.http.HttpServletResponse) UserAccountVO(com.cloud.user.UserAccountVO) BasicX509Credential(org.opensaml.xml.security.x509.BasicX509Credential) EncryptedAssertion(org.opensaml.saml2.core.EncryptedAssertion) Signature(org.opensaml.xml.signature.Signature) SignatureValidator(org.opensaml.xml.signature.SignatureValidator) InlineEncryptedKeyResolver(org.opensaml.xml.encryption.InlineEncryptedKeyResolver) DecryptionException(org.opensaml.xml.encryption.DecryptionException) UserAccount(com.cloud.user.UserAccount) LoginCmdResponse(org.apache.cloudstack.api.response.LoginCmdResponse) InlineEncryptedKeyResolver(org.opensaml.xml.encryption.InlineEncryptedKeyResolver) EncryptedKeyResolver(org.opensaml.xml.encryption.EncryptedKeyResolver)

Aggregations

CloudAuthenticationException (com.cloud.exception.CloudAuthenticationException)1 UserAccount (com.cloud.user.UserAccount)1 UserAccountVO (com.cloud.user.UserAccountVO)1 IOException (java.io.IOException)1 HttpServletResponse (javax.servlet.http.HttpServletResponse)1 ServerApiException (org.apache.cloudstack.api.ServerApiException)1 LoginCmdResponse (org.apache.cloudstack.api.response.LoginCmdResponse)1 SAMLProviderMetadata (org.apache.cloudstack.saml.SAMLProviderMetadata)1 SAMLTokenVO (org.apache.cloudstack.saml.SAMLTokenVO)1 Assertion (org.opensaml.saml2.core.Assertion)1 EncryptedAssertion (org.opensaml.saml2.core.EncryptedAssertion)1 Issuer (org.opensaml.saml2.core.Issuer)1 Response (org.opensaml.saml2.core.Response)1 Decrypter (org.opensaml.saml2.encryption.Decrypter)1 DecryptionException (org.opensaml.xml.encryption.DecryptionException)1 EncryptedKeyResolver (org.opensaml.xml.encryption.EncryptedKeyResolver)1 InlineEncryptedKeyResolver (org.opensaml.xml.encryption.InlineEncryptedKeyResolver)1 Credential (org.opensaml.xml.security.credential.Credential)1 StaticKeyInfoCredentialResolver (org.opensaml.xml.security.keyinfo.StaticKeyInfoCredentialResolver)1 BasicX509Credential (org.opensaml.xml.security.x509.BasicX509Credential)1