/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2022 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed 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.jboss.installer.postinstall.task;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.dmr.ModelNode;
import org.jboss.installer.auto.InstallationDataSerializer;
import org.jboss.installer.core.InstallationData;
import org.jboss.installer.postinstall.TaskPrinter;
import org.jboss.installer.postinstall.server.DomainServer;
import org.jboss.installer.postinstall.server.ServerOperationException;
import org.jboss.installer.postinstall.server.StandaloneServer;
import org.jboss.installer.test.utils.MockLanguageUtils;
import org.jboss.installer.test.utils.TestServer;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import static org.jboss.installer.postinstall.task.AbstractHttpsEnableTask.CLIENT_KEY_ALIAS;
import static org.jboss.installer.postinstall.task.AbstractHttpsEnableTask.SERVER_KEY_ALIAS;
import static org.jboss.installer.postinstall.task.HttpsApplicationEnableTask.CREDENTIAL_ALIAS_KEYSTORE_PASSWORD;
import static org.jboss.installer.postinstall.task.HttpsApplicationEnableTask.CREDENTIAL_ALIAS_TRUSTSTORE_PASSWORD;
import static org.jboss.installer.postinstall.task.HttpsApplicationEnableTask.Config.KEYSTORE_PASSWORD_VARIABLE_KEY;
import static org.jboss.installer.postinstall.task.HttpsApplicationEnableTask.Config.TRUSTSTORE_PASSWORD_VARIABLE_KEY;
import static org.jboss.installer.postinstall.task.HttpsApplicationEnableTask.DEFAULT_CIPHER_SUITES_TLS12;
import static org.jboss.installer.postinstall.task.HttpsApplicationEnableTask.DEFAULT_CIPHER_NAMES_TLS13;
import static org.jboss.installer.postinstall.task.HttpsApplicationEnableTask.KEYMANAGER_NAME;
import static org.jboss.installer.postinstall.task.HttpsApplicationEnableTask.KEYSTORE_NAME;
import static org.jboss.installer.postinstall.task.HttpsApplicationEnableTask.SSL_CONTEXT_NAME;
import static org.jboss.installer.postinstall.task.HttpsApplicationEnableTask.TRUSTMANAGER_NAME;
import static org.jboss.installer.postinstall.task.HttpsApplicationEnableTask.TRUSTSTORE_NAME;
import static org.jboss.installer.postinstall.task.HttpsApplicationEnableTask.PROTOCOL_TLSv12;
import static org.jboss.installer.postinstall.task.HttpsApplicationEnableTask.PROTOCOL_TLSv13;
import static org.jboss.installer.postinstall.task.HttpsEnableTaskTest.clearStateOf;
import static org.jboss.installer.postinstall.task.HttpsEnableTaskTest.getReadAliasFromKeyStoreOp;
import static org.jboss.installer.test.utils.TestServer.TARGET_PATH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

public class HttpsApplicationEnableTaskTest {
    private static final String KEYSTORE_PASSWORD = "application-keystore-secret";
    private static final String KEYSTORE_PATH = "application-test.keystore";
    private static final String TRUSTSTORE_PASSWORD = "application-truststore-secret";
    private static final String TRUSTSTORE_PATH = "application-test.truststore";
    private static final String TEST_DN = "CN=test";
    private static final String VALIDITY = "30";
    private static final String CLIENT_CERT_PATH = Paths.get("src", "test", "resources", "client.crt").toAbsolutePath().toString();
    private static final boolean WITH_CREDENTIAL_STORE = true;
    private static final boolean NO_CREDENTIAL_STORE = false;

    private static final HttpsApplicationEnableTask.Config KEYSTORE_CONFIG = new HttpsApplicationEnableTask.Config(
            Set.of(PROTOCOL_TLSv12), DEFAULT_CIPHER_SUITES_TLS12, DEFAULT_CIPHER_NAMES_TLS13, KEYSTORE_PATH, KEYSTORE_PASSWORD,
            false, null, null,
            false, null, null, null, false);
    private static final HttpsApplicationEnableTask.Config TRUSTSTORE_CONFIG = new HttpsApplicationEnableTask.Config(
            Set.of(PROTOCOL_TLSv12), DEFAULT_CIPHER_SUITES_TLS12, DEFAULT_CIPHER_NAMES_TLS13, KEYSTORE_PATH, KEYSTORE_PASSWORD,
            true, TRUSTSTORE_PATH, TRUSTSTORE_PASSWORD,
            false, null, null, null, false);
    private static final HttpsApplicationEnableTask.Config TLSv13_CONFIG = new HttpsApplicationEnableTask.Config(
            Set.of(PROTOCOL_TLSv13), DEFAULT_CIPHER_SUITES_TLS12, DEFAULT_CIPHER_NAMES_TLS13, KEYSTORE_PATH, KEYSTORE_PASSWORD,
            false, null, null,
            false, null, null, null, false);
    private static final HttpsApplicationEnableTask.Config GENERATE_KEYSTORE_CONFIG = new HttpsApplicationEnableTask.Config(
            Set.of(PROTOCOL_TLSv12), DEFAULT_CIPHER_SUITES_TLS12, DEFAULT_CIPHER_NAMES_TLS13, null, KEYSTORE_PASSWORD,
            false, null, null,
            true, TEST_DN, VALIDITY, null, false);
    private static final HttpsApplicationEnableTask.Config GENERATE_TRUSTSTORE_CONFIG = new HttpsApplicationEnableTask.Config(
            Set.of(PROTOCOL_TLSv12), DEFAULT_CIPHER_SUITES_TLS12, DEFAULT_CIPHER_NAMES_TLS13, null, KEYSTORE_PASSWORD,
            true, null, TRUSTSTORE_PASSWORD,
            true, TEST_DN, VALIDITY, CLIENT_CERT_PATH, false);
    private static final HttpsApplicationEnableTask.Config FULL_CONFIG = new HttpsApplicationEnableTask.Config(
            Set.of(PROTOCOL_TLSv13), DEFAULT_CIPHER_SUITES_TLS12, DEFAULT_CIPHER_NAMES_TLS13, KEYSTORE_PATH, KEYSTORE_PASSWORD,
            true, TRUSTSTORE_PATH, TRUSTSTORE_PASSWORD,
            true, TEST_DN, VALIDITY, CLIENT_CERT_PATH, true);

    private static final HttpsApplicationEnableTask.Config MINIMAL_CONFIG = new HttpsApplicationEnableTask.Config(
            null, null, null, KEYSTORE_PATH, KEYSTORE_PASSWORD,
            false, null, null,
            true, null, null, null, false);

    @ClassRule
    public static TestServer testServer = new TestServer();

    @Rule
    public TemporaryFolder temp = new TemporaryFolder();

    private final CredentialStoreInstallTask.Config credStoreConfig = new CredentialStoreInstallTask.Config("test-store", null, TARGET_PATH.resolve("test.store").toString(), "abcd1234");

    private StandaloneServer standaloneServer;
    private DomainServer domainServer;
    private XPath xPath =  XPathFactory.newInstance().newXPath();
    private DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    private DocumentBuilder builder;
    private InstallationData iData = new InstallationData();
    private TaskPrinter printer = new NoopPrinter();

    @Before
    public void setUp() throws Exception {
        standaloneServer = new StandaloneServer(TARGET_PATH);
        domainServer = new DomainServer(TARGET_PATH);
        builder = factory.newDocumentBuilder();

        // copy the configuration file so that we can quickly restore it between tests
        FileUtils.copyDirectory(TARGET_PATH.resolve("domain").resolve("configuration").toFile(), TARGET_PATH.resolve("domain").resolve("backup").toFile());
        FileUtils.copyDirectory(TARGET_PATH.resolve("standalone").resolve("configuration").toFile(), TARGET_PATH.resolve("standalone").resolve("backup").toFile());
    }

    @After
    public void tearDown() throws Exception {
        FileUtils.deleteDirectory(TARGET_PATH.resolve("domain").resolve("configuration").toFile());
        FileUtils.deleteDirectory(TARGET_PATH.resolve("standalone").resolve("configuration").toFile());
        FileUtils.copyDirectory(TARGET_PATH.resolve("domain").resolve("backup").toFile(), TARGET_PATH.resolve("domain").resolve("configuration").toFile());
        FileUtils.copyDirectory(TARGET_PATH.resolve("standalone").resolve("backup").toFile(), TARGET_PATH.resolve("standalone").resolve("configuration").toFile());
        FileUtils.deleteDirectory(TARGET_PATH.resolve("domain").resolve("backup").toFile());
        FileUtils.deleteDirectory(TARGET_PATH.resolve("standalone").resolve("backup").toFile());
    }

    @Test
    public void addKeystoreToStandalone() throws Exception {
        final String config = "standalone.xml";
        testStandalone(config, KEYSTORE_CONFIG, NO_CREDENTIAL_STORE);
    }

    @Test
    public void addTrustStoreToStandalone() throws Exception {
        final String config = "standalone.xml";
        testStandalone(config, TRUSTSTORE_CONFIG, NO_CREDENTIAL_STORE);
    }

    @Test
    public void addKeystoreToStandaloneWithCredentialStore() throws Exception {
        final String config = "standalone.xml";
        testStandalone(config, KEYSTORE_CONFIG, WITH_CREDENTIAL_STORE);

        // keystore password needs to be added to store only once
        testStandalone("standalone-ha.xml", KEYSTORE_CONFIG, WITH_CREDENTIAL_STORE);
    }

    @Test
    public void addTrustStoreToStandaloneWithCredentialStore() throws Exception {
        final String config = "standalone.xml";
        testStandalone(config, TRUSTSTORE_CONFIG, WITH_CREDENTIAL_STORE);

        // keystore password needs to be added to store only once
        testStandalone("standalone-ha.xml", TRUSTSTORE_CONFIG, WITH_CREDENTIAL_STORE);
    }

    @Test
    public void addKeystoreToDomainWithCredentialStore() throws Exception {
        final String config = "host.xml";
        testDomain(config, KEYSTORE_CONFIG, WITH_CREDENTIAL_STORE);
    }

    @Test
    public void addTrustStoreToDomainWithCredentialStore() throws Exception {
        final String config = "host.xml";
        testDomain(config, TRUSTSTORE_CONFIG, WITH_CREDENTIAL_STORE);
    }

    @Test
    public void addKeystoreToStandaloneFullHa() throws Exception {
        final String config = "standalone-full-ha.xml";
        testStandalone(config, KEYSTORE_CONFIG, NO_CREDENTIAL_STORE);
    }

    @Test
    public void addTrustStoreToStandaloneFullHa() throws Exception {
        final String config = "standalone-full-ha.xml";
        testStandalone(config, TRUSTSTORE_CONFIG, NO_CREDENTIAL_STORE);
    }

    @Test
    public void addKeystoreToDomain() throws Exception {
        final String config = "host.xml";

        testDomain(config, KEYSTORE_CONFIG, NO_CREDENTIAL_STORE);
    }

    @Test
    public void addTruststoreToDomain() throws Exception {
        final String config = "host.xml";

        testDomain(config, TRUSTSTORE_CONFIG, NO_CREDENTIAL_STORE);
    }

    @Test
    public void setMultipleProtocolsStandalone() throws Exception {
        final String config = "standalone.xml";
        final HttpsApplicationEnableTask.Config KEYSTORE_CONFIG = new HttpsApplicationEnableTask.Config(
                Set.of(PROTOCOL_TLSv12, PROTOCOL_TLSv13), DEFAULT_CIPHER_SUITES_TLS12, DEFAULT_CIPHER_NAMES_TLS13, KEYSTORE_PATH, KEYSTORE_PASSWORD,
                false, null, null,
                false, null, null, null, false);
        testStandalone(config, KEYSTORE_CONFIG, NO_CREDENTIAL_STORE);
    }

    @Test
    public void testSerializeDeserialize() throws Exception {
        iData.putConfig(FULL_CONFIG);
        final Path tempFile = temp.newFile("auto.xml").toPath();
        final InstallationDataSerializer serializer = new InstallationDataSerializer(new MockLanguageUtils());
        serializer.serialize(iData, tempFile);
        List<String> passwords = List.of(
                KEYSTORE_PASSWORD_VARIABLE_KEY + "=" + FULL_CONFIG.getKeyStorePassword(),
                TRUSTSTORE_PASSWORD_VARIABLE_KEY + "=" + FULL_CONFIG.getTrustStorePassword());
        FileUtils.writeLines(tempFile.getParent().resolve(tempFile.getFileName().toString() + ".variables").toFile(), "UTF-8", passwords);
        final InstallationData loadedData = serializer.deserialize(tempFile, Optional.empty());

        final HttpsApplicationEnableTask.Config config = loadedData.getConfig(HttpsApplicationEnableTask.Config.class);
        assertEquals(FULL_CONFIG, config);
    }

    @Test
    public void testSerializeDeserializeMinialConfig() throws Exception {
        iData.putConfig(MINIMAL_CONFIG);
        final Path tempFile = temp.newFile("auto.xml").toPath();
        final InstallationDataSerializer serializer = new InstallationDataSerializer(new MockLanguageUtils());
        serializer.serialize(iData, tempFile);
        List<String> passwords = List.of(
                KEYSTORE_PASSWORD_VARIABLE_KEY + "=" + FULL_CONFIG.getKeyStorePassword(),
                TRUSTSTORE_PASSWORD_VARIABLE_KEY + "=" + FULL_CONFIG.getTrustStorePassword());
        FileUtils.writeLines(tempFile.getParent().resolve(tempFile.getFileName().toString() + ".variables").toFile(), "UTF-8", passwords);
        final InstallationData loadedData = serializer.deserialize(tempFile, Optional.empty());

        final HttpsApplicationEnableTask.Config config = loadedData.getConfig(HttpsApplicationEnableTask.Config.class);
        assertEquals(MINIMAL_CONFIG, config);
    }

    @Test
    public void testTLSv13() throws Exception {
        final String config = "standalone.xml";
        testStandalone(config, TLSv13_CONFIG, NO_CREDENTIAL_STORE);
    }

    @Test
    public void testNoProtocols() throws Exception {
        final String configFile = "standalone.xml";
        final HttpsApplicationEnableTask.Config config = new HttpsApplicationEnableTask.Config(
                null, DEFAULT_CIPHER_SUITES_TLS12, DEFAULT_CIPHER_NAMES_TLS13, KEYSTORE_PATH, KEYSTORE_PASSWORD,
                false, null, null,
                false, null, null, null, false);
        testStandalone(configFile, config, NO_CREDENTIAL_STORE);
    }

    @Test
    public void generateKeystoreInStandalone() throws Exception {
        final String config = "standalone.xml";
        clearStateOf(GENERATE_KEYSTORE_CONFIG);
        testStandalone(config, GENERATE_KEYSTORE_CONFIG, NO_CREDENTIAL_STORE, KEYSTORE_NAME);
    }

    @Test
    public void generateKeystoreInStandaloneWithCredentialStore() throws Exception {
        final String config = "standalone.xml";
        clearStateOf(GENERATE_KEYSTORE_CONFIG);
        testStandalone(config, GENERATE_KEYSTORE_CONFIG, WITH_CREDENTIAL_STORE, KEYSTORE_NAME);
    }

    @Test
    public void generateKeystoreInDomain() throws Exception {
        final String config = "host.xml";
        clearStateOf(GENERATE_KEYSTORE_CONFIG);
        testDomain(config, GENERATE_KEYSTORE_CONFIG, NO_CREDENTIAL_STORE);
    }

    @Test
    public void generateTrustStoreInStandalone() throws Exception {
        final String config = "standalone.xml";
        clearStateOf(GENERATE_TRUSTSTORE_CONFIG);
        testStandalone(config, GENERATE_TRUSTSTORE_CONFIG, NO_CREDENTIAL_STORE, KEYSTORE_NAME, TRUSTSTORE_NAME);
    }

    @Test
    public void generateTrustStoreInStandaloneWithCredentialStore() throws Exception {
        final String config = "standalone.xml";
        clearStateOf(GENERATE_TRUSTSTORE_CONFIG);
        testStandalone(config, GENERATE_TRUSTSTORE_CONFIG, WITH_CREDENTIAL_STORE, KEYSTORE_NAME, TRUSTSTORE_NAME);
    }

    @Test
    public void generateTrustStoreInDomain() throws Exception {
        final String config = "host.xml";
        clearStateOf(GENERATE_TRUSTSTORE_CONFIG);
        testDomain(config, GENERATE_TRUSTSTORE_CONFIG, NO_CREDENTIAL_STORE);
    }

    private void testStandalone(String config, HttpsApplicationEnableTask.Config httpsConfig, boolean credStore) throws ServerOperationException, SAXException, IOException, XPathExpressionException, OperationFailedException {
        testStandalone(config, httpsConfig, credStore, null);
    }

    private void testStandalone(String config, HttpsApplicationEnableTask.Config httpsConfig, boolean credStore, String keyStoreNameToTest) throws ServerOperationException, SAXException, IOException, XPathExpressionException, OperationFailedException {
        testStandalone(config, httpsConfig, credStore, keyStoreNameToTest, null);
    }

    private void testStandalone(String config, HttpsApplicationEnableTask.Config httpsConfig, boolean credStore, String keyStoreNameToTest, String trustStoreNameToTest) throws ServerOperationException, SAXException, IOException, XPathExpressionException, OperationFailedException {
        iData.setTargetFolder(TARGET_PATH);
        iData.putConfig(httpsConfig);
        if (credStore) {
            iData.putConfig(credStoreConfig);
        }
        try {
            standaloneServer.start(config);

            if (credStore) {
                assertTrue(new CredentialStoreInstallTask().applyToStandalone(iData, standaloneServer, printer));
            }
            assertTrue(new HttpsApplicationEnableTask().applyToStandalone(iData, standaloneServer, printer));

            verifyStandalone(config, httpsConfig, credStore);
            if (keyStoreNameToTest != null) {
                verifyAliasInKeyStore(standaloneServer, keyStoreNameToTest, SERVER_KEY_ALIAS);
            }
            if (trustStoreNameToTest != null) {
                verifyAliasInKeyStore(standaloneServer, trustStoreNameToTest, CLIENT_KEY_ALIAS);
            }
        } finally {
            standaloneServer.shutdown();
        }
    }

    private void testDomain(String config, HttpsApplicationEnableTask.Config httpsConfig, boolean credStore) throws ServerOperationException, SAXException, IOException, XPathExpressionException, OperationFailedException {
        iData.setTargetFolder(TARGET_PATH);
        iData.putConfig(httpsConfig);
        if (credStore) {
            iData.putConfig(credStoreConfig);
        }
        try {
            domainServer.start(config);

            if (credStore) {
                assertTrue(new CredentialStoreInstallTask().applyToDomain(iData, domainServer, printer));
            }
            assertTrue(new HttpsApplicationEnableTask().applyToDomain(iData, domainServer, printer));

            verifyProfileConfig(httpsConfig, credStore);
        } finally {
            domainServer.shutdown();
        }
    }

    private void verifyStandalone(String config, HttpsApplicationEnableTask.Config httpsConfig, boolean credStore) throws SAXException, IOException, XPathExpressionException {
        Document doc = builder.parse(TARGET_PATH.resolve("standalone").resolve("configuration").resolve(config).toFile());
        final String prefix = "/";
        verifyCommonElements(doc, httpsConfig, credStore, prefix);

        // verify undefined legacy security realm
        assertNull(getAttribute(doc, "//subsystem/server[@name=\"default-server\"]/https-listener[@name=\"https\"]",
                "security-realm"));

        // verify ssl-context on https listener
        assertEquals(SSL_CONTEXT_NAME, getAttribute(doc, "//subsystem/server[@name=\"default-server\"]/https-listener[@name=\"https\"]",
                "ssl-context"));
    }

    private void verifyProfileConfig(HttpsApplicationEnableTask.Config httpsConfig, boolean credStore) throws SAXException, IOException, XPathExpressionException {
        Document doc = builder.parse(TARGET_PATH.resolve("domain").resolve("configuration").resolve("domain.xml").toFile());

        for (String profile : DomainServer.PROFILES) {
            String prefix = String.format("//profile[@name=\"%s\"]/subsystem", profile);
            verifyCommonElements(doc, httpsConfig, credStore, prefix + "/tls");

            // verify undefined legacy security realm
            assertNull(getAttribute(doc, prefix + "/server[@name=\"default-server\"]/https-listener[@name=\"https\"]",
                    "security-realm"));

            // verify ssl-context on https listener
            assertEquals(SSL_CONTEXT_NAME, getAttribute(doc, prefix + "/server[@name=\"default-server\"]/https-listener[@name=\"https\"]",
                    "ssl-context"));
        }
    }

    private void verifyCommonElements(Document doc, HttpsApplicationEnableTask.Config httpsConfig, boolean credStore, String prefix) throws XPathExpressionException {
        // verify key store
        if (!httpsConfig.isDoGenerateStore()) {
            assertEquals(httpsConfig.getKeyStorePath(), getAttribute(doc, prefix + "/key-stores/key-store[@name=\"" + KEYSTORE_NAME + "\"]/file", "path"));
        }
        // assert has alias and store
        if (credStore) {
            assertEquals("test-store",
                    getAttribute(doc, prefix + "/key-stores/key-store[@name=\"" + KEYSTORE_NAME + "\"]/credential-reference",
                            "store"));
            assertEquals(CREDENTIAL_ALIAS_KEYSTORE_PASSWORD,
                    getAttribute(doc, prefix + "/key-stores/key-store[@name=\"" + KEYSTORE_NAME + "\"]/credential-reference",
                            "alias"));
        } else {
            assertEquals(httpsConfig.getKeyStorePassword(),
                    getAttribute(doc, prefix + "/key-stores/key-store[@name=\"" + KEYSTORE_NAME + "\"]/credential-reference",
                            "clear-text"));
        }
        // verify optional trust store
        if (httpsConfig.isMutual()) {
            if (!httpsConfig.isDoGenerateStore()) {
                assertEquals(httpsConfig.getTrustStorePath(), getAttribute(doc, prefix + "/key-stores/key-store[@name=\"" + TRUSTSTORE_NAME + "\"]/file", "path"));
            }
            if (credStore) {
                assertEquals("test-store",
                        getAttribute(doc, prefix + "/key-stores/key-store[@name=\"" + TRUSTSTORE_NAME + "\"]/credential-reference",
                                "store"));
                assertEquals(CREDENTIAL_ALIAS_TRUSTSTORE_PASSWORD,
                        getAttribute(doc, prefix + "/key-stores/key-store[@name=\"" + TRUSTSTORE_NAME + "\"]/credential-reference",
                                "alias"));
            } else {
                assertEquals(httpsConfig.getTrustStorePassword(),
                        getAttribute(doc, prefix + "/key-stores/key-store[@name=\"" + TRUSTSTORE_NAME + "\"]/credential-reference",
                                "clear-text"));
            }
            assertEquals(TRUSTMANAGER_NAME,
                    getAttribute(doc, prefix + "/server-ssl-contexts/server-ssl-context[@name=\"" + SSL_CONTEXT_NAME + "\"]",
                            "trust-manager"));
            assertEquals("true",
                    getAttribute(doc, prefix + "/server-ssl-contexts/server-ssl-context[@name=\"" + SSL_CONTEXT_NAME + "\"]",
                            "need-client-auth"));
        }

        // verify key manager
        assertEquals(KEYMANAGER_NAME,
                getAttribute(doc, prefix + "/server-ssl-contexts/server-ssl-context[@name=\"" + SSL_CONTEXT_NAME + "\"]",
                        "key-manager"));

        // verify TLS protocol
        if (httpsConfig.getProtocols() != null && !httpsConfig.getProtocols().isEmpty()) {
            assertEquals(StringUtils.join(httpsConfig.getProtocols(), " "),
                    getAttribute(doc, prefix + "/server-ssl-contexts/server-ssl-context[@name=\"" + SSL_CONTEXT_NAME + "\"]",
                            "protocols"));
            if (httpsConfig.getCipherSuites() != null) {
                assertEquals(httpsConfig.getCipherSuites(),
                        getAttribute(doc, prefix + "/server-ssl-contexts/server-ssl-context[@name=\"" + SSL_CONTEXT_NAME + "\"]",
                                "cipher-suite-filter"));
            }
            if (httpsConfig.getTls13cipherNames() != null) {
                assertEquals(httpsConfig.getTls13cipherNames(),
                        getAttribute(doc, prefix + "/server-ssl-contexts/server-ssl-context[@name=\"" + SSL_CONTEXT_NAME + "\"]",
                                "cipher-suite-names"));
            }
        }
    }

    private void verifyAliasInKeyStore(StandaloneServer server, String keyStoreName, String expectedAlias) throws OperationFailedException {
        final ModelNode res = server.execute(getReadAliasFromKeyStoreOp(keyStoreName), "Read alias");
        assertEquals(1, res.asList().size());
        assertEquals(expectedAlias, res.asList().get(0).asString());
    }

    private String getAttribute(Document doc, String expression, String attrName) throws XPathExpressionException {
        NodeList nodes = (NodeList) xPath.compile(expression).evaluate(
                doc, XPathConstants.NODESET);
        final Node attr = nodes.item(0).getAttributes().getNamedItem(attrName);
        return attr == null?null:attr.getNodeValue();
    }
}
