/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.sling.auth.saml2;

import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.auth.core.AuthenticationSupport;
import org.apache.sling.auth.core.spi.AuthenticationFeedbackHandler;
import org.apache.sling.auth.core.spi.AuthenticationHandler;
import org.apache.sling.auth.core.spi.AuthenticationInfo;
import org.apache.sling.testing.paxexam.SlingOptions;
import org.apache.sling.testing.paxexam.TestSupport;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.ops4j.pax.exam.Configuration;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.junit.PaxExam;
import org.ops4j.pax.exam.options.ModifiableCompositeOption;
import org.ops4j.pax.exam.options.extra.VMOption;
import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
import org.ops4j.pax.exam.spi.reactors.PerClass;
import org.ops4j.pax.exam.util.Filter;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.http.HttpService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Iterator;
import java.util.List;
import static org.apache.sling.auth.core.spi.AuthenticationHandler.REQUEST_LOGIN_PARAMETER;
import static org.apache.sling.auth.saml2.impl.JKSHelper.IDP_ALIAS;
import static org.apache.sling.auth.saml2.impl.JKSHelper.SP_ALIAS;
import static org.apache.sling.testing.paxexam.SlingOptions.logback;
import static org.apache.sling.testing.paxexam.SlingOptions.slingAuthForm;
import static org.apache.sling.testing.paxexam.SlingOptions.slingQuickstartOakTar;
import static org.apache.sling.testing.paxexam.SlingOptions.versionResolver;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.when;
import static org.ops4j.pax.exam.CoreOptions.composite;
import static org.ops4j.pax.exam.CoreOptions.junitBundles;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.systemProperty;
import static org.ops4j.pax.exam.CoreOptions.vmOption;
import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.factoryConfiguration;
import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;

/**
 * PAX Exam Integration Tests for AuthenticationHandlerSaml2 and Saml2UserMgtService
 */
@RunWith(PaxExam.class)
@ExamReactorStrategy(PerClass.class)
public class SamlHandlerIT extends TestSupport {

    static int HTTP_PORT = 8080;
    static int DEST_HTTP_PORT = 8484;
    private static Logger logger = LoggerFactory.getLogger(SamlHandlerIT.class);
    ResourceResolver resourceResolver = null;
    Session session;
    JackrabbitSession jrSession;
    UserManager userManager;

    @Inject
    protected BundleContext bundleContext;

    @Inject
    protected ConfigurationAdmin configurationAdmin;

    @Inject
    AuthenticationSupport authenticationSupport;

    @Inject
    HttpService httpService;

    @Inject
    ResourceResolverFactory resolverFactory;

    @Inject
    @Filter(value = "(saml2SPEncryptAndSign=false)")
    AuthenticationHandler authHandler;

    @Inject
    @Filter(value = "(saml2SPEncryptAndSign=true)")
    AuthenticationHandler authHandlerEnc;

    @Inject
    Saml2UserMgtService saml2UserMgtService;

    @Configuration
    public Option[] configuration() {
        // Patch versions of features provided by SlingOptions
        HTTP_PORT = findFreePort();
        DEST_HTTP_PORT = findFreePort();
//        String OAK_VERSION = "1.32.0";
        String OAK_VERSION = "1.38.0";
        versionResolver.setVersion("commons-codec", "commons-codec", "1.14");
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-jackrabbit-api", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-auth-external", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-api", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-core-spi", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-commons", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "jackrabbit-jcr-commons", "2.20.0");
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-blob-plugins", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-store-spi", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-core", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-blob", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-store-composite", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-store-document", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-jcr", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-lucene", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-authorization-principalbased", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-query-spi", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-security-spi", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.jackrabbit", "oak-segment-tar", OAK_VERSION);
        SlingOptions.versionResolver.setVersion("org.apache.tika", "tika-core", "1.24");
        SlingOptions.versionResolver.setVersion("org.apache.sling", "org.apache.sling.jcr.oak.server", "1.2.10");
        SlingOptions.versionResolver.setVersion("org.apache.sling", "org.apache.sling.api", "2.23.0");
        SlingOptions.versionResolver.setVersion("org.apache.sling", "org.apache.sling.resourceresolver", "1.7.2");
        SlingOptions.versionResolver.setVersion("org.apache.sling", "org.apache.sling.scripting.core", "2.3.4");
        SlingOptions.versionResolver.setVersion("org.apache.sling", "org.apache.sling.commons.compiler", "2.4.0");
        SlingOptions.versionResolver.setVersion("org.apache.sling", "org.apache.sling.servlets.resolver", "2.7.12");
        Option[] options = new Option[]{
            systemProperty("org.osgi.service.http.port").value(String.valueOf(HTTP_PORT)),
            systemProperty("sling.home").value("./sling"),
            baseConfiguration(),
            slingQuickstart(),
            slingAuthForm(),
            failOnUnresolvedBundles(),
            mavenBundle().groupId("org.apache.jackrabbit").artifactId("oak-jackrabbit-api").version(versionResolver),
            mavenBundle().groupId("org.apache.jackrabbit").artifactId("oak-auth-external").version(versionResolver),
            mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.converter").version("1.0.14"),
            mavenBundle().groupId("org.mockito").artifactId("mockito-core").version("3.3.3"),
            mavenBundle().groupId("net.bytebuddy").artifactId("byte-buddy").version("1.10.5"),
            mavenBundle().groupId("net.bytebuddy").artifactId("byte-buddy-agent").version("1.10.5"),
            mavenBundle().groupId("org.objenesis").artifactId("objenesis").version("2.6"),
            mavenBundle().groupId("org.apache.santuario").artifactId("xmlsec").version("2.2.2"),
            mavenBundle().groupId("io.dropwizard.metrics").artifactId("metrics-core").version("4.2.2"),
            mavenBundle().groupId("org.apache.velocity").artifactId("velocity-engine-core").version("2.3"),
            mavenBundle().groupId("org.apache.commons").artifactId("commons-lang3").version("3.12.0"),
            mavenBundle().groupId("org.bouncycastle").artifactId("bcprov-jdk15on").version("1.64"),
            mavenBundle().groupId("org.bouncycastle").artifactId("bcpkix-jdk15on").version("1.64"),
            mavenBundle().groupId("org.apache.sling").artifactId("org.apache.sling.commons.compiler").version(versionResolver),
            factoryConfiguration("org.apache.sling.jcr.repoinit.RepositoryInitializer")
                .put("scripts", new String[]{"create service user saml2-user-mgt\n\n  set ACL for saml2-user-mgt\n\n  allow jcr:all on /home\n\n  end\n\n  create group sling-authors with path /home/groups/sling-authors"})
                .asOption(),
            newConfiguration("org.apache.sling.jcr.base.internal.LoginAdminWhitelist")
                .put("whitelist.bypass", "true").asOption(),
            // build artifact
            junitBundles(),
            logback(),
            optionalRemoteDebug(),
            optionalJacoco(),
            factoryConfiguration("org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended")
                .put("user.mapping", new String[]{"org.apache.sling.auth.saml2:Saml2UserMgtService=saml2-user-mgt"})
                .asOption(),
            newConfiguration("org.apache.sling.engine.impl.auth.SlingAuthenticator")
                .put("auth.annonymous", false)
                .asOption(),
            // supply the required configuration so the auth handler service will activate
            testBundle("bundle.filename"), // from TestSupport
//                urn:oid:1.2.840.113549.1.9.1=profile/email
//                urn:oid:2.5.4.4=profile/surname
//                urn:oid:2.5.4.42=profile/givenName
//                phone=profile/phone is configured but not included in assertion
            factoryConfiguration("org.apache.sling.auth.saml2.AuthenticationHandlerSAML2")
                .put("path", "/")
                .put("entityID", "http://localhost:8080/")
                .put("acsPath", "/sp/consumer")
                .put("saml2userIDAttr", "urn:oid:0.9.2342.19200300.100.1.1")
                .put("saml2userHome", "/home/users/saml")
                .put("saml2groupMembershipAttr", "urn:oid:2.16.840.1.113719.1.1.4.1.25")
                .put("syncAttrs", new String[]{"urn:oid:2.5.4.4=./profile/surname","urn:oid:2.5.4.42=./profile/givenName","phone=./profile/phone","urn:oid:1.2.840.113549.1.9.1=./profile/email"})
                .put("saml2SPEnabled", true)
                .put("saml2SPEncryptAndSign", false)
                .put("jksFileLocation", "")
                .put("jksStorePassword", "")
                .put("idpCertAlias","")
                .put("spKeysAlias","")
                .put("spKeysPassword","")
                .asOption(),
            factoryConfiguration("org.apache.sling.auth.saml2.AuthenticationHandlerSAML2")
                .put("path", "/")
                .put("entityID", "http://localhost:8080/")
                .put("acsPath", "/sp/consumer")
                .put("saml2userIDAttr", "username")
                .put("saml2userHome", "/home/users/saml")
                .put("saml2groupMembershipAttr", "groupMembership")
                .put("syncAttrs", new String[]{"urn:oid:2.5.4.4","urn:oid:2.5.4.42","phone","urn:oid:1.2.840.113549.1.9.1"})
                .put("saml2SPEnabled", true)
                .put("saml2SPEncryptAndSign", true)
                .put("jksFileLocation", "./src/test/resources/exampleSaml2.jks")
                .put("jksStorePassword", "password")
                .put("idpCertAlias",IDP_ALIAS)
                .put("spKeysAlias",SP_ALIAS)
                .put("spKeysPassword","sppassword")
                .asOption(),
        };
        return options;
    }

    /**
     * Optionally configure remote debugging on the port supplied by the "debugPort"
     * system property.
     */
    protected ModifiableCompositeOption optionalRemoteDebug() {
        VMOption option = null;
        String property = System.getProperty("debugPort");
        if (property != null) {
            option = vmOption(String.format("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=%s", property));
        }
        return composite(option);
    }

    protected ModifiableCompositeOption optionalJacoco(){
        VMOption jacocoCommand = null;
        final String jacocoOpt = System.getProperty("jacoco.command");
        if (StringUtils.isNotEmpty(jacocoOpt)) {
            jacocoCommand = new VMOption(jacocoOpt);
        }
        return composite(jacocoCommand);
    }

    protected Option slingQuickstart() {
        final String workingDirectory = workingDirectory(); // from TestSupport
        return composite(
            slingQuickstartOakTar(workingDirectory, HTTP_PORT) // from SlingOptions
        );
    }

    protected Bundle findBundle(final String symbolicName) {
        for (final Bundle bundle : bundleContext.getBundles()) {
            if (symbolicName.equals(bundle.getSymbolicName())) {
                return bundle;
            }
        }
        return null;
    }

    void logBundles() {
        for (final Bundle bundle : bundleContext.getBundles()) {
            // logs to target/test.log
            String active = bundle.getState() == Bundle.ACTIVE ? "active" : ""+bundle.getState();
            logger.info(bundle.getSymbolicName()+":"+bundle.getVersion().toString()+ "state:"+active);
        }
    }

    @Before
    public void before(){
        try {
            resourceResolver = resolverFactory.getAdministrativeResourceResolver(null);
            session = resourceResolver.adaptTo(Session.class);
            jrSession = (JackrabbitSession) session;
            userManager = jrSession.getUserManager();
        } catch (RepositoryException | LoginException e) {
            fail(e.getMessage());
        }
        saml2UserMgtService.setUp();
    }

    @After
    public void after(){
        resourceResolver.close();
        saml2UserMgtService.cleanUp();
        session = null;
        jrSession = null;
        userManager = null;
    }

    @Test
    public void test_setup(){
        assertNotNull(bundleContext);
        assertNotNull(configurationAdmin);
        assertNotNull(authenticationSupport);
        assertNotNull(httpService);
        assertNotNull(resolverFactory);
        assertNotNull(saml2UserMgtService);
        assertNotNull(authHandler);
        assertNotNull(authHandlerEnc);
        logBundles();
    }

    @Test
    public void test_samlBundleActive(){
        Bundle samlBundle = findBundle("org.apache.sling.auth.saml2");
        assertEquals(Bundle.ACTIVE, samlBundle.getState());
    }

    @Test
    public void test_userServiceSetup(){
        assertTrue(saml2UserMgtService.setUp());
    }

    @Test
    public void test_getOrCreateSamlUser(){
        saml2UserMgtService.setUp();
        Saml2User saml2User = new Saml2User();
        saml2User.setId("example-saml");
        User user = saml2UserMgtService.getOrCreateSamlUser(saml2User);
        assertNotNull(user);
        assertTrue(saml2UserMgtService.updateUserProperties(saml2User));
        try {
            user.getPath().startsWith("/home/users/saml");
        } catch (RepositoryException e) {
            fail(e.getMessage());
        }
        saml2UserMgtService.cleanUp();
    }

    @Test
    public void test_createSamlUserWithHomePath(){
        saml2UserMgtService.setUp();
        Saml2User saml2User = new Saml2User();
        saml2User.setId("example-saml");
        User user = saml2UserMgtService.getOrCreateSamlUser(saml2User,"/home/users/mypath");
        assertNotNull(user);
        try {
            user.getPath().startsWith("/home/users/mypath");
        } catch (RepositoryException e) {
            fail(e.getMessage());
        }
        saml2UserMgtService.cleanUp();
    }

    @Test
    public void test_groupMembership(){
        saml2UserMgtService.setUp();
        Saml2User saml2User = new Saml2User();
        saml2User.setId("example-saml");
        saml2User.addGroupMembership("sling-authors");
        assertTrue(saml2UserMgtService.updateGroupMembership(saml2User));
        try {
            Authorizable user = userManager.getAuthorizable("example-saml");
            Group group = (Group) userManager.getAuthorizable("sling-authors");
            // confirm that group sling-authors now has a property called managedGroup set to true
            assertTrue(group.isMember(user));
            // confirm that group sling-authors now has a member example-saml
            assertTrue(group.hasProperty("managedGroup"));
            // and is a managed group
            assertTrue(Arrays.stream(group.getProperty("managedGroup")).anyMatch(value -> true));
        } catch (RepositoryException e) {
            fail(e.getMessage());
        }
        saml2UserMgtService.cleanUp();
    }

    @Test
    public void test_request_credentials() throws IOException {
        HttpServletRequest requestIgnore = Mockito.mock(HttpServletRequest.class);
        HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
        when(requestIgnore.getParameter(REQUEST_LOGIN_PARAMETER)).thenReturn("FORM");
        // request credentials returns false when param ?sling:authRequestLogin=<something other than SAML2>
        assertFalse(authHandler.requestCredentials(requestIgnore,response));
        HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
        HttpSession httpSession = Mockito.mock(HttpSession.class);
        when(request.getRequestURL()).thenReturn(new StringBuffer("/"));
        when(request.getContextPath()).thenReturn("/");
        when(request.getMethod()).thenReturn("POST");
        when(request.getHeader("Referer")).thenReturn("/not/my/sp/consumer");
        when(request.getSession()).thenReturn(httpSession);
        // requestCredentials returns true when SAML is enabled and request is not specifying another auth handler
        assertTrue(authHandler.requestCredentials(request,response));
    }

    @Test
    public void test_encACSPath(){
        String base64EndSamlResp = " ";
        HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
        HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
        when(request.getRequestURI()).thenReturn("/sp/consumer");
        when(request.getMethod()).thenReturn("POST");
        when(request.getParameter("RelayState")).thenReturn("kojit9j9o1ff9q6vpeo8dnsfc9");
        when(request.getHeader("Origin")).thenReturn("http://localhost:8484");
        when(request.getHeader("Referer")).thenReturn("http://localhost:8484/");
        HttpSession httpSession = Mockito.mock(HttpSession.class);
        when(httpSession.getAttribute("saml2AuthInfo")).thenReturn("kojit9j9o1ff9q6vpeo8dnsfc9");
        when(httpSession.getAttribute("saml2RequestID")).thenReturn("_4e68b1b1596d142f0e2aac3624c41d1b");
        when(request.getSession(false)).thenReturn(httpSession);
        when(request.getParameter("SAMLResponse")).thenReturn(base64EndSamlResp);
        assertNull(authHandlerEnc.extractCredentials(request, response));
        ((AuthenticationFeedbackHandler)authHandler).authenticationFailed(request,response,null);
    }

    @Test
    public void test_goodLogin() throws IOException, RepositoryException {
        userManager.createGroup("all_tenants");
        userManager.createGroup("pcms-authors");
        session.save();
        String base64EndSamlResp = buildAuthResponse();
        HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
        HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
        when(request.getRequestURI()).thenReturn("/sp/consumer");
        when(request.getMethod()).thenReturn("POST");
        when(request.getParameter("RelayState")).thenReturn("ncu7lhndv8o4o096im9065ijqn");
        when(request.getHeader("Origin")).thenReturn("http://localhost:8484");
        when(request.getHeader("Referer")).thenReturn("http://localhost:8484/");
        HttpSession httpSession = Mockito.mock(HttpSession.class);
        when(httpSession.getAttribute("saml2AuthInfo")).thenReturn("ncu7lhndv8o4o096im9065ijqn");
        when(httpSession.getAttribute("saml2RequestID")).thenReturn("_f96afa62dc16b1cc99efab06db0c750d");
        when(request.getSession(false)).thenReturn(httpSession);
        when(request.getSession()).thenReturn(httpSession);
        when(request.getParameter("SAMLResponse")).thenReturn(base64EndSamlResp);
        AuthenticationInfo authenticationInfo = authHandler.extractCredentials(request, response);
        assertNotNull(authenticationInfo);
        assertTrue(((AuthenticationFeedbackHandler)authHandler).authenticationSucceeded(request,response,authenticationInfo));
        authHandler.dropCredentials(request,response);
        User user = (User) userManager.getAuthorizable("saml2Example");
        assertNotNull(user);
        // verify user properties sync
        assertEquals("saml2@example.com", user.getProperty("./profile/email")[0].getString());
        assertEquals("Saml2", user.getProperty("./profile/surname")[0].getString());
        assertEquals("Example", user.getProperty("./profile/givenName")[0].getString());
        // verify group membership
        List groups = new ArrayList<String>();
        groups.add("all_tenants");
        groups.add("authors");
        groups.add("pcms-authors");
        Iterator<Group> groupsIt = user.declaredMemberOf();
        while (groupsIt.hasNext()){
            Group group = groupsIt.next();
            assertTrue(groups.contains(group.getID()));
            // verify managedGroup flag
            assertTrue(group.getProperty("managedGroup")[0].getBoolean());
        }
        // authors group was not created initially and still does not exist
        assertNull(userManager.getAuthorizable("authors"));
    }

    String buildAuthResponse(){
        LocalDateTime dateTime = LocalDateTime.now();
        LocalDateTime notBefore = LocalDateTime.now().minusSeconds(3);
        LocalDateTime notOnOrAfter = LocalDateTime.now().plusMinutes(5);
        String currentTime = dateTime.format(DateTimeFormatter.ISO_DATE_TIME);
        String notOnOrAfterTime = notOnOrAfter.format(DateTimeFormatter.ISO_DATE_TIME);
        String notBeforeTime = notBefore.format(DateTimeFormatter.ISO_DATE_TIME);
        String samlResp0 = "<samlp:Response Destination=\"http://localhost:8080/sp/consumer\" ID=\"ID_5232eed3-8fd5-4562-92bb-69af0246c341\" InResponseTo=\"_f96afa62dc16b1cc99efab06db0c750d\" ";
        String issueInstance = "IssueInstant=\""+currentTime+"\" ";
        String samlResp1 ="Version=\"2.0\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"><saml:Issuer>http://localhost:8484/auth/realms/sling</saml:Issuer><samlp:Status><samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"/></samlp:Status><saml:Assertion ID=\"ID_c78146f1-6c37-4ad5-b2ee-0371667c3aeb\" ";
        //issueInstance
        String samlResp2 = "Version=\"2.0\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\"><saml:Issuer>http://localhost:8484/auth/realms/sling</saml:Issuer><saml:Subject><saml:NameID Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:transient\">G-212b1981-a621-4c67-84ac-cd75551a0250</saml:NameID><saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><saml:SubjectConfirmationData InResponseTo=\"_f96afa62dc16b1cc99efab06db0c750d\" ";
        String samlResp3 = "Recipient=\"http://localhost:8080/sp/consumer\"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions ";
        String notBeforeStr = "NotBefore=\""+notBeforeTime+"\" ";
        String notOnOrAfterStr = "NotOnOrAfter=\""+notOnOrAfterTime+"\" ";
        String samlResp4 = "><saml:AudienceRestriction><saml:Audience>http://localhost:8080/</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement ";
        String authInstance = "AuthnInstant=\""+notBeforeTime+"\" ";
        String session = "SessionIndex=\"4d647e71-cc20-4779-ad58-7df15816a8c5::901bf7ca-3984-4a5b-8f8b-c1c737738102\" ";
        String sessionNotOnOrAfter = "SessionNotOnOrAfter=\""+notOnOrAfterTime+"\"> ";
        String samlResp5 = "<saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement><saml:Attribute FriendlyName=\"givenName\" Name=\"urn:oid:2.5.4.42\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"> <saml:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">Example</saml:AttributeValue></saml:Attribute><saml:Attribute FriendlyName=\"lastName\" Name=\"lastName\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">Saml2</saml:AttributeValue></saml:Attribute><saml:Attribute FriendlyName=\"groupMembership\" Name=\"urn:oid:2.16.840.1.113719.1.1.4.1.25\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\"><saml:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">all_tenants</saml:AttributeValue><saml:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">authors</saml:AttributeValue><saml:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">pcms-authors</saml:AttributeValue></saml:Attribute><saml:Attribute FriendlyName=\"email\" Name=\"urn:oid:1.2.840.113549.1.9.1\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">saml2@example.com</saml:AttributeValue></saml:Attribute><saml:Attribute FriendlyName=\"userid\" Name=\"urn:oid:0.9.2342.19200300.100.1.1\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\"><saml:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">saml2Example</saml:AttributeValue></saml:Attribute><saml:Attribute FriendlyName=\"surname\" Name=\"urn:oid:2.5.4.4\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">Saml2</saml:AttributeValue></saml:Attribute><saml:Attribute Name=\"Role\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">uma_authorization</saml:AttributeValue></saml:Attribute><saml:Attribute Name=\"Role\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">offline_access</saml:AttributeValue></saml:Attribute><saml:Attribute Name=\"Role\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">view-profile</saml:AttributeValue></saml:Attribute><saml:Attribute Name=\"Role\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">manage-account-links</saml:AttributeValue></saml:Attribute><saml:Attribute Name=\"Role\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"> <saml:AttributeValue xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"xs:string\">manage-account</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion></samlp:Response>";
        String preEncoding =
                samlResp0 +
                issueInstance +
                samlResp1 +
                issueInstance +
                samlResp2 +
                notOnOrAfterStr +
                samlResp3 +
                notBeforeStr +
                notOnOrAfterStr +
                samlResp4 +
                authInstance +
                session +
                sessionNotOnOrAfter +
                samlResp5;
        return Base64.getEncoder().encodeToString(preEncoding.getBytes());
    }
}
