/*
 * 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.ambari.server.controller.internal;

import static org.easymock.EasyMock.expect;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import org.apache.ambari.server.controller.AmbariManagementController;
import org.apache.ambari.server.controller.KerberosHelper;
import org.apache.ambari.server.controller.spi.Predicate;
import org.apache.ambari.server.controller.spi.Request;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.controller.spi.ResourceProvider;
import org.apache.ambari.server.controller.utilities.PredicateBuilder;
import org.apache.ambari.server.controller.utilities.PropertyHelper;
import org.apache.ambari.server.orm.dao.HostDAO;
import org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO;
import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO;
import org.apache.ambari.server.orm.entities.HostEntity;
import org.apache.ambari.server.orm.entities.KerberosKeytabPrincipalEntity;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Clusters;
import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosKeytabDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosPrincipalDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosPrincipalType;
import org.easymock.EasyMock;
import org.easymock.EasyMockSupport;
import org.junit.Assert;
import org.junit.Test;


/**
 * Tests for the host Kerberos identity resource provider.
 */
public class HostKerberosIdentityResourceProviderTest extends EasyMockSupport {
  @Test(expected = org.apache.ambari.server.controller.spi.SystemException.class)
  public void testCreateResources() throws Exception {
    AmbariManagementController managementController = createMock(AmbariManagementController.class);

    ResourceProvider provider = new HostKerberosIdentityResourceProvider(managementController);

    // Create a property set of an single empty map.  It shouldn't make a difference what this is
    // since this HostKerberosIdentityResourceProvider is a read-only provider and should throw
    // a org.apache.ambari.server.controller.spi.SystemException exception
    Set<Map<String, Object>> propertySet = Collections.singleton(Collections.<String, Object>emptyMap());

    Request request = PropertyHelper.getCreateRequest(propertySet, null);

    provider.createResources(request);
  }

  @Test(expected = org.apache.ambari.server.controller.spi.SystemException.class)
  public void testUpdateResources() throws Exception {

    AmbariManagementController managementController = createMock(AmbariManagementController.class);

    Map<String, String> mapRequestProps = new HashMap<>();
    mapRequestProps.put("context", "Called from a test");

    ResourceProvider provider = new HostKerberosIdentityResourceProvider(managementController);

    Map<String, Object> properties = new LinkedHashMap<>();

    properties.put(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_CLUSTER_NAME_PROPERTY_ID, "Cluster100");
    properties.put(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_HOST_NAME_PROPERTY_ID, "Host100");
    properties.put(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_PRINCIPAL_NAME_PROPERTY_ID, "principal@REALM");
    properties.put(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_PRINCIPAL_LOCAL_USERNAME_PROPERTY_ID, "userA");

    // create the request
    Request request = PropertyHelper.getUpdateRequest(properties, mapRequestProps);

    Predicate predicate = new PredicateBuilder()
        .property(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_CLUSTER_NAME_PROPERTY_ID)
        .equals("Cluster100")
        .and()
        .property(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_HOST_NAME_PROPERTY_ID)
        .equals("Host100")
        .and()
        .property(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_PRINCIPAL_NAME_PROPERTY_ID)
        .equals("principal@REALM").toPredicate();

    provider.updateResources(request, predicate);
  }

  @Test(expected = org.apache.ambari.server.controller.spi.SystemException.class)
  public void testDeleteResources() throws Exception {
    AmbariManagementController managementController = createMock(AmbariManagementController.class);

    ResourceProvider provider = new HostKerberosIdentityResourceProvider(managementController);

    AbstractResourceProviderTest.TestObserver observer = new AbstractResourceProviderTest.TestObserver();

    ((ObservableResourceProvider) provider).addObserver(observer);

    Predicate predicate = new PredicateBuilder()
        .property(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_CLUSTER_NAME_PROPERTY_ID)
        .equals("Cluster100")
        .and()
        .property(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_HOST_NAME_PROPERTY_ID)
        .equals("Host100")
        .and()
        .property(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_PRINCIPAL_NAME_PROPERTY_ID)
        .equals("principal@REALM").toPredicate();

    provider.deleteResources(new RequestImpl(null, null, null, null), predicate);
  }


  @Test
  public void testGetResources() throws Exception {
    Clusters clusters = createNiceMock(Clusters.class);
    expect(clusters.getCluster(EasyMock.anyString())).andReturn(
        createNiceMock(Cluster.class)).once();

    AmbariManagementController managementController = createMock(AmbariManagementController.class);
    expect(managementController.getClusters()).andReturn(clusters).atLeastOnce();

    KerberosPrincipalDescriptor principalDescriptor1 = createStrictMock(KerberosPrincipalDescriptor.class);
    expect(principalDescriptor1.getValue()).andReturn("principal1@EXAMPLE.COM");
    expect(principalDescriptor1.getType()).andReturn(KerberosPrincipalType.USER).times(1);
    expect(principalDescriptor1.getLocalUsername()).andReturn("principal1");

    KerberosKeytabDescriptor keytabDescriptor1 = createStrictMock(KerberosKeytabDescriptor.class);
    expect(keytabDescriptor1.getFile()).andReturn("/etc/security/keytabs/principal1.headless.keytab").times(1);
    expect(keytabDescriptor1.getOwnerAccess()).andReturn("rw").once();
    expect(keytabDescriptor1.getGroupAccess()).andReturn("r").once();
    expect(keytabDescriptor1.getFile()).andReturn("/etc/security/keytabs/principal1.headless.keytab").times(1);
    expect(keytabDescriptor1.getOwnerName()).andReturn("principal1").once();
    expect(keytabDescriptor1.getGroupName()).andReturn("principal1").once();

    KerberosIdentityDescriptor identity1 = createStrictMock(KerberosIdentityDescriptor.class);
    expect(identity1.getPrincipalDescriptor()).andReturn(principalDescriptor1).times(1);
    expect(identity1.getKeytabDescriptor()).andReturn(keytabDescriptor1).times(1);
    expect(identity1.getName()).andReturn("identity1").times(1);

    KerberosPrincipalDescriptor principalDescriptor2 = createStrictMock(KerberosPrincipalDescriptor.class);
    expect(principalDescriptor2.getValue()).andReturn("principal2/Host100@EXAMPLE.COM");
    expect(principalDescriptor2.getType()).andReturn(KerberosPrincipalType.SERVICE).times(1);
    expect(principalDescriptor2.getLocalUsername()).andReturn("principal2");

    KerberosIdentityDescriptor identity2 = createStrictMock(KerberosIdentityDescriptor.class);
    expect(identity2.getPrincipalDescriptor()).andReturn(principalDescriptor2).times(1);
    expect(identity2.getKeytabDescriptor()).andReturn(null).times(1);
    expect(identity2.getName()).andReturn("identity2").times(1);

    KerberosIdentityDescriptor identity3 = createStrictMock(KerberosIdentityDescriptor.class);
    expect(identity3.getPrincipalDescriptor()).andReturn(null).times(1);

    KerberosIdentityDescriptor identity4 = createStrictMock(KerberosIdentityDescriptor.class);
    expect(identity4.getPrincipalDescriptor()).andReturn(null).times(1);

    KerberosPrincipalDescriptor principalDescriptor5 = createStrictMock(KerberosPrincipalDescriptor.class);
    expect(principalDescriptor5.getValue()).andReturn("principal5@EXAMPLE.COM");
    expect(principalDescriptor5.getType()).andReturn(KerberosPrincipalType.USER).times(1);
    expect(principalDescriptor5.getLocalUsername()).andReturn("principal5");

    KerberosKeytabDescriptor keytabDescriptor5 = createStrictMock(KerberosKeytabDescriptor.class);
    expect(keytabDescriptor5.getOwnerAccess()).andReturn("r").times(1);
    expect(keytabDescriptor5.getGroupAccess()).andReturn("r").times(1);
    expect(keytabDescriptor5.getFile()).andReturn("/etc/security/keytabs/principal5.headless.keytab").times(1);
    expect(keytabDescriptor5.getOwnerName()).andReturn("principal5").times(1);
    expect(keytabDescriptor5.getGroupName()).andReturn("hadoop").times(1);

    KerberosIdentityDescriptor identity5 = createStrictMock(KerberosIdentityDescriptor.class);
    expect(identity5.getPrincipalDescriptor()).andReturn(principalDescriptor5).times(1);
    expect(identity5.getKeytabDescriptor()).andReturn(keytabDescriptor5).times(1);
    expect(identity5.getName()).andReturn("identity5").times(1);

    KerberosPrincipalDAO kerberosPrincipalDAO = createStrictMock(KerberosPrincipalDAO.class);
    expect(kerberosPrincipalDAO.exists("principal1@EXAMPLE.COM")).andReturn(true).times(1);
    expect(kerberosPrincipalDAO.exists("principal2/Host100@EXAMPLE.COM")).andReturn(true).times(1);
    expect(kerberosPrincipalDAO.exists("principal5@EXAMPLE.COM")).andReturn(false).times(1);

    KerberosKeytabPrincipalDAO kerberosKeytabPrincipalDAO = createStrictMock(KerberosKeytabPrincipalDAO.class);
    KerberosKeytabPrincipalEntity distributedEntity = new KerberosKeytabPrincipalEntity();
    distributedEntity.setDistributed(true);
    expect(kerberosKeytabPrincipalDAO.findByNaturalKey(100L,"/etc/security/keytabs/principal1.headless.keytab", "principal1@EXAMPLE.COM"))
      .andReturn(distributedEntity)
      .times(1);

    HostEntity host100 = createStrictMock(HostEntity.class);
    expect(host100.getHostId()).andReturn(100L).times(1);

    HostDAO hostDAO = createStrictMock(HostDAO.class);
    expect(hostDAO.findByName("Host100")).andReturn(host100).times(1);

    Collection<KerberosIdentityDescriptor> identities = new ArrayList<>();
    identities.add(identity1);
    identities.add(identity2);
    identities.add(identity3);
    identities.add(identity4);
    identities.add(identity5);

    Map<String, Collection<KerberosIdentityDescriptor>> activeIdentities = new HashMap<>();
    activeIdentities.put("Host100", identities);

    KerberosHelper kerberosHelper = createStrictMock(KerberosHelper.class);
    KerberosDescriptor kerberosDescriptor = createNiceMock(KerberosDescriptor.class);
    expect(kerberosHelper.getKerberosDescriptor(
        EasyMock.anyObject(Cluster.class),
        EasyMock.eq(false))).andReturn(kerberosDescriptor).atLeastOnce();

    expect(kerberosHelper.getActiveIdentities("Cluster100", "Host100", null, null, true, null,
        kerberosDescriptor, null))
        .andReturn(activeIdentities)
        .once();

    // replay
    replayAll();

    ResourceProvider provider = new HostKerberosIdentityResourceProvider(managementController);

    // Set injected values...
    Field field;
    field = HostKerberosIdentityResourceProvider.class.getDeclaredField("kerberosHelper");
    field.setAccessible(true);
    field.set(provider, kerberosHelper);

    field = HostKerberosIdentityResourceProvider.class.getDeclaredField("kerberosPrincipalDAO");
    field.setAccessible(true);
    field.set(provider, kerberosPrincipalDAO);

    field = HostKerberosIdentityResourceProvider.class.getDeclaredField("kerberosKeytabPrincipalDAO");
    field.setAccessible(true);
    field.set(provider, kerberosKeytabPrincipalDAO);

    field = HostKerberosIdentityResourceProvider.class.getDeclaredField("hostDAO");
    field.setAccessible(true);
    field.set(provider, hostDAO);

    Set<String> propertyIds = new HashSet<>();

    propertyIds.add(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_CLUSTER_NAME_PROPERTY_ID);
    propertyIds.add(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_HOST_NAME_PROPERTY_ID);
    propertyIds.add(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_DESCRIPTION_PROPERTY_ID);
    propertyIds.add(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_PRINCIPAL_NAME_PROPERTY_ID);
    propertyIds.add(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_PRINCIPAL_TYPE_PROPERTY_ID);
    propertyIds.add(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_PRINCIPAL_LOCAL_USERNAME_PROPERTY_ID);
    propertyIds.add(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_PATH_PROPERTY_ID);
    propertyIds.add(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_OWNER_PROPERTY_ID);
    propertyIds.add(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_OWNER_ACCESS_PROPERTY_ID);
    propertyIds.add(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_GROUP_PROPERTY_ID);
    propertyIds.add(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_GROUP_ACCESS_PROPERTY_ID);
    propertyIds.add(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_MODE_PROPERTY_ID);
    propertyIds.add(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_INSTALLED_PROPERTY_ID);

    Predicate predicate = new PredicateBuilder()
        .property(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_CLUSTER_NAME_PROPERTY_ID)
        .equals("Cluster100")
        .and()
        .property(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_HOST_NAME_PROPERTY_ID)
        .equals("Host100").toPredicate();

    Request request = PropertyHelper.getReadRequest(propertyIds);
    Set<Resource> resources = provider.getResources(request, predicate);

    Assert.assertEquals(3, resources.size());

    for (Resource resource : resources) {
      Assert.assertEquals("Cluster100",
          resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_CLUSTER_NAME_PROPERTY_ID));
      Assert.assertEquals("Host100",
          resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_HOST_NAME_PROPERTY_ID));

      String principal = (String) resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_PRINCIPAL_NAME_PROPERTY_ID);

      if ("principal1@EXAMPLE.COM".equals(principal)) {
        Assert.assertEquals("identity1",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_DESCRIPTION_PROPERTY_ID));

        Assert.assertEquals(KerberosPrincipalType.USER,
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_PRINCIPAL_TYPE_PROPERTY_ID));

        Assert.assertEquals("principal1",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_PRINCIPAL_LOCAL_USERNAME_PROPERTY_ID));

        Assert.assertEquals("/etc/security/keytabs/principal1.headless.keytab",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_PATH_PROPERTY_ID));

        Assert.assertEquals("principal1",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_OWNER_PROPERTY_ID));

        Assert.assertEquals("rw",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_OWNER_ACCESS_PROPERTY_ID));

        Assert.assertEquals("principal1",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_GROUP_PROPERTY_ID));

        Assert.assertEquals("r",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_GROUP_ACCESS_PROPERTY_ID));

        Assert.assertEquals("640",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_MODE_PROPERTY_ID));

        Assert.assertEquals("true",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_INSTALLED_PROPERTY_ID));
      } else if ("principal2/Host100@EXAMPLE.COM".equals(principal)) {
        Assert.assertEquals("identity2",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_DESCRIPTION_PROPERTY_ID));

        Assert.assertEquals(KerberosPrincipalType.SERVICE,
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_PRINCIPAL_TYPE_PROPERTY_ID));

        Assert.assertEquals("principal2",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_PRINCIPAL_LOCAL_USERNAME_PROPERTY_ID));

        Assert.assertNull(resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_PATH_PROPERTY_ID));
        Assert.assertNull(resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_OWNER_PROPERTY_ID));
        Assert.assertNull(resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_OWNER_ACCESS_PROPERTY_ID));
        Assert.assertNull(resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_GROUP_PROPERTY_ID));
        Assert.assertNull(resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_GROUP_ACCESS_PROPERTY_ID));
        Assert.assertNull(resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_MODE_PROPERTY_ID));

        Assert.assertEquals("false",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_INSTALLED_PROPERTY_ID));
      } else if ("principal5@EXAMPLE.COM".equals(principal)) {
        Assert.assertEquals("identity5",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_DESCRIPTION_PROPERTY_ID));

        Assert.assertEquals(KerberosPrincipalType.USER,
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_PRINCIPAL_TYPE_PROPERTY_ID));

        Assert.assertEquals("principal5",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_PRINCIPAL_LOCAL_USERNAME_PROPERTY_ID));

        Assert.assertEquals("/etc/security/keytabs/principal5.headless.keytab",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_PATH_PROPERTY_ID));

        Assert.assertEquals("principal5",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_OWNER_PROPERTY_ID));

        Assert.assertEquals("r",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_OWNER_ACCESS_PROPERTY_ID));

        Assert.assertEquals("hadoop",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_GROUP_PROPERTY_ID));

        Assert.assertEquals("r",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_GROUP_ACCESS_PROPERTY_ID));

        Assert.assertEquals("440",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_MODE_PROPERTY_ID));

        Assert.assertEquals("unknown",
            resource.getPropertyValue(HostKerberosIdentityResourceProvider.KERBEROS_IDENTITY_KEYTAB_FILE_INSTALLED_PROPERTY_ID));
      } else {
        Assert.fail("Unexpected principal: " + principal);
      }
    }

    // verify
    verifyAll();
  }
}
