content: /*
   * 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 static org.easymock.EasyMock.expectLastCall;
  
  import java.util.ArrayList;
  import java.util.Calendar;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.LinkedHashMap;
  import java.util.LinkedHashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import jakarta.persistence.EntityManager;
  
  import org.apache.ambari.server.api.services.AmbariMetaInfo;
  import org.apache.ambari.server.controller.predicate.AndPredicate;
  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.mpack.MpackManagerFactory;
  import org.apache.ambari.server.orm.DBAccessor;
  import org.apache.ambari.server.orm.entities.UserAuthenticationEntity;
  import org.apache.ambari.server.orm.entities.UserEntity;
  import org.apache.ambari.server.security.TestAuthenticationFactory;
  import org.apache.ambari.server.security.authentication.AmbariUserDetails;
  import org.apache.ambari.server.security.authorization.AuthorizationException;
  import org.apache.ambari.server.security.authorization.AuthorizationHelper;
  import org.apache.ambari.server.security.authorization.UserAuthenticationType;
  import org.apache.ambari.server.security.authorization.Users;
  import org.apache.ambari.server.stack.StackManagerFactory;
  import org.apache.ambari.server.state.Clusters;
  import org.apache.ambari.server.state.stack.OsFamily;
  import org.apache.ambari.server.testutils.PartialNiceMockBinder;
  import org.apache.velocity.exception.ResourceNotFoundException;
  import org.easymock.EasyMockSupport;
  import org.junit.After;
  import org.junit.Assert;
  import org.junit.Before;
  import org.junit.Test;
  import org.springframework.security.core.Authentication;
  import org.springframework.security.core.context.SecurityContextHolder;
  
  import com.google.inject.AbstractModule;
  import com.google.inject.Guice;
  import com.google.inject.Injector;
  
  /**
   * UserAuthenticationSourceResourceProviderTest tests.
   */
  public class UserAuthenticationSourceResourceProviderTest extends EasyMockSupport {
  
    private static final long CREATE_TIME = Calendar.getInstance().getTime().getTime();
    private static final long UPDATE_TIME = Calendar.getInstance().getTime().getTime();
  
    @Before
    public void resetMocks() {
      resetAll();
    }
  
    @After
    public void clearAuthentication() {
      SecurityContextHolder.getContext().setAuthentication(null);
    }
  
    @Test
    public void testCreateResources_Administrator() throws Exception {
      createResourcesTest(TestAuthenticationFactory.createAdministrator("admin"));
    }
  
    @Test(expected = AuthorizationException.class)
    public void testCreateResources_NonAdministrator() throws Exception {
      createResourcesTest(TestAuthenticationFactory.createClusterAdministrator("User1", 2L));
    }
  
    @Test
    public void testGetResources_Administrator() throws Exception {
      getResourcesTest(TestAuthenticationFactory.createAdministrator("admin"));
    }
  
    @Test
    public void testGetResources_NonAdministrator() throws Exception {
      getResourcesTest(TestAuthenticationFactory.createClusterAdministrator("User1", 2L));
    }
  
    @Test
    public void testGetResource_Administrator_Self() throws Exception {
      getResourceTest(TestAuthenticationFactory.createAdministrator("admin"), "admin");
    }
  
    @Test
    public void testGetResource_Administrator_Other() throws Exception {
      getResourceTest(TestAuthenticationFactory.createAdministrator("admin"), "User1");
    }
  
    @Test
    public void testGetResource_NonAdministrator_Self() throws Exception {
      getResourceTest(TestAuthenticationFactory.createClusterAdministrator("User1", 2L), "User1");
    }
  
    @Test(expected = AuthorizationException.class)
    public void testGetResource_NonAdministrator_Other() throws Exception {
      getResourceTest(TestAuthenticationFactory.createClusterAdministrator("User1", 2L), "User100");
    }
  
    @Test
    public void testUpdateResources_SetPassword_Administrator_Self() throws Exception {
      updateResources_SetAuthenticationKey(TestAuthenticationFactory.createAdministrator("admin"), "User100", null);
    }
  
    @Test
    public void testUpdateResources_SetPassword_Administrator_Other() throws Exception {
      updateResources_SetAuthenticationKey(TestAuthenticationFactory.createAdministrator("admin"), "User100", null);
    }
  
    @Test
    public void testUpdateResources_SetPassword_NonAdministrator_Self() throws Exception {
      updateResources_SetAuthenticationKey(TestAuthenticationFactory.createClusterAdministrator("User1", 2L), "User1", null);
    }
  
    @Test(expected = AuthorizationException.class)
    public void testUpdateResources_SetPassword_NonAdministrator_Other() throws Exception {
      updateResources_SetAuthenticationKey(TestAuthenticationFactory.createClusterAdministrator("User1", 2L), "User100", null);
    }
  
    @Test
    public void testUpdateResources_SetPassword_VerifyLocal_Success() throws Exception {
      updateResources_SetAuthenticationKey(TestAuthenticationFactory.createAdministrator(), "User100", "local");
    }
  
    @Test(expected = ResourceNotFoundException.class)
    public void testUpdateResources_SetPassword_VerifyLocal_Fail() throws Exception {
      updateResources_SetAuthenticationKey(TestAuthenticationFactory.createAdministrator(), "User100", "KERBEROS");
    }
  
    @Test
    public void testDeleteResource_Administrator_Self() throws Exception {
      deleteResourcesTest(TestAuthenticationFactory.createAdministrator("admin"), "admin");
    }
  
    @Test
    public void testDeleteResource_Administrator_Other() throws Exception {
      deleteResourcesTest(TestAuthenticationFactory.createAdministrator("admin"), "User100");
    }
  
    @Test(expected = AuthorizationException.class)
    public void testDeleteResource_NonAdministrator_Self() throws Exception {
      deleteResourcesTest(TestAuthenticationFactory.createClusterAdministrator("User1", 2L), "User1");
    }
  
    @Test(expected = AuthorizationException.class)
    public void testDeleteResource_NonAdministrator_Other() throws Exception {
      deleteResourcesTest(TestAuthenticationFactory.createClusterAdministrator("User1", 2L), "User100");
    }
  
    private Injector createInjector() throws Exception {
      return Guice.createInjector(new AbstractModule() {
        @Override
        protected void configure() {
          PartialNiceMockBinder.newBuilder(UserAuthenticationSourceResourceProviderTest.this)
              .addAmbariMetaInfoBinding().addLdapBindings().build().configure(binder());
  
          bind(EntityManager.class).toInstance(createNiceMock(EntityManager.class));
          bind(DBAccessor.class).toInstance(createNiceMock(DBAccessor.class));
          bind(OsFamily.class).toInstance(createNiceMock(OsFamily.class));
          bind(AmbariMetaInfo.class).toInstance(createMock(AmbariMetaInfo.class));
          bind(Clusters.class).toInstance(createNiceMock(Clusters.class));
          bind(StackManagerFactory.class).toInstance(createNiceMock(StackManagerFactory.class));
          bind(Users.class).toInstance(createMock(Users.class));
          bind(MpackManagerFactory.class).toInstance(createNiceMock(MpackManagerFactory.class));
        }
      });
    }
  
  
    private void createResourcesTest(Authentication authentication) throws Exception {
      Injector injector = createInjector();
  
      UserEntity userEntity100 = createNiceMock(UserEntity.class);
      UserEntity userEntity200 = createNiceMock(UserEntity.class);
  
      Users users = injector.getInstance(Users.class);
      expect(users.getUserEntity("User100")).andReturn(userEntity100).once();
      expect(users.getUserEntity("User200")).andReturn(userEntity200).once();
      users.addAuthentication(userEntity100, UserAuthenticationType.LOCAL, "my_password_100_1234");
      expectLastCall().once();
      users.addAuthentication(userEntity200, UserAuthenticationType.LOCAL, "my_password_200_1234");
      expectLastCall().once();
  
      // replay
      replayAll();
  
      SecurityContextHolder.getContext().setAuthentication(authentication);
  
      AmbariMetaInfo ambariMetaInfo = injector.getInstance(AmbariMetaInfo.class);
      ambariMetaInfo.init();
  
      ResourceProvider provider = getResourceProvider(injector);
  
      // add the property map to a set for the request.  add more maps for multiple creates
      Set<Map<String, Object>> propertySet = new LinkedHashSet<>();
  
      Map<String, Object> properties;
  
      properties = new LinkedHashMap<>();
      properties.put(UserAuthenticationSourceResourceProvider.AUTHENTICATION_USER_NAME_PROPERTY_ID, "User100");
      properties.put(UserAuthenticationSourceResourceProvider.AUTHENTICATION_AUTHENTICATION_TYPE_PROPERTY_ID, "local");
      properties.put(UserAuthenticationSourceResourceProvider.AUTHENTICATION_KEY_PROPERTY_ID, "my_password_100_1234");
      propertySet.add(properties);
  
      properties = new LinkedHashMap<>();
      properties.put(UserAuthenticationSourceResourceProvider.AUTHENTICATION_USER_NAME_PROPERTY_ID, "User200");
      properties.put(UserAuthenticationSourceResourceProvider.AUTHENTICATION_AUTHENTICATION_TYPE_PROPERTY_ID, "local");
      properties.put(UserAuthenticationSourceResourceProvider.AUTHENTICATION_KEY_PROPERTY_ID, "my_password_200_1234");
      propertySet.add(properties);
  
      // create the request
      Request request = PropertyHelper.getCreateRequest(propertySet, null);
  
      provider.createResources(request);
  
      // verify
      verifyAll();
    }
  
    private void getResourcesTest(Authentication authentication) throws Exception {
      Injector injector = createInjector();
  
      Users users = injector.getInstance(Users.class);
      Map<String, UserAuthenticationEntity> entities = new HashMap<>();
  
      entities.put("User1", createMockUserAuthenticationEntity("User1"));
  
      if ("admin".equals(authentication.getName())) {
        entities.put("User10", createMockUserAuthenticationEntity("User10"));
        entities.put("User100", createMockUserAuthenticationEntity("User100"));
        entities.put("admin", createMockUserAuthenticationEntity("admin"));
  
        expect(users.getUserAuthenticationEntities((String)null, null)).andReturn(entities.values()).once();
      } else {
        expect(users.getUserAuthenticationEntities("user1", null)).andReturn(entities.values()).once();
      }
  
      replayAll();
  
      AmbariMetaInfo ambariMetaInfo = injector.getInstance(AmbariMetaInfo.class);
      ambariMetaInfo.init();
  
      SecurityContextHolder.getContext().setAuthentication(authentication);
  
      ResourceProvider provider = getResourceProvider(injector);
  
      Set<String> propertyIds = new HashSet<>();
      propertyIds.add(UserAuthenticationSourceResourceProvider.AUTHENTICATION_USER_NAME_PROPERTY_ID);
      propertyIds.add(UserAuthenticationSourceResourceProvider.AUTHENTICATION_AUTHENTICATION_TYPE_PROPERTY_ID);
      propertyIds.add(UserAuthenticationSourceResourceProvider.AUTHENTICATION_KEY_PROPERTY_ID);
      propertyIds.add(UserAuthenticationSourceResourceProvider.AUTHENTICATION_CREATED_PROPERTY_ID);
      propertyIds.add(UserAuthenticationSourceResourceProvider.AUTHENTICATION_UPDATED_PROPERTY_ID);
  
      Request request = PropertyHelper.getReadRequest(propertyIds);
  
      Set<Resource> resources = provider.getResources(request, null);
  
      Assert.assertEquals(entities.size(), resources.size());
      for (Resource resource : resources) {
        String userName = (String) resource.getPropertyValue(UserAuthenticationSourceResourceProvider.AUTHENTICATION_USER_NAME_PROPERTY_ID);
        Assert.assertTrue(entities.containsKey(userName));
  
        // This value should never come back...
        Assert.assertNull(resource.getPropertyValue(UserAuthenticationSourceResourceProvider.AUTHENTICATION_KEY_PROPERTY_ID));
      }
  
      verifyAll();
    }
  
    private void getResourceTest(Authentication authentication, String requestedUsername) throws Exception {
      Injector injector = createInjector();
  
      List<UserAuthenticationEntity> entities = new ArrayList<>();
      entities.add(createMockUserAuthenticationEntity(requestedUsername));
  
      Users users = injector.getInstance(Users.class);
      expect(users.getUserAuthenticationEntities(requestedUsername, null)).andReturn(entities).once();
  
      replayAll();
  
      AmbariMetaInfo ambariMetaInfo = injector.getInstance(AmbariMetaInfo.class);
      ambariMetaInfo.init();
  
      SecurityContextHolder.getContext().setAuthentication(authentication);
  
      ResourceProvider provider = getResourceProvider(injector);
  
      Set<String> propertyIds = new HashSet<>();
      propertyIds.add(UserAuthenticationSourceResourceProvider.AUTHENTICATION_USER_NAME_PROPERTY_ID);
      propertyIds.add(UserAuthenticationSourceResourceProvider.AUTHENTICATION_KEY_PROPERTY_ID);
  
      Request request = PropertyHelper.getReadRequest(propertyIds);
  
      Set<Resource> resources = provider.getResources(request, createPredicate(requestedUsername, null));
  
      Assert.assertEquals(1, resources.size());
      for (Resource resource : resources) {
        String userName = (String) resource.getPropertyValue(UserAuthenticationSourceResourceProvider.AUTHENTICATION_USER_NAME_PROPERTY_ID);
        Assert.assertEquals(requestedUsername, userName);
  
        // This value should never come back...
        Assert.assertNull(resource.getPropertyValue(UserAuthenticationSourceResourceProvider.AUTHENTICATION_KEY_PROPERTY_ID));
      }
  
      verifyAll();
    }
  
    private void updateResources_SetAuthenticationKey(Authentication authentication, String requestedUsername, String authenticationType) throws Exception {
      Injector injector = createInjector();
  
      UserAuthenticationEntity userAuthenticationEntity = createMockUserAuthenticationEntity(requestedUsername);
  
      boolean isSelf = authentication.getName().equalsIgnoreCase(requestedUsername);
  
      List<UserAuthenticationEntity> userAuthenticationEntities = new ArrayList<>();
      userAuthenticationEntities.add(userAuthenticationEntity);
  
      UserEntity userEntity = createMock(UserEntity.class);
      expect(userEntity.getAuthenticationEntities()).andReturn(userAuthenticationEntities).once();
      if (isSelf) {
        expect(userEntity.getUserId()).andReturn(((AmbariUserDetails) authentication.getPrincipal()).getUserId()).once();
      } else {
        expect(userEntity.getUserId()).andReturn(AuthorizationHelper.getAuthenticatedId() + 100).once();
      }
  
      Users users = injector.getInstance(Users.class);
      expect(users.getUserEntity(requestedUsername)).andReturn(userEntity).once();
      users.modifyAuthentication(userAuthenticationEntity, "old_password", "new_password", isSelf);
      expectLastCall().once();
  
      replayAll();
  
      AmbariMetaInfo ambariMetaInfo = injector.getInstance(AmbariMetaInfo.class);
      ambariMetaInfo.init();
  
      SecurityContextHolder.getContext().setAuthentication(authentication);
  
      ResourceProvider provider = getResourceProvider(injector);
  
      // add the property map to a set for the request.
      Map<String, Object> properties = new LinkedHashMap<>();
      properties.put(UserAuthenticationSourceResourceProvider.AUTHENTICATION_OLD_KEY_PROPERTY_ID, "old_password");
      properties.put(UserAuthenticationSourceResourceProvider.AUTHENTICATION_KEY_PROPERTY_ID, "new_password");
  
      if (authenticationType != null) {
        properties.put(UserAuthenticationSourceResourceProvider.AUTHENTICATION_AUTHENTICATION_TYPE_PROPERTY_ID, authenticationType);
      }
  
      // create the request
      Request request = PropertyHelper.getUpdateRequest(properties, null);
  
      provider.updateResources(request, createPredicate(requestedUsername, userAuthenticationEntity.getUserAuthenticationId()));
  
      verifyAll();
    }
  
    private void deleteResourcesTest(Authentication authentication, String requestedUsername) throws Exception {
      Injector injector = createInjector();
  
      Users users = injector.getInstance(Users.class);
      users.removeAuthentication(requestedUsername, 1L);
      expectLastCall().atLeastOnce();
  
      // replay
      replayAll();
  
      AmbariMetaInfo ambariMetaInfo = injector.getInstance(AmbariMetaInfo.class);
      ambariMetaInfo.init();
  
      SecurityContextHolder.getContext().setAuthentication(authentication);
  
      ResourceProvider provider = getResourceProvider(injector);
  
      provider.deleteResources(new RequestImpl(null, null, null, null), createPredicate(requestedUsername, 1L));
  
      // verify
      verifyAll();
    }
  
  
    private Predicate createPredicate(String requestedUsername, Long sourceId) {
      Predicate predicate1 = new PredicateBuilder()
          .property(UserAuthenticationSourceResourceProvider.AUTHENTICATION_USER_NAME_PROPERTY_ID)
          .equals(requestedUsername)
          .toPredicate();
  
      if (sourceId == null) {
        return predicate1;
      } else {
        Predicate predicate2 = new PredicateBuilder()
            .property(UserAuthenticationSourceResourceProvider.AUTHENTICATION_AUTHENTICATION_SOURCE_ID_PROPERTY_ID)
            .equals(sourceId.toString())
            .toPredicate();
        return new AndPredicate(predicate1, predicate2);
      }
    }
  
    private UserAuthenticationEntity createMockUserAuthenticationEntity(String username) {
      UserAuthenticationEntity entity = createMock(UserAuthenticationEntity.class);
      UserEntity userEntity = createMock(UserEntity.class);
      expect(entity.getAuthenticationType()).andReturn(UserAuthenticationType.LOCAL).anyTimes();
      expect(entity.getAuthenticationKey()).andReturn("this is a secret").anyTimes();
      expect(entity.getCreateTime()).andReturn(CREATE_TIME).anyTimes();
      expect(entity.getUpdateTime()).andReturn(UPDATE_TIME).anyTimes();
      expect(entity.getUserAuthenticationId()).andReturn(100L).anyTimes();
      expect(entity.getUser()).andReturn(userEntity).anyTimes();
  
      expect(userEntity.getUserName()).andReturn(username).anyTimes();
      return entity;
    }
  
    private ResourceProvider getResourceProvider(Injector injector) {
      UserAuthenticationSourceResourceProvider resourceProvider = new UserAuthenticationSourceResourceProvider();
  
      injector.injectMembers(resourceProvider);
      return resourceProvider;
    }
  }