/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2024 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 static org.jboss.installer.postinstall.ldap.LdapModel.LDAP_PASSWORD_KEY;
import static org.jboss.installer.postinstall.task.secdom.CertificateConfig.TRUST_STORE_PASSWORD_KEY;
import static org.jboss.installer.test.utils.TestServer.TARGET_PATH;
import static org.junit.Assert.assertEquals;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;

import org.apache.commons.io.FileUtils;
import org.jboss.installer.auto.InstallationDataSerializer;
import org.jboss.installer.core.InstallationData;
import org.jboss.installer.postinstall.ldap.LdapModel;
import org.jboss.installer.postinstall.task.secdom.CertificateConfig;
import org.jboss.installer.postinstall.task.secdom.JdbcConfig;
import org.jboss.installer.postinstall.task.secdom.KerberosConfig;
import org.jboss.installer.postinstall.task.secdom.PropertiesFileConfig;
import org.jboss.installer.test.utils.MockLanguageUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

public class SecurityDomainDescriptorTest {

    public static final String A_PASSWORD = "secret";

    @Rule
    public TemporaryFolder tempFolder = new TemporaryFolder();
    private InstallationData idata;
    private File keyTabFile;
    private File krb5ConfFile;

    @Before
    public void setup() throws Exception {
        idata = new InstallationData();
        idata.setTargetFolder(TARGET_PATH);

        keyTabFile = tempFolder.newFile("http.keytab");
        krb5ConfFile = tempFolder.newFile("krb5.conf");
    }

    @Test
    public void certificateConfigSerializationTest() throws Exception {
        final CertificateConfig certificateConfig = defaultCertificateConfig();
        final InstallationDataSerializer serializer = new InstallationDataSerializer(new MockLanguageUtils());
        final Path outputFile = tempFolder.newFile().toPath();

        serializer.serialize(idata, outputFile);
        final File variableFile = outputFile.getParent().resolve(outputFile.getFileName().toString() + ".variables").toFile();
        FileUtils.writeStringToFile(variableFile, TRUST_STORE_PASSWORD_KEY + "=" + A_PASSWORD, "UTF-8");
        final SecurityDomainConfig deserializedConfig = serializer.deserialize(outputFile, Optional.empty()).getConfig(SecurityDomainConfig.class);

        assertEquals(certificateConfig, deserializedConfig.getCertConfig());
    }

    @Test
    public void testSerializeJDBCConfig() throws Exception {
        final JdbcConfig jdbcFileConfig = new JdbcConfig();
        jdbcFileConfig.setSaltEncoding("UTF-8");
        jdbcFileConfig.setHashEncoding("ASCII");
        jdbcFileConfig.setEncodingAlgorithm("hex");
        jdbcFileConfig.setGroupIndex("2");
        jdbcFileConfig.setPasswordIndex("1");
        jdbcFileConfig.setDatasourceName("test-ds");
        jdbcFileConfig.setMapperType(JdbcConfig.MapperType.Digest);
        jdbcFileConfig.setSqlQuery("Select * from Test");
        final SecurityDomainConfig config = new SecurityDomainConfig();
        config.setDomainName("TestSD");
        config.setJdbcFileConfig(jdbcFileConfig);
        idata.putConfig(config);

        final InstallationDataSerializer serializer = new InstallationDataSerializer(new MockLanguageUtils());
        final Path outputFile = tempFolder.newFile().toPath();
        serializer.serialize(idata, outputFile);

        final SecurityDomainConfig deserializedConfig = serializer.deserialize(outputFile, Optional.empty()).getConfig(SecurityDomainConfig.class);

        assertEquals(jdbcFileConfig, deserializedConfig.getJdbcConfig());
    }

    @Test
    public void testSerializeKerberosConfig() throws Exception {
        final KerberosConfig kerberosConfig2 = new KerberosConfig();
        kerberosConfig2.setKerberosSecurityFactoryName("krbSF");
        kerberosConfig2.setPrincipal("HTTP/host@REALM");
        kerberosConfig2.setKeyTabPath(keyTabFile.toPath());
        kerberosConfig2.setMechanismNames(new String[]{"KRB5", "SPNEGO"});
        kerberosConfig2.setMechanismOids(new String[]{"1.2.840.113554.1.2.2", "1.3.6.1.5.5.2"});
        kerberosConfig2.setKrb5Conf(krb5ConfFile.toPath());
        kerberosConfig2.setFileSystemRealmName("exampleFsRealm");
        kerberosConfig2.setFileSystemRealmPath("fs-realm-users");
        kerberosConfig2.setFileSystemRealmStandalonePath(Paths.get(TARGET_PATH.toAbsolutePath().toString(), File.separator, "standalone", File.separator, "configuration", File.separator, "fs-realm-users"));
        kerberosConfig2.setFileSystemRealmDomainPath(Paths.get(TARGET_PATH.toAbsolutePath().toString(), File.separator, "domain", File.separator, "configuration", File.separator, "fs-realm-users"));
        kerberosConfig2.setIdentityName("user1@REALM");
        kerberosConfig2.setIdentityAttributeName("Roles");
        kerberosConfig2.setIdentityAttributeValue(new String[]{"Admin", "Guest"});
        kerberosConfig2.setSimpleRoleDecoderName("from-roles-attribute");
        kerberosConfig2.setSimpleRoleDecoderAttribute("Roles");
        KerberosConfig kerberosConfig = kerberosConfig2;
        final SecurityDomainConfig config = new SecurityDomainConfig();
        config.setDomainName("TestSD");
        final KerberosConfig kerberosConfig1 = new KerberosConfig();
        kerberosConfig1.setKerberosSecurityFactoryName("krbSF");
        kerberosConfig1.setPrincipal("HTTP/host@REALM");
        kerberosConfig1.setKeyTabPath(keyTabFile.toPath());
        kerberosConfig1.setMechanismNames(new String[]{"KRB5", "SPNEGO"});
        kerberosConfig1.setMechanismOids(new String[]{"1.2.840.113554.1.2.2", "1.3.6.1.5.5.2"});
        kerberosConfig1.setKrb5Conf(krb5ConfFile.toPath());
        kerberosConfig1.setFileSystemRealmName("exampleFsRealm");
        kerberosConfig1.setFileSystemRealmPath("fs-realm-users");
        kerberosConfig1.setFileSystemRealmStandalonePath(Paths.get(TARGET_PATH.toAbsolutePath().toString(), File.separator, "standalone", File.separator, "configuration", File.separator, "fs-realm-users"));
        kerberosConfig1.setFileSystemRealmDomainPath(Paths.get(TARGET_PATH.toAbsolutePath().toString(), File.separator, "domain", File.separator, "configuration", File.separator, "fs-realm-users"));
        kerberosConfig1.setIdentityName("user1@REALM");
        kerberosConfig1.setIdentityAttributeName("Roles");
        kerberosConfig1.setIdentityAttributeValue(new String[]{"Admin", "Guest"});
        kerberosConfig1.setSimpleRoleDecoderName("from-roles-attribute");
        kerberosConfig1.setSimpleRoleDecoderAttribute("Roles");
        config.setKerberosConfig(kerberosConfig1);
        idata.putConfig(config);
        final InstallationDataSerializer serializer = new InstallationDataSerializer(new MockLanguageUtils());
        final Path outputFile = tempFolder.newFile().toPath();
        serializer.serialize(idata, outputFile);

        final SecurityDomainConfig deserializedConfig = serializer.deserialize(outputFile, Optional.empty()).getConfig(SecurityDomainConfig.class);
        assertEquals(kerberosConfig, deserializedConfig.getKerberosConfig());
    }

    @Test
    public void testSerializeLdapConfig() throws Exception {
        final LdapModel ldapModel = new LdapModel();
        ldapModel.setRealmName("test-realm");
        ldapModel.setRole("ou=Roles,dc=example,dc=org", "(&(objectClass=groupOfNames)(member={1}))", "cn");
        ldapModel.setConnection("test-name", "ldap://127.0.0.1:10389", "uid=admin,ou=system", A_PASSWORD);
        ldapModel.setUserFilter(LDAPManagementAuthConfig.FilterType.USERNAME, "ou=Users,dc=example,dc=org", "uid", "userPassword", true);
        final SecurityDomainConfig config = new SecurityDomainConfig();
        config.setDomainName("TestSD");
        config.setLdapConfig(ldapModel);
        idata.putConfig(config);

        final InstallationDataSerializer serializer = new InstallationDataSerializer(new MockLanguageUtils());
        final Path outputFile = tempFolder.newFile().toPath();
        serializer.serialize(idata, outputFile);

        final File variableFile = outputFile.getParent().resolve(outputFile.getFileName().toString() + ".variables").toFile();
        FileUtils.writeStringToFile(variableFile, LDAP_PASSWORD_KEY + "=" + A_PASSWORD, "UTF-8");
        final SecurityDomainConfig deserializedConfig = serializer.deserialize(outputFile, Optional.of(variableFile.toPath())).getConfig(SecurityDomainConfig.class);

        assertEquals(ldapModel, deserializedConfig.getLdapConfig());
    }

    @Test
    public void testSerializePropertiesConfig() throws Exception {
        final PropertiesFileConfig propertiesFileConfig = new PropertiesFileConfig();
        propertiesFileConfig.setPlainText(false);
        propertiesFileConfig.setUsersFilePath(TARGET_PATH.resolve("standalone").resolve("configuration").resolve("test-users.properties"));
        propertiesFileConfig.setGroupsFilePath(TARGET_PATH.resolve("standalone").resolve("configuration").resolve("test-groups.properties"));
        propertiesFileConfig.setHashCharset("UTF-8");
        propertiesFileConfig.setHashEncoding("base64");
        final SecurityDomainConfig config = new SecurityDomainConfig();
        config.setDomainName("TestSD");
        config.setPropertiesFileConfig(propertiesFileConfig);
        idata.putConfig(config);

        final InstallationDataSerializer serializer = new InstallationDataSerializer(new MockLanguageUtils());
        final Path outputFile = tempFolder.newFile().toPath();
        serializer.serialize(idata, outputFile);

        final SecurityDomainConfig deserializedConfig = serializer.deserialize(outputFile, Optional.empty()).getConfig(SecurityDomainConfig.class);

        assertEquals(propertiesFileConfig, deserializedConfig.getPropertiesFileConfig());
    }

    private SecurityDomainConfig defaultConfig() {
        final SecurityDomainConfig config = new SecurityDomainConfig();
        idata.putConfig(config);
        config.setDomainName("TestSD");

        final KerberosConfig kerberosConfig = new KerberosConfig();
        kerberosConfig.setKerberosSecurityFactoryName("krbSF");
        kerberosConfig.setPrincipal("HTTP/host@REALM");
        kerberosConfig.setKeyTabPath(keyTabFile.toPath());
        kerberosConfig.setMechanismNames(new String[]{"KRB5", "SPNEGO"});
        kerberosConfig.setMechanismOids(new String[]{"1.2.840.113554.1.2.2", "1.3.6.1.5.5.2"});
        kerberosConfig.setKrb5Conf(krb5ConfFile.toPath());
        kerberosConfig.setFileSystemRealmName("exampleFsRealm");
        kerberosConfig.setFileSystemRealmPath("fs-realm-users");
        kerberosConfig.setFileSystemRealmStandalonePath(Paths.get(TARGET_PATH.toAbsolutePath().toString(), File.separator, "standalone", File.separator, "configuration", File.separator, "fs-realm-users"));
        kerberosConfig.setFileSystemRealmDomainPath(Paths.get(TARGET_PATH.toAbsolutePath().toString(), File.separator, "domain", File.separator, "configuration", File.separator, "fs-realm-users"));
        kerberosConfig.setIdentityName("user1@REALM");
        kerberosConfig.setIdentityAttributeName("Roles");
        kerberosConfig.setIdentityAttributeValue(new String[]{"Admin", "Guest"});
        kerberosConfig.setSimpleRoleDecoderName("from-roles-attribute");
        kerberosConfig.setSimpleRoleDecoderAttribute("Roles");

        config.setKerberosConfig(kerberosConfig);
        return config;
    }

    private CertificateConfig defaultCertificateConfig() throws IOException {
        final SecurityDomainConfig config = new SecurityDomainConfig();
        idata.putConfig(config);
        config.setDomainName("TestSD");
        final CertificateConfig certConfig = new CertificateConfig();
        config.setCertConfig(certConfig);

        certConfig.setApplicationDomainName("TestSD");
        certConfig.setFilterExpression("2.5.4.3");
        certConfig.setMaximumSegments(1);
        certConfig.setStartSegments(0);
        certConfig.setRoles(new String[]{"Admin", "Guest"});
        certConfig.setTrustStorePath(tempFolder.newFile().toPath());
        certConfig.setTrustStorePassword(A_PASSWORD);
        certConfig.setUseOid(true);
        return certConfig;
    }
}
