public class SAMLSSOException extends ServletException {

    private String errorCode;

    public SAMLSSOException(String message) {

        super(message);
    }

    public SAMLSSOException(String code, String message) {

        super(message);
        this.errorCode = code;
    }

    public SAMLSSOException(String message, Throwable cause) {

        super(message, cause);
    }

    public SAMLSSOException(String code, String message, Throwable cause) {

        super(message, cause);
        this.errorCode = code;
    }

    public String getErrorCode() {

        return errorCode;
    }

    public void setErrorCode(String errorCode) {

        this.errorCode = errorCode;
    }
} @Component(
         name = "identity.application.authenticator.samlsso.component", 
         immediate = true)
public class SAMLSSOAuthenticatorServiceComponent {

    private static final Log log = LogFactory.getLog(SAMLSSOAuthenticatorServiceComponent.class);

    private static String postPage = null;

    @Activate
    protected void activate(ComponentContext ctxt) {
        String postPagePath = null;
        FileInputStream fis = null;
        try {
            SAMLSSOAuthenticator samlSSOAuthenticator = new SAMLSSOAuthenticator();
            ctxt.getBundleContext().registerService(ApplicationAuthenticator.class.getName(), samlSSOAuthenticator, null);
            ctxt.getBundleContext().registerService(HttpIdentityRequestFactory.class.getName(), new
                    SAMLLogoutRequestFactory(), null);
            ctxt.getBundleContext().registerService(HttpIdentityResponseFactory.class.getName(), new
                    SAMLLogoutResponseFactory(), null);
            ctxt.getBundleContext()
                    .registerService(IdentityProcessor.class.getName(), new SAMLLogoutRequestProcessor(), null);
            ctxt.getBundleContext()
                    .registerService(IdentityProcessor.class.getName(), new SAMLLogoutResponseProcessor(), null);

            postPagePath = CarbonUtils.getCarbonHome() + File.separator + "repository" + File.separator + "resources" + File.separator + "identity" + File.separator + "pages" + File.separator + "samlsso_federate.html";
            fis = new FileInputStream(new File(postPagePath));
            postPage = new Scanner(fis, "UTF-8").useDelimiter("\\A").next();
            if (log.isDebugEnabled()) {
                log.info("SAML2 SSO Authenticator bundle is activated");
            }
        } catch (FileNotFoundException e) {
            if (log.isDebugEnabled()) {
                log.debug("Failed to find SAMLSSO POST page for federation in " + postPagePath);
            }
        } catch (Throwable e) {
            if (log.isDebugEnabled()) {
                log.debug("Failed SAMLSSO authentication" + e);
            }
        } finally {
            IdentityIOStreamUtils.closeInputStream(fis);
        }
    }

    @Deactivate
    protected void deactivate(ComponentContext context) {
        if (log.isDebugEnabled()) {
            log.info("SAML2 SSO Authenticator bundle is deactivated");
        }
    }

    @Reference(
            name = "RealmService",
            service = org.wso2.carbon.user.core.service.RealmService.class,
            cardinality = ReferenceCardinality.MANDATORY,
            policy = ReferencePolicy.DYNAMIC,
            unbind = "unsetRealmService")
    protected void setRealmService(RealmService realmService) {
        if (log.isDebugEnabled()) {
            log.debug("RealmService is set in the SAML2 SSO Authenticator bundle");
        }
        SAMLSSOAuthenticatorServiceDataHolder.getInstance().setRealmService(realmService);
    }

    protected void unsetRealmService(RealmService realmService) {
        if (log.isDebugEnabled()) {
            log.debug("RealmService is unset in the SAML2 SSO Authenticator bundle");
        }
        SAMLSSOAuthenticatorServiceDataHolder.getInstance().setRealmService(null);
    }

    @Reference(
            name = "ServerConfigurationService",
            service = org.wso2.carbon.base.api.ServerConfigurationService.class,
            cardinality = ReferenceCardinality.MANDATORY,
            policy = ReferencePolicy.DYNAMIC,
            unbind = "unsetServerConfigurationService")
    protected void setServerConfigurationService(ServerConfigurationService serverConfigurationService) {
        if (log.isDebugEnabled()) {
            log.debug("Set the ServerConfiguration Service");
        }
        SAMLSSOAuthenticatorServiceDataHolder.getInstance().setServerConfigurationService(serverConfigurationService);
    }

    protected void unsetServerConfigurationService(ServerConfigurationService serverConfigurationService) {
        if (log.isDebugEnabled()) {
            log.debug("Unset the ServerConfiguration Service");
        }
        SAMLSSOAuthenticatorServiceDataHolder.getInstance().setServerConfigurationService(null);
    }

    public static String getPostPage() {
        return postPage;
    }
} public class DefaultSAML2SSOManager implements SAML2SSOManager {

    private static final String SIGN_AUTH2_SAML_USING_SUPER_TENANT = "SignAuth2SAMLUsingSuperTenant";
    private static final String NAME_ID_TYPE = "NameIDType";
    private static final Log log = LogFactory.getLog(DefaultSAML2SSOManager.class);
    private static boolean bootStrapped = false;
    private static String DEFAULT_MULTI_ATTRIBUTE_SEPARATOR = ",";
    private static String MULTI_ATTRIBUTE_SEPARATOR = "MultiAttributeSeparator";
    private static final String VERIFY_ASSERTION_ISSUER = "VerifyAssertionIssuer";
    private static final String BEGIN_CERTIFICATE = "-----BEGIN CERTIFICATE-----";
    private static final String END_CERTIFICATE = "-----END CERTIFICATE-----";
    private IdentityProvider identityProvider = null;
    private Map<String, String> properties;
    private String tenantDomain;

    public static void doBootstrap() {

        /* Initializing the OpenSAML library */
        if (!bootStrapped) {
            Thread thread = Thread.currentThread();
            ClassLoader loader = thread.getContextClassLoader();
            thread.setContextClassLoader(new DefaultSAML2SSOManager().getClass().getClassLoader());
            try {
                SAMLInitializer.doBootstrap();
                bootStrapped = true;
            } catch (InitializationException e) {
                log.error("Error in bootstrapping the OpenSAML3 library", e);
            } finally {
                thread.setContextClassLoader(loader);
            }
        }

    }

    @Override
    public void init(String tenantDomain, Map<String, String> properties, IdentityProvider idp)
            throws SAMLSSOException {

        this.tenantDomain = tenantDomain;
        this.identityProvider = idp;
        this.properties = properties;
    }

    /**
     * Returns the redirection URL with the appended SAML2
     * Request message
     *
     * @param request SAML 2 request
     * @return redirectionUrl
     */
    @Override
    public String buildRequest(HttpServletRequest request, boolean isLogout, boolean isPassive,
                               String loginPage, AuthenticationContext context)
            throws SAMLSSOException {

        doBootstrap();
        String contextIdentifier = context.getContextIdentifier();
        RequestAbstractType requestMessage;

        if (request.getParameter(SSOConstants.HTTP_POST_PARAM_SAML2_AUTH_REQ) == null) {
            String queryParam = context.getQueryParams();
            if (queryParam != null) {
                String[] params = queryParam.split("&");
                for (String param : params) {
                    String[] values = param.split("=");
                    if (values.length == 2 && SSOConstants.HTTP_POST_PARAM_SAML2_AUTH_REQ.equals(values[0])) {
                        request.setAttribute(SSOConstants.HTTP_POST_PARAM_SAML2_AUTH_REQ, values[1]);
                        break;
                    }
                }
            }
        }

        if (!isLogout) {
            requestMessage = buildAuthnRequest(request, isPassive, loginPage, context);
        } else {
            String username = (String) request.getSession().getAttribute(SSOConstants.LOGOUT_USERNAME);
            String sessionIndex = (String) request.getSession().getAttribute(SSOConstants.LOGOUT_SESSION_INDEX);
            String nameQualifier = (String) request.getSession().getAttribute(SSOConstants.NAME_QUALIFIER);
            String spNameQualifier = (String) request.getSession().getAttribute(SSOConstants.SP_NAME_QUALIFIER);
            String nameIdFormat = (String) request.getSession().getAttribute(SSOConstants.NAME_ID_FORMAT);

            requestMessage = buildLogoutRequest(username, sessionIndex, loginPage, nameQualifier, spNameQualifier,
                    nameIdFormat, context);
        }
        String idpUrl = null;
        boolean isSignAuth2SAMLUsingSuperTenant = false;

        String encodedRequestMessage = encodeRequestMessage(requestMessage);
        StringBuilder httpQueryString = new StringBuilder("SAMLRequest=" + encodedRequestMessage);

        try {
            httpQueryString.append("&RelayState=" + URLEncoder.encode(contextIdentifier, "UTF-8").trim());
        } catch (UnsupportedEncodingException e) {
            throw new SAMLSSOException(ErrorMessages.URL_ENCODING_RELAY_STATE.getCode(),
                    ErrorMessages.URL_ENCODING_RELAY_STATE.getMessage(), e);
        }

        boolean isRequestSigned;
        if (!isLogout) {
            isRequestSigned = SSOUtils.isAuthnRequestSigned(properties);
        } else {
            isRequestSigned = SSOUtils.isLogoutRequestSigned(properties);
        }

        if (isRequestSigned) {
            String signatureAlgoProp = properties
                    .get(IdentityApplicationConstants.Authenticator.SAML2SSO.SIGNATURE_ALGORITHM);
            if (StringUtils.isEmpty(signatureAlgoProp)) {
                signatureAlgoProp = IdentityApplicationConstants.XML.SignatureAlgorithm.RSA_SHA1;
            }
            String signatureAlgo = IdentityApplicationManagementUtil.getXMLSignatureAlgorithms()
                    .get(signatureAlgoProp);

            Map<String, String> parameterMap = FileBasedConfigurationBuilder.getInstance()
                    .getAuthenticatorBean(SSOConstants.AUTHENTICATOR_NAME).getParameterMap();
            if (parameterMap.size() > 0) {
                isSignAuth2SAMLUsingSuperTenant = Boolean.parseBoolean(parameterMap.
                        get(SIGN_AUTH2_SAML_USING_SUPER_TENANT));
            }
            if (isSignAuth2SAMLUsingSuperTenant) {
                SSOUtils.addSignatureToHTTPQueryString(httpQueryString, signatureAlgo,
                        new X509CredentialImpl(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME, null));
            } else {
                SSOUtils.addSignatureToHTTPQueryString(httpQueryString, signatureAlgo,
                        new X509CredentialImpl(context.getTenantDomain(), null));
            }
        }
        if (loginPage.indexOf("?") > -1) {
            idpUrl = loginPage.concat("&").concat(httpQueryString.toString());
        } else {
            idpUrl = loginPage.concat("?").concat(httpQueryString.toString());
        }
        return idpUrl;
    }

    /**
     * @param request
     * @param isLogout
     * @param isPassive
     * @param loginPage
     * @return return encoded SAML Auth request
     * @throws SAMLSSOException
     */
    public String buildPostRequest(HttpServletRequest request, boolean isLogout,
                                   boolean isPassive, String loginPage, AuthenticationContext context) throws SAMLSSOException {

        doBootstrap();
        RequestAbstractType requestMessage;
        String signatureAlgoProp = null;
        String digestAlgoProp = null;
        String includeCertProp = null;
        String signatureAlgo = null;
        String digestAlgo = null;
        boolean includeCert = false;

        // get Signature Algorithm
        signatureAlgoProp = properties
                .get(IdentityApplicationConstants.Authenticator.SAML2SSO.SIGNATURE_ALGORITHM);
        if (StringUtils.isEmpty(signatureAlgoProp)) {
            signatureAlgoProp = IdentityApplicationConstants.XML.SignatureAlgorithm.RSA_SHA1;
        }
        signatureAlgo = IdentityApplicationManagementUtil.getXMLSignatureAlgorithms().get(signatureAlgoProp);

        // get Digest Algorithm
        digestAlgoProp = properties
                .get(IdentityApplicationConstants.Authenticator.SAML2SSO.DIGEST_ALGORITHM);
        if (StringUtils.isEmpty(digestAlgoProp)) {
            digestAlgoProp = IdentityApplicationConstants.XML.DigestAlgorithm.SHA1;
        }
        digestAlgo = IdentityApplicationManagementUtil.getXMLDigestAlgorithms().get(digestAlgoProp);

        includeCertProp = properties
                .get(IdentityApplicationConstants.Authenticator.SAML2SSO.INCLUDE_CERT);
        if (StringUtils.isEmpty(includeCertProp) || Boolean.parseBoolean(includeCertProp)) {
            includeCert = true;
        }

        if (!isLogout) {
            requestMessage = buildAuthnRequest(request, isPassive, loginPage, context);
            if (SSOUtils.isAuthnRequestSigned(properties)) {
                SSOUtils.setSignature(requestMessage, signatureAlgo, digestAlgo, includeCert,
                        new X509CredentialImpl(context.getTenantDomain(), null));
            }
        } else {
            String username = (String) request.getSession().getAttribute(SSOConstants.LOGOUT_USERNAME);
            String sessionIndex = (String) request.getSession().getAttribute(SSOConstants.LOGOUT_SESSION_INDEX);
            String nameQualifier = (String) request.getSession().getAttribute(SSOConstants.NAME_QUALIFIER);
            String spNameQualifier = (String) request.getSession().getAttribute(SSOConstants.SP_NAME_QUALIFIER);
            String nameIdFormat = (String) request.getSession().getAttribute(SSOConstants.NAME_ID_FORMAT);

            requestMessage = buildLogoutRequest(username, sessionIndex, loginPage, nameQualifier, spNameQualifier,
                    nameIdFormat, context);
            if (SSOUtils.isLogoutRequestSigned(properties)) {
                SSOUtils.setSignature(requestMessage, signatureAlgo, digestAlgo, includeCert,
                        new X509CredentialImpl(context.getTenantDomain(), null));
            }
        }

        return SSOUtils.encode(SSOUtils.marshall(requestMessage));
    }

    @Override
    public void processResponse(HttpServletRequest request) throws SAMLSSOException {

        doBootstrap();
        if (isSAMLArtifactResponse(request)) {
            processArtifactResponse(request);
        } else {
            processSAMLResponse(request);
        }
    }

    private void processArtifactResponse(HttpServletRequest request) throws SAMLSSOException {

        SAMLSSOArtifactResolutionService artifactResolutionService = new SAMLSSOArtifactResolutionService(properties,
                tenantDomain);
        try {
            ArtifactResponse artifactResponse = artifactResolutionService.getSAMLArtifactResponse(request.getParameter(
                    SSOConstants.HTTP_POST_PARAM_SAML2_ARTIFACT_ID));
            validateSignature(artifactResponse);

            String code;
            for (XMLObject child : artifactResponse.getOrderedChildren()) {
                if (child instanceof Response || child instanceof LogoutResponse) {
                    validateResponseFormat(child);

                    for (XMLObject responseElement : child.getOrderedChildren()) {
                        if (responseElement instanceof Status) {
                            for (XMLObject statusElement : responseElement.getOrderedChildren()) {
                                code = getStatusCode(statusElement);
                                if (SUCCESS.equals(code)) {
                                    executeSAMLReponse(request, child);
                                } else {
                                    throw new SAMLSSOException(
                                            ErrorMessages.SAML_RESPONSE_STATUS_CODE_MISMATCHED_WITH_SUCCESS_CODE
                                                    .getCode(),
                                            ErrorMessages.SAML_RESPONSE_STATUS_CODE_MISMATCHED_WITH_SUCCESS_CODE
                                                    .getMessage());
                                }
                            }
                        }
                    }
                }
            }
        } catch (ArtifactResolutionException e) {
            throw new SAMLSSOException(ErrorMessages.ARTIFACT_RESPONSE_RESOLUTION_FAILED.getCode(),
                    ErrorMessages.ARTIFACT_RESPONSE_RESOLUTION_FAILED.getMessage(), e);
        }
    }

    private void processSAMLResponse(HttpServletRequest request) throws SAMLSSOException {

        String decodedResponse = new String(org.apache.commons.codec.binary.Base64.decodeBase64(request.getParameter(
                SSOConstants.HTTP_POST_PARAM_SAML2_RESP).getBytes()));
        XMLObject samlObject = SSOUtils.unmarshall(decodedResponse);
        validateResponseFormat(samlObject);
        executeSAMLReponse(request, samlObject);
    }

    private void executeSAMLReponse(HttpServletRequest request, XMLObject samlObject) throws SAMLSSOException {
        if (samlObject instanceof LogoutResponse) {
            //This is a SAML response for a single logout request from the SP
            // TODO need to change the API of this method to prevent unmarshalling twice.
            doSLO(request);
        } else if (samlObject instanceof Response) {
            processSSOResponse(request, (Response) samlObject);
        } else {
            throw new SAMLSSOException(ErrorMessages.UNABLE_TO_PROCESS_SAML_OBJECT_TYPE.getCode(),
                    ErrorMessages.UNABLE_TO_PROCESS_SAML_OBJECT_TYPE.getMessage());
        }
    }

    private String getStatusCode(XMLObject statusCode) {

        String code;
        if (statusCode.hasChildren()) {
            code = ((StatusCodeImpl) statusCode.getOrderedChildren().get(0)).getValue();
        } else {
            code = ((StatusCode) statusCode).getValue();
        }
        return code;
    }

    private boolean isSAMLArtifactResponse(HttpServletRequest request) {

        return request.getParameter(SSOConstants.HTTP_POST_PARAM_SAML2_ARTIFACT_ID) != null;
    }

    protected AuthnRequest getAuthnRequest(AuthenticationContext context) throws SAMLSSOException {

        AuthnRequest authnRequest = null;
        AuthenticationRequest authenticationRequest = context.getAuthenticationRequest();
        String[] samlRequestParams = authenticationRequest
                .getRequestQueryParam(SSOConstants.HTTP_POST_PARAM_SAML2_AUTH_REQ);
        String samlRequest = null;
        if (samlRequestParams != null && samlRequestParams.length > 0) {
            samlRequest = samlRequestParams[0];
            XMLObject xmlObject;
            if (authenticationRequest.isPost()) {
                xmlObject = SSOUtils.unmarshall(SSOUtils.decodeForPost(samlRequest));
            } else {
                xmlObject = SSOUtils.unmarshall(SSOUtils.decode(samlRequest));
            }
            validateResponseFormat(xmlObject);
            if (xmlObject instanceof AuthnRequest) {
                authnRequest = (AuthnRequest) xmlObject;
            }
        }
        return authnRequest;
    }

    protected Extensions getSAMLExtensions(HttpServletRequest request) {

        try {
            String samlRequest = request.getParameter(SSOConstants.HTTP_POST_PARAM_SAML2_AUTH_REQ);
            if (samlRequest == null) {
                samlRequest = (String) request.getAttribute(SSOConstants.HTTP_POST_PARAM_SAML2_AUTH_REQ);
            }

            if (samlRequest != null) {
                XMLObject xmlObject;
                if (SSOConstants.HTTP_POST.equals(request.getMethod())) {
                    xmlObject = SSOUtils.unmarshall(SSOUtils.decodeForPost(samlRequest));
                } else {
                    xmlObject = SSOUtils.unmarshall(SSOUtils.decode(samlRequest));
                }
                validateResponseFormat(xmlObject);
                if (xmlObject instanceof AuthnRequest) {
                    AuthnRequest authnRequest = (AuthnRequest) xmlObject;
                    Extensions oldExtensions = authnRequest.getExtensions();
                    if (oldExtensions != null) {
                        ExtensionsBuilder extBuilder = new ExtensionsBuilder();
                        Extensions extensions = extBuilder.buildObject(SAMLConstants.SAML20P_NS,
                                Extensions.DEFAULT_ELEMENT_LOCAL_NAME, SAMLConstants.SAML20P_PREFIX);
                        extensions.setDOM(oldExtensions.getDOM());
                        return extensions;
                    }
                }
            }
        } catch (Exception e) { // TODO IDENTITY-2421
            //ignore
            log.debug("Error while loading SAML Extensions", e);
        }

        return null;
    }

    protected Extensions getSAMLExtensions(AuthnRequest inboundAuthnRequest) {

        Extensions extensions = null;
        Extensions oldExtensions = inboundAuthnRequest.getExtensions();
        if (oldExtensions != null) {
            ExtensionsBuilder extBuilder = new ExtensionsBuilder();
            extensions = extBuilder.buildObject(SAMLConstants.SAML20P_NS,
                    Extensions.DEFAULT_ELEMENT_LOCAL_NAME, SAMLConstants.SAML20P_PREFIX);
            extensions.setDOM(oldExtensions.getDOM());
        }
        return extensions;
    }

    /**
     * This method handles the logout requests from the IdP
     * Any request for the defined logout URL is handled here
     *
     * @param request
     */
    public void doSLO(HttpServletRequest request) throws SAMLSSOException {

        doBootstrap();
        XMLObject samlObject = null;
        if (request.getParameter(SSOConstants.HTTP_POST_PARAM_SAML2_AUTH_REQ) != null) {
            samlObject = SSOUtils.unmarshall(new String(Base64.decodeBase64(request.getParameter(
                    SSOConstants.HTTP_POST_PARAM_SAML2_AUTH_REQ).getBytes())));
        }
        if (samlObject == null) {
            samlObject = SSOUtils.unmarshall(new String(Base64.decodeBase64(request.getParameter(
                    SSOConstants.HTTP_POST_PARAM_SAML2_RESP).getBytes())));
        }
        validateResponseFormat(samlObject);
        if (samlObject instanceof LogoutRequest) {
            LogoutRequest logoutRequest = (LogoutRequest) samlObject;
            String sessionIndex = logoutRequest.getSessionIndexes().get(0).getSessionIndex();
        } else if (samlObject instanceof LogoutResponse) {
            request.getSession().invalidate();
        } else {
            throw new SAMLSSOException(ErrorMessages.INVALID_SINGLE_LOGOUT_SAML_REQUEST.getCode(),
                    ErrorMessages.INVALID_SINGLE_LOGOUT_SAML_REQUEST.getMessage());
        }
    }

    private void processSSOResponse(HttpServletRequest request, Response samlResponse) throws SAMLSSOException {

        Assertion assertion = null;

        if (SSOUtils.isAssertionEncryptionEnabled(properties)) {
            List<EncryptedAssertion> encryptedAssertions = samlResponse.getEncryptedAssertions();
            EncryptedAssertion encryptedAssertion = null;
            if (isNotEmpty(encryptedAssertions)) {
                encryptedAssertion = encryptedAssertions.get(0);
                try {
                    assertion = getDecryptedAssertion(encryptedAssertion);
                } catch (Exception e) {
                    throw new SAMLSSOException(ErrorMessages.UNABLE_TO_DECRYPT_THE_SAML_ASSERTION.getCode(),
                            ErrorMessages.UNABLE_TO_DECRYPT_THE_SAML_ASSERTION.getMessage(), e);
                }
            }
        } else {
            List<Assertion> assertions = samlResponse.getAssertions();
            if (isNotEmpty(assertions)) {
                assertion = assertions.get(0);
            }
        }

        if (assertion == null) {
            if (samlResponse.getStatus() != null && samlResponse.getStatus().getStatusCode() != null) {
                if (samlResponse.getStatus().getStatusCode().getValue().equals(
                        SSOConstants.StatusCodes.IDENTITY_PROVIDER_ERROR)) {
                    if (samlResponse.getStatus().getStatusCode().getStatusCode() != null) {
                        if (samlResponse.getStatus().getStatusCode().getStatusCode().getValue().equals(
                                SSOConstants.StatusCodes.NO_PASSIVE)) {
                            return;
                        } else if (log.isDebugEnabled()) {
                            log.debug("SAML Response status code object value is: " +
                                    samlResponse.getStatus().getStatusCode().getStatusCode().getValue()
                                    + ".");
                            throw new SAMLSSOException("SAML Response status code object value is not" +
                                    "equal to: " + SSOConstants.StatusCodes.NO_PASSIVE + ".");
                        }
                    } else if (log.isDebugEnabled()) {
                        log.debug("SAML Response status code object is null.");
                    }
                } else if (log.isDebugEnabled()) {
                    log.debug("SAML Response status code value is: " +
                            samlResponse.getStatus().getStatusCode().getValue() + ".");
                    throw new SAMLSSOException("SAML Response status code value is not equal to: " +
                            SSOConstants.StatusCodes.IDENTITY_PROVIDER_ERROR + ".");
                }
            } else if (log.isDebugEnabled()) {
                log.debug("SAML Response status or the status code is null.");
            }

            throw new SAMLSSOException(ErrorMessages.SAML_ASSERTION_NOT_FOUND_IN_RESPONSE.getCode(),
                    ErrorMessages.SAML_ASSERTION_NOT_FOUND_IN_RESPONSE.getMessage());
        }

        // Validate the assertion issuer. This is an optional validation which is not mandate by the spec.
        validateAssertionIssuer(assertion);

        // validate the assertion validity period
        validateAssertionValidityPeriod(assertion);
        
        // this request attribute is populated in processAuthenticationResponse of SAMLSSOAuthenticator
        AuthenticationContext context = (AuthenticationContext) request
                .getAttribute(SAMLSSOAuthenticator.AUTHENTICATION_CONTEXT);

        // validate audience restriction
        validateAudienceRestriction(assertion, getIssuer(context));
        
        // validate signature this SP only looking for assertion signature
        validateSignature(samlResponse, assertion);

        // Get the subject name from the Response Object and forward it to login_action.jsp
        String subject = null;
        String nameQualifier = null;
        String spNameQualifier = null;
        String nameIdFormat = null;
        if (assertion.getSubject() != null && assertion.getSubject().getNameID() != null) {
            subject = assertion.getSubject().getNameID().getValue();
        }

        if (subject == null) {
            throw new SAMLSSOException(ErrorMessages.SUBJECT_NAME_NOT_FOUND_IN_RESPONSE.getCode(),
                    ErrorMessages.SUBJECT_NAME_NOT_FOUND_IN_RESPONSE.getMessage());
        }

        request.getSession().setAttribute("username", subject); // get the subject
        nameQualifier = assertion.getSubject().getNameID().getNameQualifier();
        spNameQualifier = assertion.getSubject().getNameID().getSPNameQualifier();
        nameIdFormat = assertion.getSubject().getNameID().getFormat();

        request.getSession(false).setAttribute("samlssoAttributes", getAssertionStatements(assertion));

        if (assertion.getAuthnStatements() != null) {
            List<String> authnContextClassRefs = new ArrayList<>();
            for (AuthnStatement authnStatement : assertion.getAuthnStatements()) {
                if (authnStatement.getAuthnContext() != null
                        && authnStatement.getAuthnContext().getAuthnContextClassRef() != null
                        && StringUtils.isNotBlank(authnStatement.getAuthnContext().getAuthnContextClassRef()
                        .getAuthnContextClassRef())) {
                    if (log.isDebugEnabled()) {
                        log.debug("Received AuthnContextClassRef: " + authnStatement.getAuthnContext()
                                .getAuthnContextClassRef().getAuthnContextClassRef());
                    }
                    authnContextClassRefs.add(authnStatement.getAuthnContext().getAuthnContextClassRef()
                            .getAuthnContextClassRef());
                }
            }

            if (!authnContextClassRefs.isEmpty()) {
                Map<String, Object> authnContextClassRefMap = new HashMap<>();
                authnContextClassRefMap.put(SSOConstants.AUTHN_CONTEXT_CLASS_REF, authnContextClassRefs);
                authnContextClassRefMap.put(IdentityApplicationConstants.Authenticator.SAML2SSO.IDP_ENTITY_ID,
                        assertion.getIssuer().getValue());
                request.getSession().setAttribute(SSOConstants.AUTHN_CONTEXT_CLASS_REF, authnContextClassRefMap);
            }
        }

        //For removing the session when the single sign out request made by the SP itself
        if (SSOUtils.isLogoutEnabled(properties)) {
            String sessionId = assertion.getAuthnStatements().get(0).getSessionIndex();
            if (sessionId == null) {
                throw new SAMLSSOException(ErrorMessages.IDP_SESSION_ID_NOT_FOUND_FOR_SLO.getCode(),
                        ErrorMessages.IDP_SESSION_ID_NOT_FOUND_FOR_SLO.getMessage());
            }
            request.getSession().setAttribute(SSOConstants.IDP_SESSION, sessionId);
            request.getSession().setAttribute(SSOConstants.NAME_QUALIFIER, nameQualifier);
            request.getSession().setAttribute(SSOConstants.SP_NAME_QUALIFIER, spNameQualifier);
            request.getSession().setAttribute(SSOConstants.NAME_ID_FORMAT, nameIdFormat);
        }

    }

    /**
     * this method builds the SAML logout request corresponding to the federated identity provider.
     * override this method to customize the SAML request.
     * 
     * @param user
     * @param sessionIndexStr
     * @param idpUrl
     * @param nameQualifier
     * @param spNameQualifier
     * @param nameIdFormat
     * @param context
     * @return
     * @throws SAMLSSOException
     */
    protected LogoutRequest buildLogoutRequest(String user, String sessionIndexStr, String idpUrl, String nameQualifier,
            String spNameQualifier, String nameIdFormat, AuthenticationContext context) throws SAMLSSOException {

        LogoutRequest logoutReq = new LogoutRequestBuilder().buildObject();

        logoutReq.setID(SSOUtils.createID());
        logoutReq.setDestination(idpUrl);

        DateTime issueInstant = new DateTime();
        logoutReq.setIssueInstant(issueInstant);
        logoutReq.setNotOnOrAfter(new DateTime(issueInstant.getMillis() + 5 * 60 * 1000));

        IssuerBuilder issuerBuilder = new IssuerBuilder();
        Issuer issuer = issuerBuilder.buildObject();

        String spEntityId = getIssuer(context);

        if (spEntityId != null && !spEntityId.isEmpty()) {
            issuer.setValue(spEntityId);
        } else {
            issuer.setValue("carbonServer");
        }

        logoutReq.setIssuer(issuer);

        NameID nameId = new NameIDBuilder().buildObject();

        if (StringUtils.isNotBlank(nameIdFormat)) {
            nameId.setFormat(nameIdFormat);
        } else {
            String includeNameIDPolicyProp = properties
                    .get(IdentityApplicationConstants.Authenticator.SAML2SSO.INCLUDE_NAME_ID_POLICY);
            if (StringUtils.isBlank(includeNameIDPolicyProp) || Boolean.parseBoolean(includeNameIDPolicyProp)) {
                nameId.setFormat(NameIDType.UNSPECIFIED);
            }
        }

        nameId.setValue(user);
        nameId.setNameQualifier(nameQualifier);
        nameId.setSPNameQualifier(spNameQualifier);
        logoutReq.setNameID(nameId);

        SessionIndex sessionIndex = new SessionIndexBuilder().buildObject();

        if (sessionIndexStr != null) {
            sessionIndex.setSessionIndex(sessionIndexStr);
        } else {
            sessionIndex.setSessionIndex(UUID.randomUUID().toString());
        }

        logoutReq.getSessionIndexes().add(sessionIndex);
        logoutReq.setReason("Single Logout");

        return logoutReq;
    }

    /**
     * this method builds the SAML request corresponding to the federated identity provider.
     * override this method to customize the SAML request.
     * 
     * @param request
     * @param isPassive
     * @param idpUrl
     * @param context
     * @return
     * @throws SAMLSSOException
     */
    protected AuthnRequest buildAuthnRequest(HttpServletRequest request, boolean isPassive, String idpUrl,
            AuthenticationContext context) throws SAMLSSOException {

        IssuerBuilder issuerBuilder = new IssuerBuilder();
        Issuer issuer = issuerBuilder.buildObject("urn:oasis:names:tc:SAML:2.0:assertion", "Issuer", "samlp");

        String spEntityId = getIssuer(context);

        if (spEntityId != null && !spEntityId.isEmpty()) {
            issuer.setValue(spEntityId);
        } else {
            issuer.setValue("carbonServer");
        }

        DateTime issueInstant = new DateTime();

        /* Creation of AuthRequestObject */
        AuthnRequestBuilder authRequestBuilder = new AuthnRequestBuilder();
        AuthnRequest authRequest = authRequestBuilder.buildObject("urn:oasis:names:tc:SAML:2.0:protocol",
                "AuthnRequest", "samlp");
        authRequest.setForceAuthn(isForceAuthenticate(context));
        authRequest.setIsPassive(isPassive);
        authRequest.setIssueInstant(issueInstant);

        String includeProtocolBindingProp = properties
                .get(IdentityApplicationConstants.Authenticator.SAML2SSO.INCLUDE_PROTOCOL_BINDING);
        if (StringUtils.isEmpty(includeProtocolBindingProp) || Boolean.parseBoolean(includeProtocolBindingProp)) {
            authRequest.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
        }

        AuthenticatorConfig authenticatorConfig =
                FileBasedConfigurationBuilder.getInstance().getAuthenticatorConfigMap()
                        .get(SSOConstants.AUTHENTICATOR_NAME);

        String acsUrl = getAcsUrl(authenticatorConfig);
        authRequest.setAssertionConsumerServiceURL(acsUrl);
        authRequest.setIssuer(issuer);
        authRequest.setID(SSOUtils.createID());
        authRequest.setVersion(SAMLVersion.VERSION_20);
        authRequest.setDestination(idpUrl);

        String attributeConsumingServiceIndexProp = properties
                .get(IdentityApplicationConstants.Authenticator.SAML2SSO.ATTRIBUTE_CONSUMING_SERVICE_INDEX);
        if (StringUtils.isNotEmpty(attributeConsumingServiceIndexProp)) {
            try {
                authRequest.setAttributeConsumingServiceIndex(Integer
                        .valueOf(attributeConsumingServiceIndexProp));
            } catch (NumberFormatException e) {
                log.error(
                        "Error while populating SAMLRequest with AttributeConsumingServiceIndex: "
                                + attributeConsumingServiceIndexProp, e);
            }
        }

        String includeNameIDPolicyProp = properties
                .get(IdentityApplicationConstants.Authenticator.SAML2SSO.INCLUDE_NAME_ID_POLICY);

        boolean isNameIDPolicyPropIncluded;
        if (Boolean.parseBoolean(IdentityUtil.getProperty(
                IdentityConstants.ServerConfig.ADD_NAME_ID_POLICY_IF_UNSPECIFIED))) {
            // Adding empty string check for backward compatibility.
            isNameIDPolicyPropIncluded = StringUtils.isEmpty(includeNameIDPolicyProp) ||
                    Boolean.parseBoolean(includeNameIDPolicyProp);
        } else {
            isNameIDPolicyPropIncluded = Boolean.parseBoolean(includeNameIDPolicyProp);
        }

        if (isNameIDPolicyPropIncluded) {
            NameIDPolicyBuilder nameIdPolicyBuilder = new NameIDPolicyBuilder();
            NameIDPolicy nameIdPolicy = nameIdPolicyBuilder.buildObject();

            String nameIdType = properties.get(NAME_ID_TYPE);
            if (StringUtils.isBlank(nameIdType)) {
                // NameID format was not set from the UI. Check the application-authentication.xml configs
                if (authenticatorConfig != null) {
                    nameIdType = authenticatorConfig.getParameterMap().get(NAME_ID_TYPE);
                    if (StringUtils.isBlank(nameIdType)) {
                        // No NameID format set. Let's go with the default NameID format
                        nameIdType = NameIDType.UNSPECIFIED;
                    }
                }
            }
            nameIdPolicy.setFormat(nameIdType);
            if (spEntityId != null && !spEntityId.isEmpty()) {
                nameIdPolicy.setSPNameQualifier(spEntityId);
            }
            //nameIdPolicy.setSPNameQualifier(issuer);
            nameIdPolicy.setAllowCreate(true);
            authRequest.setNameIDPolicy(nameIdPolicy);
        }

        //Get the inbound SAMLRequest
        AuthnRequest inboundAuthnRequest = getAuthnRequest(context);

        RequestedAuthnContext requestedAuthnContext = buildRequestedAuthnContext(inboundAuthnRequest);
        if (requestedAuthnContext != null) {
            authRequest.setRequestedAuthnContext(requestedAuthnContext);
        }

        Extensions extensions = getSAMLExtensions(request);
        if (extensions != null) {
            authRequest.setExtensions(extensions);
        }

        return authRequest;
    }

    private String getAcsUrl(AuthenticatorConfig authenticatorConfig) throws SAMLSSOException {

        String acsUrl = properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.ACS_URL);

        if (StringUtils.isNotEmpty(acsUrl) && log.isDebugEnabled()) {
            log.debug("Picking SAML acs URL from " + identityProvider.getIdentityProviderName() + " IDP's "
                    + "configuration: " + acsUrl);
        }

        if (StringUtils.isEmpty(acsUrl) && authenticatorConfig != null) {
            String tmpAcsUrl = authenticatorConfig.getParameterMap().get(SSOConstants.ServerConfig.SAML_SSO_ACS_URL);
            if (StringUtils.isNotBlank(tmpAcsUrl)) {
                acsUrl = tmpAcsUrl;
                if (log.isDebugEnabled()) {
                    log.debug("Picking SAML acs URL from application-authentication.xml: " + acsUrl);
                }
            }
        }

        if (StringUtils.isEmpty(acsUrl)) {
            try {
                acsUrl = ServiceURLBuilder.create().addPath(FrameworkConstants.COMMONAUTH).build()
                        .getAbsolutePublicURL();
            } catch (URLBuilderException e) {
                throw new SAMLSSOException("Error while building the acs url.", e);
            }
            if (log.isDebugEnabled()) {
                log.debug("Falling back to default SAML acs URL of the server: " + acsUrl);
            }
        }

        return acsUrl;
    }

    protected RequestedAuthnContext buildRequestedAuthnContext(AuthnRequest inboundAuthnRequest) throws SAMLSSOException {

        /* AuthnContext */
        RequestedAuthnContextBuilder requestedAuthnContextBuilder = null;
        RequestedAuthnContext requestedAuthnContext = null;

        String includeAuthnContext = properties
                .get(IdentityApplicationConstants.Authenticator.SAML2SSO.INCLUDE_AUTHN_CONTEXT);

        if (StringUtils.isNotEmpty(includeAuthnContext) && "as_request".equalsIgnoreCase(includeAuthnContext)) {
            if (inboundAuthnRequest != null) {
                RequestedAuthnContext incomingRequestedAuthnContext = inboundAuthnRequest.getRequestedAuthnContext();
                if (incomingRequestedAuthnContext != null) {
                    requestedAuthnContextBuilder = new RequestedAuthnContextBuilder();
                    requestedAuthnContext = requestedAuthnContextBuilder.buildObject();
                    requestedAuthnContext.setDOM(incomingRequestedAuthnContext.getDOM());
                }
            }
        } else if (StringUtils.isEmpty(includeAuthnContext) || "yes".equalsIgnoreCase(includeAuthnContext)) {
            requestedAuthnContextBuilder = new RequestedAuthnContextBuilder();
            requestedAuthnContext = requestedAuthnContextBuilder.buildObject();
            /* AuthnContextClass */
            AuthnContextClassRefBuilder authnContextClassRefBuilder = new AuthnContextClassRefBuilder();

            String authnContextClasses = properties
                    .get(IdentityApplicationConstants.Authenticator.SAML2SSO.AUTHENTICATION_CONTEXT_CLASS);

            if (StringUtils.isNotBlank(authnContextClasses)) {
                String[] authnContextClassList = authnContextClasses.split(DEFAULT_MULTI_ATTRIBUTE_SEPARATOR);
                for (String authnContextClassListElement : authnContextClassList) {
                    AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder
                            .buildObject(SAMLConstants.SAML20_NS,
                                    AuthnContextClassRef.DEFAULT_ELEMENT_LOCAL_NAME,
                                    SAMLConstants.SAML20_PREFIX);
                    String samlAuthnContextURN;
                    if (authnContextClassListElement.equals(IdentityApplicationConstants.Authenticator.SAML2SSO
                            .CUSTOM_AUTHENTICATION_CONTEXT_CLASS_OPTION)) {
                        samlAuthnContextURN = properties.get(IdentityApplicationConstants.Authenticator
                                .SAML2SSO.ATTRIBUTE_CUSTOM_AUTHENTICATION_CONTEXT_CLASS);
                    } else {
                        samlAuthnContextURN = IdentityApplicationManagementUtil
                                .getSAMLAuthnContextClasses().get(authnContextClassListElement);
                    }

                    if (StringUtils.isNotBlank(samlAuthnContextURN)) {
                        // There was one matched URN for given authnContextClass.
                        authnContextClassRef.setAuthnContextClassRef(samlAuthnContextURN);
                    } else {
                        // There are no any matched URN for given authnContextClass, so added authnContextClassListElement name to the
                        // AuthnContextClassRef.
                        authnContextClassRef.setAuthnContextClassRef(authnContextClassListElement);
                    }
                    requestedAuthnContext.getAuthnContextClassRefs().add(authnContextClassRef);
                }

            } else {
                AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder
                        .buildObject(SAMLConstants.SAML20_NS,
                                AuthnContextClassRef.DEFAULT_ELEMENT_LOCAL_NAME,
                                SAMLConstants.SAML20_PREFIX);
                authnContextClassRef.setAuthnContextClassRef(AuthnContext.PPT_AUTHN_CTX);
                requestedAuthnContext.getAuthnContextClassRefs().add(authnContextClassRef);
            }

            /* Authentication Context Comparison Level */
            String authnContextComparison = properties
                    .get(IdentityApplicationConstants.Authenticator.SAML2SSO.AUTHENTICATION_CONTEXT_COMPARISON_LEVEL);

            if (StringUtils.isNotEmpty(authnContextComparison)) {
                if (AuthnContextComparisonTypeEnumeration.EXACT.toString().equalsIgnoreCase(
                        authnContextComparison)) {
                    requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT);
                } else if (AuthnContextComparisonTypeEnumeration.MINIMUM.toString().equalsIgnoreCase(
                        authnContextComparison)) {
                    requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.MINIMUM);
                } else if (AuthnContextComparisonTypeEnumeration.MAXIMUM.toString().equalsIgnoreCase(
                        authnContextComparison)) {
                    requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.MAXIMUM);
                } else if (AuthnContextComparisonTypeEnumeration.BETTER.toString().equalsIgnoreCase(
                        authnContextComparison)) {
                    requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.BETTER);
                }
            } else {
                requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT);
            }
        }
        return requestedAuthnContext;
    }

    protected boolean isForceAuthenticate(AuthenticationContext context) {

        boolean forceAuthenticate = false;
        String forceAuthenticateProp = properties
                .get(IdentityApplicationConstants.Authenticator.SAML2SSO.FORCE_AUTHENTICATION);
        if ("yes".equalsIgnoreCase(forceAuthenticateProp)) {
            forceAuthenticate = true;
        } else if ("as_request".equalsIgnoreCase(forceAuthenticateProp)) {
            forceAuthenticate = context.isForceAuthenticate();
        }
        return forceAuthenticate;
    }

    protected String encodeRequestMessage(RequestAbstractType requestMessage)
            throws SAMLSSOException {

        Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(requestMessage);
        Element authDOM = null;
        try {
            authDOM = marshaller.marshall(requestMessage);

            /* Compress the message */
            Deflater deflater = new Deflater(Deflater.DEFLATED, true);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(byteArrayOutputStream, deflater);
            SerializeSupport.writeNode(authDOM, deflaterOutputStream);
            deflaterOutputStream.close();

            /* Encoding the compressed message */
            String encodedRequestMessage = new String(org.apache.commons.codec.binary.Base64.encodeBase64(byteArrayOutputStream.toByteArray(), false));

            byteArrayOutputStream.write(byteArrayOutputStream.toByteArray());
            byteArrayOutputStream.toString();

            // log saml
            if (log.isDebugEnabled()) {
                log.debug("SAML Request  :  " + deflaterOutputStream.toString());
            }

            return URLEncoder.encode(encodedRequestMessage, "UTF-8").trim();

        } catch (MarshallingException | IOException e) {
            throw new SAMLSSOException(ErrorMessages.IO_ERROR.getCode(),
                    "Error occurred while encoding SAML request", e);
        }
    }

    /**
     * Validate response format.
     *
     * @param response XMLObject response.
     * @throws SAMLSSOException SAMLSSOException.
     */
    protected void validateResponseFormat(XMLObject response) throws SAMLSSOException {

        // Checking for duplicate samlp:Response. This is done to thwart possible XSW attacks
        NodeList responseList = response.getDOM().getElementsByTagNameNS(SAMLConstants.SAML20P_NS, "Response");
        if (responseList != null && responseList.getLength() > 0) {
            throw new SAMLSSOException(ErrorMessages.INVALID_SCHEMA_FOR_THE_SAML_2_RESPONSE.getCode(),
                    ErrorMessages.INVALID_SCHEMA_FOR_THE_SAML_2_RESPONSE.getMessage());
        }

        // Checking for multiple Assertions. This is done to thwart possible XSW attacks.
        NodeList assertionList = response.getDOM().getElementsByTagNameNS(SAMLConstants.SAML20_NS, "Assertion");
        if (assertionList != null && assertionList.getLength() > 1) {
            throw new SAMLSSOException(ErrorMessages.PROCESSING_SAML2_MULTIPLE_ASSERTION_ELEMENT_FOUND.getCode(),
                    ErrorMessages.PROCESSING_SAML2_MULTIPLE_ASSERTION_ELEMENT_FOUND.getMessage());
        }
    }

    /*
     * Process the response and returns the results
     */
    private Map<ClaimMapping, String> getAssertionStatements(Assertion assertion) {

        Map<ClaimMapping, String> results = new HashMap<ClaimMapping, String>();
        String multiAttributeSeparator = DEFAULT_MULTI_ATTRIBUTE_SEPARATOR;

        UserRealm realm;
        try {
            realm = SAMLSSOAuthenticatorServiceDataHolder.getInstance().getRealmService().getTenantUserRealm
                    (MultitenantConstants.SUPER_TENANT_ID);
            UserStoreManager userStoreManager = (UserStoreManager) realm.getUserStoreManager();

            multiAttributeSeparator = userStoreManager.
                    getRealmConfiguration().getUserStoreProperty(MULTI_ATTRIBUTE_SEPARATOR);
        } catch (UserStoreException e) {
            log.warn("Error while reading MultiAttributeSeparator valaue from primary user store ", e);
        }

        if (assertion != null) {

            List<AttributeStatement> attributeStatementList = assertion.getAttributeStatements();

            if (attributeStatementList != null) {
                for (AttributeStatement statement : attributeStatementList) {
                    List<Attribute> attributesList = statement.getAttributes();
                    for (Attribute attribute : attributesList) {
                        List<XMLObject> values = attribute.getAttributeValues();
                        String attributesValue = null;
                        if (values != null) {
                            for (int i = 0; i < values.size(); i++) {
                                Element value = attribute.getAttributeValues().get(i).getDOM();
                                String attributeValue = value.getTextContent();
                                if (StringUtils.isBlank(attributesValue)) {
                                    attributesValue = attributeValue;
                                } else {
                                    attributesValue = attributesValue + multiAttributeSeparator + attributeValue;
                                }
                            }
                        }

                        results.put(ClaimMapping.build(attribute.getName(),
                                attribute.getName(), null, false), attributesValue);
                    }
                }
            }
        }
        return results;
    }

    /**
     * Validate the AudienceRestriction of SAML2 Response
     *
     * @param assertion SAML2 Assertion
     * @param issuer Issuer of the SAML2 Assertion
     */
    protected void validateAudienceRestriction(Assertion assertion, String issuer) throws SAMLSSOException {

        if (assertion != null) {
            Conditions conditions = assertion.getConditions();
            if (conditions != null) {
                List<AudienceRestriction> audienceRestrictions = conditions.getAudienceRestrictions();
                if (audienceRestrictions != null && !audienceRestrictions.isEmpty()) {
                    for (AudienceRestriction audienceRestriction : audienceRestrictions) {
                        if (isNotEmpty(audienceRestriction.getAudiences())) {
                            boolean audienceFound = false;
                            for (Audience audience : audienceRestriction.getAudiences()) {
                                if (issuer != null && issuer.equals(audience.getAudienceURI())) {
                                    audienceFound = true;
                                    break;
                                }
                            }
                            if (!audienceFound) {
                                throw new SAMLSSOException(
                                        ErrorMessages.AUDIENCE_RESTRICTION_VALIDATION_FAILED.getCode(),
                                        ErrorMessages.AUDIENCE_RESTRICTION_VALIDATION_FAILED.getMessage());
                            }
                        } else {
                            throw new SAMLSSOException(
                                    ErrorMessages.AUDIENCES_NOT_FOUND.getCode(),
                                    ErrorMessages.AUDIENCES_NOT_FOUND.getMessage());
                        }
                    }
                } else {
                    throw new SAMLSSOException(ErrorMessages.AUDIENCE_RESTRICTION_NOT_FOUND.getCode(),
                            ErrorMessages.AUDIENCE_RESTRICTION_NOT_FOUND.getMessage());
                }
            } else {
                throw new SAMLSSOException(ErrorMessages.SAML_CONDITIONS_NOT_FOUND.getCode(),
                        ErrorMessages.SAML_CONDITIONS_NOT_FOUND.getMessage());
            }
        }
    }

    /**
     * Validate the signature of a SAML2 Response and Assertion.
     *
     * @param response SAML2 Response.
     * @param assertion SAML2 assertion.
     * @throws SAMLSSOException SAMLSSOException.
     */
    protected void validateSignature(Response response, Assertion assertion) throws
            SAMLSSOException {

        if (SSOUtils.isAuthnResponseSigned(properties)) {

            XMLObject signature = response.getSignature();
            if (signature == null) {
                throw new SAMLSSOException(ErrorMessages.SIGNATURE_ELEMENT_NOT_FOUND_WHILE_ENABLED.getCode(),
                        ErrorMessages.SIGNATURE_ELEMENT_NOT_FOUND_WHILE_ENABLED.getMessage());
            } else {
                validateSignature(signature);
            }
        }
        if (SSOUtils.isAssertionSigningEnabled(properties)) {

            XMLObject signature = assertion.getSignature();
            if (assertion.getSignature() == null) {
                throw new SAMLSSOException(
                        ErrorMessages.SIGNATURE_ELEMENT_NOT_FOUND_IN_SAML_ASSERTION_WHILE_SIGNING_ENABLED.getCode(),
                        ErrorMessages.SIGNATURE_ELEMENT_NOT_FOUND_IN_SAML_ASSERTION_WHILE_SIGNING_ENABLED.getMessage());
            } else {
                validateSignature(signature);
            }
        }
    }

    /**
     * Validate the signature of a SAML2 Artifact Response
     *
     * @param artifactResponse SAML2 Artifact Response
     * @throws SAMLSSOException
     */
    protected void validateSignature(ArtifactResponse artifactResponse) throws SAMLSSOException {

        if (SSOUtils.isArtifactResponseSigningEnabled(properties)) {

            XMLObject signature = artifactResponse.getSignature();
            if (signature == null) {
                throw new SAMLSSOException(
                        ErrorMessages.SIGNATURE_ELEMENT_NOT_FOUND_IN_ARTIFACT_RESPONSE_WHILE_ENABLED.getCode(),
                        ErrorMessages.SIGNATURE_ELEMENT_NOT_FOUND_IN_ARTIFACT_RESPONSE_WHILE_ENABLED.getMessage());
            } else {
                validateSignature(signature);
            }
        }
    }

    /**
     * Validates the XML Signature element
     *
     * @param signature XML Signature element
     * @throws SAMLSSOException
     */
    protected void validateSignature(XMLObject signature) throws SAMLSSOException {

        SignatureImpl signImpl = (SignatureImpl) signature;
        CertificateInfo[] certificateInfos;
        boolean isExceptionThrown = false;
        SignatureException validationException = null;
        try {
            SAMLSignatureProfileValidator signatureProfileValidator = new SAMLSignatureProfileValidator();
            signatureProfileValidator.validate(signImpl);
        } catch (SignatureException ex) {
            String logMsg = ErrorMessages.SIGNATURE_NOT_CONFIRM_TO_SAML_SIGNATURE_PROFILE.getMessage();
            AUDIT_LOG.warn(logMsg);
            throw new SAMLSSOException(ErrorMessages.SIGNATURE_NOT_CONFIRM_TO_SAML_SIGNATURE_PROFILE.getCode(),
                    logMsg, ex);
        }

        if (ArrayUtils.isEmpty(identityProvider.getCertificateInfoArray())) {
            throw new SAMLSSOException(ErrorMessages.SIGNATURE_VALIDATION_FAILED_FOR_SAML_RESPONSE.getCode(),
                    ErrorMessages.SIGNATURE_VALIDATION_FAILED_FOR_SAML_RESPONSE.getMessage(),
                    validationException);
        }

        certificateInfos = identityProvider.getCertificateInfoArray();
        if (log.isDebugEnabled()) {
            log.debug("The number of certificates has been found is: " + certificateInfos.length);
        }
        int index = 0;
        for (CertificateInfo certificateInfo : certificateInfos) {
            String certVal = certificateInfo.getCertValue();
            X509Credential credential = new X509CredentialImpl(tenantDomain, certVal);
            try {
                if (log.isDebugEnabled()) {
                    log.debug("Validating the SAML signature with certificate at index: " + index);
                }
                SignatureValidator.validate(signImpl, credential);
                isExceptionThrown = false;
                break;
            } catch (SignatureException e) {
                isExceptionThrown = true;
                if (validationException == null) {
                    validationException = e;
                } else {
                    validationException.addSuppressed(e);
                }
            }
            index++;
        }
        // If all the certification validation fails, then throw the exception.
        if (isExceptionThrown) {
            throw new SAMLSSOException("Signature validation failed for SAML Response", validationException);
        }
    }

    /**
     * Validates the 'Not Before' and 'Not On Or After' conditions of the SAML Assertion
     *
     * @param assertion SAML Assertion element
     * @throws SAMLSSOException
     */
    protected void validateAssertionValidityPeriod(Assertion assertion) throws SAMLSSOException {

        if (assertion.getConditions() != null) {
            DateTime validFrom = assertion.getConditions().getNotBefore();
            DateTime validTill = assertion.getConditions().getNotOnOrAfter();
            int timeStampSkewInSeconds = IdentityUtil.getClockSkewInSeconds();

            if (validFrom != null && validFrom.minusSeconds(timeStampSkewInSeconds).isAfterNow()) {
                throw new SAMLSSOException(ErrorMessages.NOT_BEFORE_CONDITION_NOT_MET.getCode(),
                        ErrorMessages.NOT_BEFORE_CONDITION_NOT_MET.getMessage());
            }

            if (validTill != null && validTill.plusSeconds(timeStampSkewInSeconds).isBeforeNow()) {
                throw new SAMLSSOException(
                        ErrorMessages.NOT_ON_OR_BEFORE_CONDITION_NOT_MET.getCode(),
                        ErrorMessages.NOT_ON_OR_BEFORE_CONDITION_NOT_MET.getMessage());
            }

            if (validFrom != null && validTill != null && validFrom.isAfter(validTill)) {
                throw new SAMLSSOException(ErrorMessages.NOT_ON_OR_BEFORE_CONDITION_NOT_MET.getCode(),
                        "SAML Assertion Condition 'Not Before' must be less than the value of 'Not On Or After'");
            }
        }
    }

    /**
     * Get Decrypted Assertion.
     *
     * @param encryptedAssertion Encrypted assertion.
     * @return Decrypted assertion.
     * @throws Exception Exception.
     */
    protected Assertion getDecryptedAssertion(EncryptedAssertion encryptedAssertion) throws Exception {

        X509Credential credential = new X509CredentialImpl(tenantDomain, null);
        KeyInfoCredentialResolver keyResolver = new StaticKeyInfoCredentialResolver(credential);
        EncryptedKey key = getEncryptedKey(encryptedAssertion);
        Decrypter decrypter = new Decrypter(null, keyResolver, null);
        SecretKey dkey = (SecretKey) decrypter.decryptKey(key, encryptedAssertion.getEncryptedData().
                getEncryptionMethod().getAlgorithm());
        Credential shared = CredentialSupport.getSimpleCredential(dkey);
        decrypter = new Decrypter(new StaticKeyInfoCredentialResolver(shared), null, null);
        decrypter.setRootInNewDocument(true);
        return decrypter.decrypt(encryptedAssertion);
    }

    /**
     * Validate assertion issuer.
     *
     * @param assertion SAML2 assertion.
     * @throws SAMLSSOException SAMLSSOException.
     */
    protected void validateAssertionIssuer(Assertion assertion) throws SAMLSSOException {

        if (isAssertionIssuerVerificationEnabled()) {
            if (log.isDebugEnabled()) {
                log.debug("Assertion issuer verification is enabled.");
            }

            String idpEntityId = properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.IDP_ENTITY_ID);
            if (!idpEntityId.equals(assertion.getIssuer().getValue())) {
                log.warn("Issuer value in the assertion is invalid. Expected value is '" + idpEntityId + "'," +
                        " but received value in the assertion is '" + assertion.getIssuer().getValue() + "'.");
                throw new SAMLSSOException(ErrorMessages.INVALID_IDP_ID.getCode(),
                        String.format(ErrorMessages.INVALID_IDP_ID.getMessage(),
                                assertion.getIssuer().getValue()));
            }
        }
    }

    private boolean isAssertionIssuerVerificationEnabled() {

        AuthenticatorConfig authenticatorConfig = FileBasedConfigurationBuilder.getInstance()
                .getAuthenticatorConfigMap().get(SSOConstants.AUTHENTICATOR_NAME);

        if (authenticatorConfig != null && authenticatorConfig.getParameterMap() != null) {
            String isVerifyAssertionIssuer = authenticatorConfig.getParameterMap().get(VERIFY_ASSERTION_ISSUER);
            if (StringUtils.isNotEmpty(isVerifyAssertionIssuer)) {
                return Boolean.parseBoolean(isVerifyAssertionIssuer);
            }
        }
        return false;
    }
    
    /**
     * finds the issuer of the SAML request. this is used at the time we build the request and also
     * at the time we validate the audience in the SAML response.
     * 
     * @param context
     * @return
     */
    protected String getIssuer(AuthenticationContext context) {
        // this is the issuer from the SAML federated authenticator.
        return properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.SP_ENTITY_ID);
    }

    private EncryptedKey getEncryptedKey(EncryptedAssertion encryptedAssertion) throws Exception {

        List<EncryptedKey> encryptedKeys = encryptedAssertion.getEncryptedData().getKeyInfo().getEncryptedKeys();
        if (isNotEmpty(encryptedKeys)) {
            if (log.isDebugEnabled()) {
                log.debug("EncryptedKey obtain from the encrypted data element.");
            }
            return encryptedKeys.get(0);
        }
        encryptedKeys = encryptedAssertion.getEncryptedKeys();
        if (isNotEmpty(encryptedKeys)) {
            if (log.isDebugEnabled()) {
                log.debug("EncryptedKey obtained from the Assertion.");
            }
            return encryptedKeys.get(0);
        }
        throw new Exception("Could not obtain the encrypted key from the encrypted assertion.");
    }
} public interface SAML2SSOManager {

    public void init(String tenantDomain, Map<String, String> properties, IdentityProvider idp) throws SAMLSSOException;

    public String buildRequest(HttpServletRequest request, boolean isLogout, boolean isPassive,
                               String loginPage, AuthenticationContext context)
            throws SAMLSSOException;

    public void processResponse(HttpServletRequest request) throws SAMLSSOException;
} public class StateInfo extends AuthenticatorStateInfo {

    private String sessionIndex;
    private String subject;
    private String nameQualifier;
    private String spNameQualifier;

    public String getSessionIndex() {
        return sessionIndex;
    }

    public void setSessionIndex(String sessionIndex) {
        this.sessionIndex = sessionIndex;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public String getNameQualifier() {
        return nameQualifier;
    }

    public void setNameQualifier(String nameQualifier) {
        this.nameQualifier = nameQualifier;
    }

    public String getSpNameQualifier() {
        return spNameQualifier;
    }

    public void setSpNameQualifier(String spNameQualifier) {
        this.spNameQualifier = spNameQualifier;
    }
} public class SSOConstants {

    public static final String AUTHENTICATOR_NAME = "SAMLSSOAuthenticator";
    public static final String AUTHENTICATOR_FRIENDLY_NAME = "samlsso";

    public static final String HTTP_POST_PARAM_SAML2_AUTH_REQ = "SAMLRequest";
    public static final String HTTP_POST_PARAM_SAML2_RESP = "SAMLResponse";
    public static final String HTTP_POST_PARAM_SAML2_ARTIFACT_ID = "SAMLart";
    public static final String IDP_SESSION = "IdPSession";
    public static final String RELAY_STATE = "RelayState";

    public static final String HTTP_POST = "POST";
    public static final String POST = "POST";
    public static final String REDIRECT = "REDIRECT";

	public static final String SERVLET_REQ_ATTR_AUTHENTICATION_CONTEXT = "authenticationContext";

    public static final String SP_NAME_QUALIFIER = "spNameQualifier";
    public static final String NAME_QUALIFIER = "nameQualifier";
    public static final String LOGOUT_USERNAME = "logoutUsername";
    public static final String LOGOUT_SESSION_INDEX = "logoutSessionIndex";
    public static final String AUTHN_CONTEXT_CLASS_REF = "AuthnContextClassRef";
    public static final String NAME_ID_FORMAT = "nameIdFormat";
    public static final String ISSUER_FORMAT = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity";

    public static final String SOAP_ACTION_PARAM_KEY = "SOAPAction";
    public static final String ACCEPT_PARAM_KEY = "Accept";
    public static final String CONTENT_TYPE_PARAM_KEY = "Content-Type";
    public static final String PRAGMA_PARAM_KEY = "Pragma";
    public static final String CACHE_CONTROL_PARAM_KEY = "Cache-Control";

    public static final String SECURITY_KEYSTORE_LOCATION = "Security.KeyStore.Location";
    public static final String SECURITY_KEYSTORE_TYPE = "Security.KeyStore.Type";

    public static final String SAML_SLO_URL = "identity/saml/slo";
    public static final Pattern SAML_SLO_ENDPOINT_URL_PATTERN = Pattern.compile("(.*)/identity/saml/slo/?");

    public class StatusCodes {
        private StatusCodes() {

        }

        public static final String IDENTITY_PROVIDER_ERROR = "urn:oasis:names:tc:SAML:2.0:status:Responder";
        public static final String NO_PASSIVE = "urn:oasis:names:tc:SAML:2.0:status:NoPassive";
        public static final String SUCCESS_CODE = "urn:oasis:names:tc:SAML:2.0:status:Success";
        public static final String REQUESTOR_ERROR = "urn:oasis:names:tc:SAML:2.0:status:Requester";
        public static final String VERSION_MISMATCH = "urn:oasis:names:tc:SAML:2.0:status:VersionMismatch";

    }

    public class ServerConfig {
        private ServerConfig() {

        }

        public static final String KEY_ALIAS = "Security.KeyStore.KeyAlias";
        public static final String KEY_PASSWORD = "Security.KeyStore.KeyPassword";

        public static final String SAML2_SSO_MANAGER = "SAML2SSOManager";
        public static final String SAML_SSO_ACS_URL = "SAMLSSOAssertionConsumerUrl";

        public static final String HTTPS_PROXY_HOST = "HTTPS_PROXY_HOST";
        public static final String HTTPS_PROXY_PORT = "HTTPS_PROXY_PORT";
    }
} public enum ErrorMessages {

    // SAML Assertion related Exceptions
    UNABLE_TO_DECRYPT_THE_SAML_ASSERTION("SAM-65001", "Unable to decrypt the SAML Assertion"),
    // Not found related Exceptions
    FEDERATED_USER_IDENTIFIER_NOT_FOUND("SAM-65021",
            "Cannot find federated User Identifier post authentication handler"),
    CLASS_NOT_FOUND_EXCEPTION("SAM-65022", "Cannot find the class definition"),
    // Signature related Exceptions
    ADDING_SIGNATURE_TO_HTTP_QUERY_STRING_FAILED("SAM-65041",
            "Error while adding signature to HTTP query string"),
    SIGNING_SAML_REQUEST_FAILED("SAM-65042", "Error while signing the SAML Request"),
    UNABLE_TO_SIGN_QUERY_STRING("SAM-65043", "Unable to sign query string"),
    IDP_CERTIFICATE_MISSING("SAM-65044",
            "IdP certificate is needed for AuthnRequest signing in POST binding"),
    // Tenant and IDP  related Exceptions
    INVALID_IDP_ID("SAM-65061",
            "Identity provider with entity id '%s' is not registered in the system."),
    IO_ERROR("SAM-65062", "IO_ERROR"),
    // keystore related Exceptions
    UNABLE_TO_LOCATE_KEYSTORE("SAM-65071", "Unable to locate keystore"),
    UNABLE_TO_READ_KEYSTORE("SAM-65072", "Unable to read keystore"),
    CONFIGURED_PRIVATE_KEY_IS_INVALID("SAM-65073",
            "Configured signing KeyStore private key is invalid"),
    CONFIGURED_PUBLIC_KEY_IS_INVALID("SAM-65074",
            "Configured signing KeyStore public key is invalid"),
    UNABLE_TO_LOAD_KEYSTORE("SAM-65075", "Unable to load keystore"),
    // key and certificate related Exceptions
    UNABLE_TO_READ_CERTIFICATE("SAM-65091", "Unable to read certificate"),
    INVALID_ALGORITHM("SAM-65092", "Unable to load algorithm"),
    UNABLE_TO_LOAD_KEY("SAM-65093", "Unable to load key"),
    RETRIEVING_PRIVATE_KEY_AND_CERTIFICATE_FOR_TENANT_FAILED("SAM-65094",
            "Error retrieving private key and the certificate for tenant %s"),
    CANNOT_FIND_THE_PRIVATE_KEY_FOR_TENANT("SAM-65095",
            "Cannot find the private key for tenant %s"),
    CANNOT_FIND_THE_CERTIFICATE("SAM-65096", "Cannot find the certificate."),
    // Other Exceptions
    ILLEGAL_ACCESS("SAM-60131", "Error while illegal access"),
    SAML_RESPONSE_STATUS_CODE_MISMATCHED_WITH_SUCCESS_CODE("SAM-60132", "Received an error SAML response."),
    ARTIFACT_RESPONSE_RESOLUTION_FAILED("SAM-60133", "Error when getting the Artifact Response."),
    INVALID_SINGLE_LOGOUT_SAML_REQUEST("SAM-60134", "Invalid Single Logout SAML Request"),
    // SAML Assertion related Exceptions
    SAML_ASSERTION_NOT_FOUND_IN_RESPONSE("SAM-60001", "SAML Assertion is not found in the Response"),
    AUDIENCE_RESTRICTION_VALIDATION_FAILED("SAM-60002",
            "SAML Assertion Audience Restriction validation failed"),
    PROCESSING_SAML2_MULTIPLE_ASSERTION_ELEMENT_FOUND("SAM-60003",
            "Error occurred while processing SAML2 response. Invalid schema for the SAML2 response. Multiple Response elements found."),
    SIGNATURE_ELEMENT_NOT_FOUND_IN_SAML_ASSERTION_WHILE_SIGNING_ENABLED("SAM-60004",
            "SAMLAssertion signing is enabled, but signature element not found in SAML Assertion element."),
    NOT_BEFORE_CONDITION_NOT_MET("SAM-60005",
            "Failed to meet SAML Assertion Condition 'Not Before'"),
    NOT_ON_OR_BEFORE_CONDITION_NOT_MET("SAM-60006",
            "Failed to meet SAML Assertion Condition 'Not On Or After'"),
    IDP_SESSION_ID_NOT_FOUND_FOR_SLO("SAM-60007",
            "Single Logout is enabled but IdP Session ID not found in SAML Assertion"),
    // Not found related Exceptions
    SUBJECT_NAME_NOT_FOUND_IN_RESPONSE("SAM-60021",
            "SAML Response does not contain the name of the subject"),
    INVALID_SCHEMA_FOR_THE_SAML_2_RESPONSE("SAM-60022",
            "Error occurred while processing SAML2 response. Invalid schema for the SAML2 response. Multiple Response elements found."),
    AUDIENCES_NOT_FOUND("SAM-60023",
            "SAML Response's AudienceRestriction doesn't contain Audiences"),
    SAML_CONDITIONS_NOT_FOUND("SAM-60024", "SAML Response doesn't contain Conditions"),
    // Signature related Exceptions
    SIGNATURE_ELEMENT_NOT_FOUND_WHILE_ENABLED("SAM-60041",
            "SAMLResponse signing is enabled, but signature element not found in SAML Response element."),
    SIGNATURE_ELEMENT_NOT_FOUND_IN_ARTIFACT_RESPONSE_WHILE_ENABLED(
            "SAM-60042",
            "Artifact Response signing is enabled, but signature element not found in Artifact Response element."),
    AUDIENCE_RESTRICTION_NOT_FOUND("SAM-60043",
            "SAML Response doesn't contain AudienceRestrictions"),
    SIGNATURE_NOT_CONFIRM_TO_SAML_SIGNATURE_PROFILE("SAM-60044",
            "Signature do not confirm to SAML signature profile. Possible XML Signature Wrapping Attack!"),
    SIGNATURE_VALIDATION_FAILED_FOR_SAML_RESPONSE("SAM-60045",
            "Signature validation failed for SAML Response"),
    // Tenant and IDP  related Exceptions
    RETRIEVING_TENANT_ID_FAILED("SAM-60061",
            "Exception occurred while retrieving Tenant ID from tenant domain %S"),
    // key and certificate related Exceptions
    RETRIEVING_THE_CERTIFICATE_FAILED("SAM-60091", "Retrieving the certificate"),
    // Decoding and Encoding related Exceptions
    UNABLE_TO_PROCESS_SAML_OBJECT_TYPE("SAM-65111", "Unable to process unknown SAML object type."),
    URL_ENCODING_RELAY_STATE("SAM-65112", "Error occurred while url encoding RelayState"),
    UNSUPPORTED_ENCODING_EXCEPTION("SAM-65113", "UnsupportedEncodingException"),
    UNMARSHALLING_SAML_REQUEST_ENCODED_STRING_FAILED("SAM-65114",
            "Error in unmarshalling SAML Request from the encoded String"),
    MARSHALLING_SAML_REQUEST_FOR_SIGNING_FAILED("SAM-65115",
            "Error while marshalling the SAML Request for signing"),
    // Other Exceptions
    HTTP_POST_NOT_SUPPORTED("SAM-65131", "HTTP-POST is not supported"),
    INSTANTIATION_FAILED("SAM-65132", "Error while instantiation"),
    UNABLE_TO_RETRIEVE_BUILDER_FOR_OBJECT_QNAME("SAM-65133",
            "Unable to retrieve builder for object QName %s");

    private final String code;
    private final String message;

    /**
     * Create an Error Message.
     *
     * @param code    Relevant error code.
     * @param message Relevant error message.
     */
    ErrorMessages(String code, String message) {

        this.code = code;
        this.message = message;
    }

    /**
     * To get the code of specific error.
     *
     * @return Error code.
     */
    public String getCode() {

        return code;
    }

    /**
     * To get the message of specific error.
     *
     * @return Error message.
     */
    public String getMessage() {

        return message;
    }

    @Override
    public String toString() {

        return String.format("%s - %s", code, message);
    }
} public class SSOUtils {

    private SSOUtils() {

    }

    private static final Log log = LogFactory.getLog(SSOUtils.class);

    /**
     * Generates a unique Id for Authentication Requests
     *
     * @return generated unique ID
     */

    public static String createID() {

        RandomIdentifierGenerationStrategy generator = new RandomIdentifierGenerationStrategy();
        return generator.generateIdentifier();

    }

    /**
     * Sign the SAML Logout Response.
     *
     * @param response           Generic xml request.
     * @param signatureAlgorithm Signature algorithm.
     * @param digestAlgorithm    Cryptographic hash algorithm.
     * @param includeCert        Whether to include certificate.
     * @param cred               X509credential instance.
     * @return LogoutResponse    Signed XML object.
     * @throws SAMLSSOException  If unable to set signature.
     */
    public static void setSignature(LogoutResponse response, String signatureAlgorithm, String digestAlgorithm,
                                    boolean includeCert, X509Credential cred) throws SAMLSSOException {

        doSetSignature(response, signatureAlgorithm, digestAlgorithm, includeCert, cred);
    }

    /**
     * Sign the SAML Request message.
     *
     * @param request              Generic xml request.
     * @param signatureAlgorithm   Signature algorithm.
     * @param digestAlgorithm      Cryptographic hash algorithm.
     * @param includeCert          Whether to include certificate.
     * @param cred                 X509credential instance.
     * @return RequestAbstractType Signed XML object.
     * @throws SAMLSSOException    If unable to set signature.
     */
    public static void setSignature(RequestAbstractType request, String signatureAlgorithm, String
            digestAlgorithm, boolean includeCert, X509Credential cred) throws SAMLSSOException {

        doSetSignature(request, signatureAlgorithm, digestAlgorithm, includeCert, cred);
    }

    /**
     * Generic method to sign SAML2.0 Assertion or Response.
     *
     * @param request            Generic xml request.
     * @param signatureAlgorithm Signature algorithm.
     * @param digestAlgorithm    Cryptographic hash algorithm.
     * @param includeCert        Whether to include certificate.
     * @param x509Credential     X509credential instance.
     * @return SignableXMLObject Signed XML object.
     * @throws SAMLSSOException  If unable to set signature.
     */
    public static void doSetSignature(SignableXMLObject request, String signatureAlgorithm,
                                      String digestAlgorithm, boolean includeCert, X509Credential x509Credential)
            throws SAMLSSOException {
        
        if (request == null) {
            throw new IllegalArgumentException("Request cannot be null");
        }
        if (x509Credential == null) {
            throw new IllegalArgumentException("X509Credential cannot be null");
        }
        if (x509Credential.getEntityCertificate() == null) {
            throw new SAMLSSOException(
                    ErrorMessages.IDP_CERTIFICATE_MISSING.getCode(),
                    ErrorMessages.IDP_CERTIFICATE_MISSING.getMessage());
        }
        //TODO use StringUtils.isBlank
        if (StringUtils.isEmpty(signatureAlgorithm)) {
            signatureAlgorithm = IdentityApplicationManagementUtil.getXMLSignatureAlgorithms().get(
                    IdentityApplicationConstants.XML.SignatureAlgorithm.RSA_SHA1);
        }
        if (StringUtils.isEmpty(digestAlgorithm)) {
            digestAlgorithm = IdentityApplicationManagementUtil.getXMLDigestAlgorithms().get(
                    IdentityApplicationConstants.XML.DigestAlgorithm.SHA1);
        }
        
        Signature signature = (Signature) buildXMLObject(Signature.DEFAULT_ELEMENT_NAME);
        signature.setSigningCredential(x509Credential);
        signature.setSignatureAlgorithm(signatureAlgorithm);
        signature.setCanonicalizationAlgorithm(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);

        if (includeCert) {
                KeyInfo keyInfo = (KeyInfo) buildXMLObject(KeyInfo.DEFAULT_ELEMENT_NAME);
                X509Data data = (X509Data) buildXMLObject(X509Data.DEFAULT_ELEMENT_NAME);
                org.opensaml.xmlsec.signature.X509Certificate cert = (org.opensaml.xmlsec.signature.X509Certificate) buildXMLObject(org.opensaml.xmlsec.signature.X509Certificate.DEFAULT_ELEMENT_NAME);
                String value = null;
                try {
                    value = org.apache.xml.security.utils.Base64.encode(x509Credential
                            .getEntityCertificate().getEncoded());
                } catch (CertificateEncodingException e) {
                    throw new SAMLSSOException(ErrorMessages.RETRIEVING_THE_CERTIFICATE_FAILED.getCode(),
                            "Error getting the certificate to include in the signature", e);
                }
                cert.setValue(value);
                data.getX509Certificates().add(cert);
                keyInfo.getX509Datas().add(data);
                signature.setKeyInfo(keyInfo);
        }

        request.setSignature(signature);
        ((SAMLObjectContentReference) signature.getContentReferences().get(0))
              .setDigestAlgorithm(digestAlgorithm);
        
        List<Signature> signatureList = new ArrayList<Signature>();
        signatureList.add(signature);

        // Marshall and Sign
        MarshallerFactory marshallerFactory = XMLObjectProviderRegistrySupport.getMarshallerFactory();
        Marshaller marshaller = marshallerFactory.getMarshaller(request);
        try {
            marshaller.marshall(request);
        } catch (MarshallingException e) {
            throw new SAMLSSOException(ErrorMessages.MARSHALLING_SAML_REQUEST_FOR_SIGNING_FAILED.getCode(),
                    ErrorMessages.MARSHALLING_SAML_REQUEST_FOR_SIGNING_FAILED.getMessage(), e);
        }

        org.apache.xml.security.Init.init();
        /*
          The process mentioned below is done because OpenSAML3 does not support OSGi refer
          https://shibboleth.1660669.n2.nabble.com/Null-Pointer-Exception-from-UnmarshallerFactory-while-migrating-from-OpenSAML2-x-to-OpenSAML3-x-td7643903.html
          and https://stackoverflow.com/questions/37948303/opensaml3-resource-not-found-default-config-xml-in-osgi-container
        */
        Thread thread = Thread.currentThread();
        ClassLoader originalClassLoader = thread.getContextClassLoader();
        thread.setContextClassLoader(SignatureValidationProvider.class.getClassLoader());
        try {
            Signer.signObjects(signatureList);
        } catch (SignatureException e) {
            throw new SAMLSSOException(ErrorMessages.SIGNING_SAML_REQUEST_FAILED.getCode(),
                    ErrorMessages.SIGNING_SAML_REQUEST_FAILED.getMessage(), e);
        } finally {
            thread.setContextClassLoader(originalClassLoader);
        }
    }

  public static void addSignatureToHTTPQueryString(StringBuilder httpQueryString,
            String signatureAlgorithmURI, X509Credential credential) throws SAMLSSOException {
        try {
			httpQueryString.append("&SigAlg=");
            httpQueryString
                    .append(URLEncoder.encode(signatureAlgorithmURI, "UTF-8").trim());

            byte[] rawSignature = XMLSigningUtil.signWithURI(credential, signatureAlgorithmURI,
                    httpQueryString.toString().getBytes("UTF-8"));

            String base64Signature = new String(Base64.encodeBase64(rawSignature, false));

            if (log.isDebugEnabled()) {
                log.debug("Generated digital signature value (base64-encoded) {} " + base64Signature);
            }

            httpQueryString.append("&Signature=" + URLEncoder.encode(base64Signature, "UTF-8").trim());

        } catch (SecurityException e) {
            throw new SAMLSSOException(ErrorMessages.UNABLE_TO_SIGN_QUERY_STRING.getCode(),
                    ErrorMessages.UNABLE_TO_SIGN_QUERY_STRING.getMessage(), e);
        } catch (UnsupportedEncodingException e) {
            // UTF-8 encoding is required to be supported by all JVMs
            throw new SAMLSSOException(ErrorMessages.ADDING_SIGNATURE_TO_HTTP_QUERY_STRING_FAILED.getCode(),
                    ErrorMessages.ADDING_SIGNATURE_TO_HTTP_QUERY_STRING_FAILED.getMessage(), e);
        }
  }

    /**
     * Builds SAML Elements
     *
     * @param objectQName
     * @return
     * @throws SAMLSSOException
     */
    private static XMLObject buildXMLObject(QName objectQName) throws SAMLSSOException {
        XMLObjectBuilder builder = XMLObjectProviderRegistrySupport.getBuilderFactory()
                        .getBuilder(objectQName);
        if (builder == null) {
            throw new SAMLSSOException(ErrorMessages.UNABLE_TO_RETRIEVE_BUILDER_FOR_OBJECT_QNAME.getCode(),
                    String.format(ErrorMessages.UNABLE_TO_RETRIEVE_BUILDER_FOR_OBJECT_QNAME.getMessage(), objectQName));
        }
        return builder.buildObject(objectQName.getNamespaceURI(), objectQName.getLocalPart(),
                objectQName.getPrefix());
    }

    /**
     * Decoding and deflating the encoded AuthReq
     *
     * @param encodedStr encoded AuthReq
     * @return decoded AuthReq
     */
    public static String decode(String encodedStr) throws SAMLSSOException {
        try {
            if(log.isDebugEnabled()){
                log.debug(" >> encoded string in the SSOUtils/decode : " + encodedStr);
            }
            org.apache.commons.codec.binary.Base64 base64Decoder =
                    new org.apache.commons.codec.binary.Base64();
            byte[] xmlBytes = encodedStr.getBytes("UTF-8");
            byte[] base64DecodedByteArray = base64Decoder.decode(xmlBytes);

            try {
                //TODO if the request came in POST, inflating is wrong
                Inflater inflater = new Inflater(true);
                inflater.setInput(base64DecodedByteArray);
                byte[] xmlMessageBytes = new byte[5000];
                int resultLength = inflater.inflate(xmlMessageBytes);

                if (!inflater.finished() ){
                    throw new RuntimeException("End of the compressed data stream has NOT been reached");
                }

                inflater.end();
                String decodedString = new String(xmlMessageBytes, 0, resultLength, "UTF-8");
                if (log.isDebugEnabled()) {
                    log.debug("Request message " + decodedString);
                }
                return decodedString;

            } catch (DataFormatException e) {
                ByteArrayInputStream bais = new ByteArrayInputStream(base64DecodedByteArray);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                InflaterInputStream iis = new InflaterInputStream(bais);
                byte[] buf = new byte[1024];
                int count = iis.read(buf);
                while (count != -1) {
                    baos.write(buf, 0, count);
                    count = iis.read(buf);
                }
                iis.close();
                String decodedStr = new String(baos.toByteArray(), Charset.forName("UTF-8"));
                if (log.isDebugEnabled()) {
                    log.debug("Request message " + decodedStr);
                }
                return decodedStr;
            }
        } catch (IOException e) {
            throw new SAMLSSOException(ErrorMessages.IO_ERROR.getCode(), "Error when decoding the SAML Request.", e);
        }

    }

    public static String decodeForPost(String encodedStr)
            throws SAMLSSOException {
        try {
            org.apache.commons.codec.binary.Base64 base64Decoder = new org.apache.commons.codec.binary.Base64();
            byte[] xmlBytes = encodedStr.getBytes("UTF-8");
            byte[] base64DecodedByteArray = base64Decoder.decode(xmlBytes);

            String decodedString = new String(base64DecodedByteArray, "UTF-8");
            if (log.isDebugEnabled()) {
                log.debug("Request message " + decodedString);
            }
            return decodedString;

        } catch (IOException e) {
            throw new SAMLSSOException(ErrorMessages.IO_ERROR.getCode(), "Error when decoding the SAML Request.", e);
        }
    }

    /**
     * Serializing a SAML2 object into a String
     *
     * @param xmlObject object that needs to serialized.
     * @return serialized object
     * @throws SAMLSSOException
     */
    public static String marshall(XMLObject xmlObject) throws SAMLSSOException {
        try {

            System.setProperty("javax.xml.parsers.DocumentBuilderFactory",
                    "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");

            MarshallerFactory marshallerFactory = XMLObjectProviderRegistrySupport.getMarshallerFactory();
            Marshaller marshaller = marshallerFactory.getMarshaller(xmlObject);
            Element element = marshaller.marshall(xmlObject);

            ByteArrayOutputStream byteArrayOutputStrm = new ByteArrayOutputStream();
            DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
            DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS");
            LSSerializer writer = impl.createLSSerializer();
            LSOutput output = impl.createLSOutput();
            output.setByteStream(byteArrayOutputStrm);
            writer.write(element, output);
            return byteArrayOutputStrm.toString();
        } catch (Exception e) {
            log.error("Error Serializing the SAML Response");
            throw new SAMLSSOException(ErrorMessages.IO_ERROR.getCode(), "Error Serializing the SAML Response", e);
        }
    }

    /**
     * Unmarshalling a String into a SAML2 object
     *
     * @param samlString marshalled String
     * @return unmarshalled object
     * @throws SAMLSSOException
     */
    public static XMLObject unmarshall(String samlString) throws SAMLSSOException {

        try {
            DocumentBuilderFactory documentBuilderFactory = IdentityUtil.getSecuredDocumentBuilderFactory();
            documentBuilderFactory.setIgnoringComments(true);
            Document document = getDocument(documentBuilderFactory, samlString);
            if (isSignedWithComments(document)) {
                documentBuilderFactory.setIgnoringComments(false);
                document = getDocument(documentBuilderFactory, samlString);
            }
            Element element = document.getDocumentElement();
            UnmarshallerFactory unmarshallerFactory = XMLObjectProviderRegistrySupport.getUnmarshallerFactory();
            Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(element);
            return unmarshaller.unmarshall(element);
        } catch (ParserConfigurationException | UnmarshallingException | SAXException | IOException e) {
            throw new SAMLSSOException(ErrorMessages.UNMARSHALLING_SAML_REQUEST_ENCODED_STRING_FAILED.getCode(),
                    ErrorMessages.UNMARSHALLING_SAML_REQUEST_ENCODED_STRING_FAILED.getMessage(), e);
        }

    }

    /**
     * Encoding the response
     *
     * @param xmlString String to be encoded
     * @return encoded String
     */
    public static String encode(String xmlString) {
        String encodedRequestMessage = new String(org.apache.commons.codec.binary.Base64.encodeBase64(xmlString.getBytes(), false));
        return encodedRequestMessage.trim();
    }

    public static boolean isAuthnRequestSigned(Map<String, String> properties) {
        if (properties != null) {
            String prop = properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.IS_AUTHN_REQ_SIGNED);
            if (prop != null) {
                return Boolean.parseBoolean(prop);
            }
        }
        return false;
    }

    public static boolean isLogoutEnabled(Map<String, String> properties) {
        if (properties != null) {
            String prop = properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.IS_LOGOUT_ENABLED);
            if (prop != null) {
                return Boolean.parseBoolean(prop);
            }
        }
        return false;
    }

    public static boolean isLogoutRequestSigned(Map<String, String> properties) {
        if (properties != null) {
            String prop = properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.IS_LOGOUT_REQ_SIGNED);
            if (prop != null) {
                return Boolean.parseBoolean(prop);
            }
        }
        return false;
    }

    public static boolean isAuthnResponseSigned(Map<String, String> properties) {
        if (properties != null) {
            String prop = properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.IS_AUTHN_RESP_SIGNED);
            if (prop != null) {
                return Boolean.parseBoolean(prop);
            }
        }
        return false;
    }

    public static boolean isAssertionSigningEnabled(Map<String, String> properties) {
        if (properties != null) {
            String prop = properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.IS_ENABLE_ASSERTION_SIGNING);
            if (prop != null) {
                return Boolean.parseBoolean(prop);
            }
        }
        return false;
    }

    public static boolean isAssertionEncryptionEnabled(Map<String, String> properties) {
        if (properties != null) {
            String prop = properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.IS_ENABLE_ASSERTION_ENCRYPTION);
            if (prop != null) {
                return Boolean.parseBoolean(prop);
            }
        }
        return false;
    }

    public static boolean isArtifactResolveReqSigningEnabled(Map<String, String> properties) {

        if (properties != null) {
            String prop = properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.IS_ARTIFACT_RESOLVE_REQ_SIGNED);
            if (StringUtils.isNotBlank((prop))) {
                return Boolean.parseBoolean(prop);
            }
        }
        return false;
    }

    public static boolean isArtifactResponseSigningEnabled(Map<String, String> properties) {

        if (properties != null) {
            String prop = properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.IS_ARTIFACT_RESPONSE_SIGNED);
            if (StringUtils.isNotBlank((prop))) {
                return Boolean.parseBoolean(prop);
            }
        }
        return false;
    }

    public static String getArtifactResolveUrl(Map<String, String> properties) {

        String artifactResolveUrl = null;
        if (properties != null) {
            artifactResolveUrl = properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.ARTIFACT_RESOLVE_URL);
            if (log.isDebugEnabled()) {
                log.debug("Artifact Resolution Service Url: " + artifactResolveUrl);
            }
        }
        return artifactResolveUrl;
    }

    public static String getSignatureAlgorithm(Map<String, String> properties) {

        String signatureAlgo = null;
        if (properties != null) {
            signatureAlgo = properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.SIGNATURE_ALGORITHM);
        }
        if (StringUtils.isEmpty(signatureAlgo)) {
            signatureAlgo = IdentityApplicationConstants.XML.SignatureAlgorithm.RSA_SHA1;
        }
        signatureAlgo = IdentityApplicationManagementUtil.getXMLSignatureAlgorithms().get(signatureAlgo);
        if (log.isDebugEnabled()) {
            log.debug("Signature Algorithm: " + signatureAlgo);
        }
        return signatureAlgo;
    }

    public static String getDigestAlgorithm(Map<String, String> properties) {

        String digestAlgo = null;
        if (properties != null) {
            digestAlgo = properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.DIGEST_ALGORITHM);
        }
        if (StringUtils.isEmpty(digestAlgo)) {
            digestAlgo = IdentityApplicationConstants.XML.DigestAlgorithm.SHA1;
        }
        digestAlgo = IdentityApplicationManagementUtil.getXMLDigestAlgorithms().get(digestAlgo);
        if (log.isDebugEnabled()) {
            log.debug("Digest Algorithm: " + digestAlgo);
        }
        return digestAlgo;
    }

    public static String getSPEntityID(Map<String, String> properties) {

        String spEntityID = null;
        if (properties != null) {
            spEntityID = properties.get(IdentityApplicationConstants.Authenticator.SAML2SSO.SP_ENTITY_ID);
            if (log.isDebugEnabled()) {
                log.debug("SP Entity ID: " + spEntityID);
            }
        }
        return spEntityID;
    }

    public static Map<String, String> getQueryMap(String query) {

        Map<String, String> map = new HashMap<>();
        if (StringUtils.isNotBlank(query)) {
            String[] params = query.split("&");
            for (String param : params) {
                String[] paramSplitArr = param.split("=");
                String name = paramSplitArr[0];
                String value = "";
                if (paramSplitArr.length > 1) {
                    value = paramSplitArr[1];
                }
                map.put(name, value);
            }
        }
        return map;
    }

    /**
     * Return whether SAML Assertion has the canonicalization method
     * set to 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments'.
     *
     * @param document
     * @return true if canonicalization method equals to 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments'
     */
    private static boolean isSignedWithComments(Document document) {

        XPath xPath = XPathFactory.newInstance().newXPath();
        try {
            String assertionId = (String) xPath.compile("//*[local-name()='Assertion']/@ID")
                    .evaluate(document, XPathConstants.STRING);

            if (StringUtils.isBlank(assertionId)) {
                return false;
            }

            NodeList nodeList = ((NodeList) xPath.compile(
                    "//*[local-name()='Assertion']" +
                            "/*[local-name()='Signature']" +
                            "/*[local-name()='SignedInfo']" +
                            "/*[local-name()='Reference'][@URI='#" + assertionId + "']" +
                            "/*[local-name()='Transforms']" +
                            "/*[local-name()='Transform']" +
                            "[@Algorithm='http://www.w3.org/2001/10/xml-exc-c14n#WithComments']")
                    .evaluate(document, XPathConstants.NODESET));
            return nodeList != null && nodeList.getLength() > 0;
        } catch (XPathExpressionException e) {
            String message = "Failed to find the canonicalization algorithm of the assertion. Defaulting to: " +
                    "http://www.w3.org/2001/10/xml-exc-c14n#";
            log.warn(message);
            if (log.isDebugEnabled()) {
                log.debug(message, e);
            }
            return false;
        }
    }

    private static Document getDocument(DocumentBuilderFactory documentBuilderFactory, String samlString)
            throws IOException, SAXException, ParserConfigurationException {

        DocumentBuilder docBuilder = documentBuilderFactory.newDocumentBuilder();
        ByteArrayInputStream inputStream = new ByteArrayInputStream(samlString.getBytes());
        return docBuilder.parse(inputStream);
    }

    /**
     * Load a keystore from a given path.
     *
     * @param keyStorePath Path to the keystore file.
     * @param password     Password of the keystore.
     * @param type         Type of the keystore.
     * @return
     */
    public static KeyStore loadKeyStoreFromFileSystem(String keyStorePath, String password, String type) {

        try (FileInputStream inputStream = new FileInputStream(keyStorePath)) {
            KeyStore keyStore = KeyStore.getInstance(type);
            keyStore.load(inputStream, password.toCharArray());
            return keyStore;
        } catch (KeyStoreException e1) {
            throw new java.lang.SecurityException("Could not get a keystore instance of type: " + type + ": " + e1);
        } catch (IOException e2) {
            throw new java.lang.SecurityException("Could not open keystore in path: " + keyStorePath + ": " + e2);
        } catch (CertificateException | NoSuchAlgorithmException e3) {
            throw new java.lang.SecurityException("Error in loading keystore in path: " + keyStorePath + ": " + e3);
        }
    }
} public static final String HTTP_POST_PARAM_SAML2_ARTIFACT_ID = "SAMLart"; public static final String HTTP_POST_PARAM_SAML2_RESP = "SAMLResponse";
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.owasp.encoder.Encode;
import org.wso2.carbon.identity.application.authentication.framework.AbstractApplicationAuthenticator;
import org.wso2.carbon.identity.application.authentication.framework.AuthenticatorStateInfo;
import org.wso2.carbon.identity.application.authentication.framework.FederatedApplicationAuthenticator;
import org.wso2.carbon.identity.application.authentication.framework.context.AuthenticationContext;
import org.wso2.carbon.identity.application.authentication.framework.exception.AuthenticationFailedException;
import org.wso2.carbon.identity.application.authentication.framework.exception.LogoutFailedException;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticationContextProperty;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants;
import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils;
import org.wso2.carbon.identity.application.authenticator.samlsso.exception.SAMLSSOException;
import org.wso2.carbon.identity.application.authenticator.samlsso.internal.SAMLSSOAuthenticatorServiceComponent;
import org.wso2.carbon.identity.application.authenticator.samlsso.manager.DefaultSAML2SSOManager;
import org.wso2.carbon.identity.application.authenticator.samlsso.manager.SAML2SSOManager;
import org.wso2.carbon.identity.application.authenticator.samlsso.model.StateInfo;
import org.wso2.carbon.identity.application.authenticator.samlsso.util.SSOConstants;
import org.wso2.carbon.identity.application.authenticator.samlsso.util.SSOErrorConstants.ErrorMessages;
import org.wso2.carbon.identity.application.authenticator.samlsso.util.SSOUtils;
import org.wso2.carbon.identity.application.common.model.ClaimMapping;
import org.wso2.carbon.identity.application.common.model.Property;
import org.wso2.carbon.identity.application.common.model.SubProperty;
import org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static org.wso2.carbon.identity.application.authenticator.samlsso.util.SSOConstants.HTTP_POST_PARAM_SAML2_ARTIFACT_ID;
import static org.wso2.carbon.identity.application.authenticator.samlsso.util.SSOConstants.HTTP_POST_PARAM_SAML2_RESP;
import static org.wso2.carbon.identity.base.IdentityConstants.FEDERATED_IDP_SESSION_ID;
/*
 * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. 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.wso2.carbon.identity.application.authenticator.samlsso;





public class SAMLSSOAuthenticator extends AbstractApplicationAuthenticator
        implements FederatedApplicationAuthenticator {

    private static final long serialVersionUID = -8097512332218044859L;
    public static final String AS_REQUEST = "AS_REQUEST";
    public static final String AUTHENTICATION_CONTEXT = "AUTHENTICATION_CONTEXT";

    private static final String AS_RESPONSE = "AS_RESPONSE";
    private static final String AUTH_PARAM = "$authparam";
    private static final String DYNAMIC_AUTH_PARAMS_LOOKUP_REGEX = "\\$authparam\\{(\\w+)}";
    private static final Pattern authParamDynamicQueryPattern = Pattern.compile(DYNAMIC_AUTH_PARAMS_LOOKUP_REGEX);

    private static final Log log = LogFactory.getLog(SAMLSSOAuthenticator.class);

    @Override
    public boolean canHandle(HttpServletRequest request) {

        if (log.isTraceEnabled()) {
            log.trace("Inside canHandle()");
        }

        return request.getParameter(HTTP_POST_PARAM_SAML2_RESP) != null ||
                request.getParameter(HTTP_POST_PARAM_SAML2_ARTIFACT_ID) != null;