/**
 * 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.jboss.installer.auto.InstallationDataSerializer;
import org.jboss.installer.core.InstallationData;
import org.jboss.installer.postinstall.TaskPrinter;
import org.jboss.installer.postinstall.ldap.LdapSecurity;
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 java.io.File;
import java.nio.file.Path;
import java.util.Optional;

import static org.jboss.installer.postinstall.ldap.LdapSecurity.Model.LDAP_PASSWORD_KEY;
import static org.jboss.installer.test.utils.TestServer.TARGET_PATH;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class AddLdapSecurityDomainTest {

    public static final String A_PASSWORD = "password";
    private StandaloneServer standaloneServer;
    private DomainServer domainServer;
    @ClassRule
    public static TestServer testServer = new TestServer();
    @Rule
    public TemporaryFolder tempFolder = new TemporaryFolder();
    private SecurityDomainTask task;
    private InstallationData idata;
    private TaskPrinter printer = new NoopPrinter();

    @Before
    public void setup() throws Exception {
        task = new SecurityDomainTask();
        idata = new InstallationData();
        standaloneServer = new StandaloneServer(TARGET_PATH);
        domainServer = new DomainServer(TARGET_PATH);
        idata.setTargetFolder(TARGET_PATH);
    }

    @After
    public void tearDown() {
        standaloneServer.close();
        domainServer.close();
    }

    @Test
    public void testInstallLdapSecurityDomainInStandalone() throws Exception {

        final SecurityDomainTask.Config config = new SecurityDomainTask.Config();
        config.setDomainName("TestSD");
        final LdapSecurity.Model ldapModel = new LdapSecurity.Model();
        ldapModel.setRealmName("TestSD-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(LDAPSetupTask.FilterType.USERNAME, "ou=Users,dc=example,dc=org", "uid", "userPassword", true);
        config.setLdapConfig(ldapModel);
        idata.putConfig(config);

        standaloneServer.start("standalone.xml");
        try {
            assertTrue(task.applyToStandalone(idata, standaloneServer, printer));
        } finally {
            standaloneServer.shutdown();
        }
    }

    @Test
    public void testInstallLdapSecurityDomainInDomain() throws Exception {
        testDomain("host.xml");
        testDomain(DomainServer.HOST_PRIMARY_XML);
        testDomain(DomainServer.HOST_SECONDARY_XML);
    }

    private void testDomain(String profile) throws ServerOperationException {
        final SecurityDomainTask.Config config = new SecurityDomainTask.Config();
        config.setDomainName("TestSD");
        final LdapSecurity.Model ldapModel = new LdapSecurity.Model();
        ldapModel.setRealmName("TestSD-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(LDAPSetupTask.FilterType.USERNAME, "ou=Users,dc=example,dc=org", "uid", "userPassword", true);
        config.setLdapConfig(ldapModel);
        idata.putConfig(config);

        domainServer.start(profile);
        try {
            assertTrue(task.applyToDomain(idata, domainServer, printer));
        } finally {
            domainServer.shutdown();
        }
    }

    @Test
    public void testSerializeLdapConfig() throws Exception {
        final LdapSecurity.Model ldapModel = new LdapSecurity.Model();
        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(LDAPSetupTask.FilterType.USERNAME, "ou=Users,dc=example,dc=org", "uid", "userPassword", true);
        final SecurityDomainTask.Config config = new SecurityDomainTask.Config();
        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 SecurityDomainTask.Config deserializedConfig = serializer.deserialize(outputFile, Optional.of(variableFile.toPath())).getConfig(SecurityDomainTask.Config.class);

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