ambari-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nc...@apache.org
Subject [15/29] ambari git commit: AMBARI-12263. Support PAM as authentication mechanism for accessing Ambari UI/REST (Vishal Ghugare via rlevas)
Date Wed, 16 Nov 2016 13:57:41 GMT
AMBARI-12263.  Support PAM as authentication mechanism for accessing Ambari UI/REST (Vishal Ghugare via rlevas)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/b5a2bb8d
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/b5a2bb8d
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/b5a2bb8d

Branch: refs/heads/branch-dev-patch-upgrade
Commit: b5a2bb8ddbc7badcdd459b443077d429c5e8235d
Parents: 7df30b1
Author: Vishal Ghugare <ghugare@us.ibm.com>
Authored: Tue Nov 15 09:19:06 2016 -0500
Committer: Robert Levas <rlevas@hortonworks.com>
Committed: Tue Nov 15 09:20:22 2016 -0500

----------------------------------------------------------------------
 ambari-server/pom.xml                           |  10 +
 ambari-server/sbin/ambari-server                |   4 +
 .../server/configuration/Configuration.java     |  23 ++
 .../AmbariManagementControllerImpl.java         |   7 +-
 .../ambari/server/controller/AmbariServer.java  |   3 +
 .../ambari/server/controller/GroupResponse.java |  14 ++
 .../internal/GroupResourceProvider.java         |   4 +
 .../internal/UserPrivilegeResourceProvider.java |   3 +
 .../apache/ambari/server/orm/dao/GroupDAO.java  |  19 +-
 .../ambari/server/orm/dao/ResourceDAO.java      |  21 ++
 .../ambari/server/orm/entities/GroupEntity.java |  18 ++
 .../server/security/ClientSecurityType.java     |   3 +-
 .../AmbariPamAuthenticationProvider.java        | 252 +++++++++++++++++++
 .../server/security/authorization/Group.java    |   6 +
 .../security/authorization/GroupType.java       |  25 ++
 .../PamAuthenticationException.java             |  36 +++
 .../server/security/authorization/UserType.java |   3 +-
 .../server/security/authorization/Users.java    |  54 +++-
 .../server/upgrade/UpgradeCatalog250.java       |  11 +
 ambari-server/src/main/python/ambari-server.py  |   7 +-
 .../main/python/ambari_server/setupActions.py   |   1 +
 .../main/python/ambari_server/setupSecurity.py  |  53 +++-
 .../main/resources/Ambari-DDL-MySQL-CREATE.sql  |   1 +
 .../main/resources/Ambari-DDL-Oracle-CREATE.sql |   1 +
 .../resources/Ambari-DDL-Postgres-CREATE.sql    |   1 +
 .../resources/Ambari-DDL-SQLAnywhere-CREATE.sql |   1 +
 .../resources/Ambari-DDL-SQLServer-CREATE.sql   |   1 +
 .../src/main/resources/properties.json          |   1 +
 .../webapp/WEB-INF/spring-security.xml          |   1 +
 .../AmbariPamAuthenticationProviderTest.java    |  97 +++++++
 .../security/authorization/TestUsers.java       |  10 +-
 .../server/upgrade/UpgradeCatalog250Test.java   |  13 +
 32 files changed, 686 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/pom.xml
----------------------------------------------------------------------
diff --git a/ambari-server/pom.xml b/ambari-server/pom.xml
index e02b7a5..36c57de 100644
--- a/ambari-server/pom.xml
+++ b/ambari-server/pom.xml
@@ -1469,6 +1469,16 @@
       <version>1.0.0.0-SNAPSHOT</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.kohsuke</groupId>
+      <artifactId>libpam4j</artifactId>
+      <version>1.8</version>
+    </dependency>
+    <dependency>
+      <groupId>net.java.dev.jna</groupId>
+      <artifactId>jna</artifactId>
+      <version>4.1.0</version>
+    </dependency>
   </dependencies>
 
   <pluginRepositories>

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/sbin/ambari-server
----------------------------------------------------------------------
diff --git a/ambari-server/sbin/ambari-server b/ambari-server/sbin/ambari-server
index bdbdd0f..f08db13 100755
--- a/ambari-server/sbin/ambari-server
+++ b/ambari-server/sbin/ambari-server
@@ -132,6 +132,10 @@ case "${1:-}" in
         echo -e "Updating jce policy"
         $PYTHON "$AMBARI_PYTHON_EXECUTABLE" $@
         ;;
+  setup-pam)
+        echo -e "Setting up PAM properties..."
+        $PYTHON "$AMBARI_PYTHON_EXECUTABLE" $@
+        ;;
   setup-ldap)
         echo -e "Setting up LDAP properties..."
         $PYTHON "$AMBARI_PYTHON_EXECUTABLE" $@

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
index 0b8e195..b8b8f54 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
@@ -725,6 +725,21 @@ public class Configuration {
       "jce.name", null);
 
   /**
+   * The auto group creation by Ambari.
+   */
+  @Markdown(
+      description = "The auto group creation by Ambari")
+  public static final ConfigurationProperty<Boolean> AUTO_GROUP_CREATION = new ConfigurationProperty<>(
+      "auto.group.creation", Boolean.FALSE);
+
+  /**
+   * The PAM configuration file.
+   */
+  @Markdown(description = "The PAM configuration file.")
+  public static final ConfigurationProperty<String> PAM_CONFIGURATION_FILE = new ConfigurationProperty<>(
+      "pam.configuration", null);
+
+  /**
    * The type of authentication mechanism used by Ambari.
    *
    * @see ClientSecurityType
@@ -5747,4 +5762,12 @@ public class Configuration {
     String acceptors = getProperty(SRVR_API_ACCEPTOR_THREAD_COUNT);
     return StringUtils.isEmpty(acceptors) ? null : Integer.parseInt(acceptors);
   }
+ 
+  public String getPamConfigurationFile() {
+    return getProperty(PAM_CONFIGURATION_FILE);
+  }
+
+  public String getAutoGroupCreation() {
+    return getProperty(AUTO_GROUP_CREATION);
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
index b04fdd7..8e2fe74 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java
@@ -129,6 +129,7 @@ import org.apache.ambari.server.scheduler.ExecutionScheduleManager;
 import org.apache.ambari.server.security.authorization.AuthorizationException;
 import org.apache.ambari.server.security.authorization.AuthorizationHelper;
 import org.apache.ambari.server.security.authorization.Group;
+import org.apache.ambari.server.security.authorization.GroupType;
 import org.apache.ambari.server.security.authorization.ResourceType;
 import org.apache.ambari.server.security.authorization.RoleAuthorization;
 import org.apache.ambari.server.security.authorization.User;
@@ -973,7 +974,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
       if (group != null) {
         throw new AmbariException("Group already exists.");
       }
-      users.createGroup(request.getGroupName());
+      users.createGroup(request.getGroupName(), GroupType.LOCAL);
     }
   }
 
@@ -3685,7 +3686,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
       // get them all
       if (null == request.getGroupName()) {
         for (Group group: users.getAllGroups()) {
-          final GroupResponse response = new GroupResponse(group.getGroupName(), group.isLdapGroup());
+          final GroupResponse response = new GroupResponse(group.getGroupName(), group.isLdapGroup(), group.getGroupType());
           responses.add(response);
         }
       } else {
@@ -3698,7 +3699,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle
                 + request.getGroupName() + "'");
           }
         } else {
-          final GroupResponse response = new GroupResponse(group.getGroupName(), group.isLdapGroup());
+          final GroupResponse response = new GroupResponse(group.getGroupName(), group.isLdapGroup(), group.getGroupType());
           responses.add(response);
         }
       }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
index e54d54e..537ebc5 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java
@@ -103,6 +103,7 @@ import org.apache.ambari.server.security.authorization.AmbariLocalUserProvider;
 import org.apache.ambari.server.security.authorization.AmbariUserAuthorizationFilter;
 import org.apache.ambari.server.security.authorization.PermissionHelper;
 import org.apache.ambari.server.security.authorization.Users;
+import org.apache.ambari.server.security.authorization.AmbariPamAuthenticationProvider;
 import org.apache.ambari.server.security.authorization.internal.AmbariInternalAuthenticationProvider;
 import org.apache.ambari.server.security.ldap.AmbariLdapDataPopulator;
 import org.apache.ambari.server.security.unsecured.rest.CertificateDownload;
@@ -339,6 +340,8 @@ public class AmbariServer {
         injector.getInstance(AmbariUserAuthorizationFilter.class));
       factory.registerSingleton("ambariInternalAuthenticationProvider",
         injector.getInstance(AmbariInternalAuthenticationProvider.class));
+      factory.registerSingleton("ambariPamAuthenticationProvider",
+	injector.getInstance(AmbariPamAuthenticationProvider.class));
 
       // Spring Security xml config depends on this Bean
       String[] contextLocations = {SPRING_CONTEXT_LOCATION};

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/controller/GroupResponse.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/GroupResponse.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/GroupResponse.java
index ef28f61..0baccc7 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/GroupResponse.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/GroupResponse.java
@@ -17,16 +17,26 @@
  */
 package org.apache.ambari.server.controller;
 
+import org.apache.ambari.server.security.authorization.GroupType;
+
 /**
  * Represents a user group maintenance response.
  */
 public class GroupResponse {
   private final String groupName;
   private final boolean ldapGroup;
+  private final GroupType groupType;
+
+  public GroupResponse(String groupName, boolean ldapGroup, GroupType groupType) {
+    this.groupName = groupName;
+    this.ldapGroup = ldapGroup;
+    this.groupType = groupType;
+  }
 
   public GroupResponse(String groupName, boolean ldapGroup) {
     this.groupName = groupName;
     this.ldapGroup = ldapGroup;
+    this.groupType = GroupType.LOCAL;
   }
 
   public String getGroupName() {
@@ -37,6 +47,10 @@ public class GroupResponse {
     return ldapGroup;
   }
 
+  public GroupType getGroupType() {
+    return groupType;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o)

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/GroupResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/GroupResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/GroupResourceProvider.java
index e1aa5ac..e07dece 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/GroupResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/GroupResourceProvider.java
@@ -49,6 +49,7 @@ public class GroupResourceProvider extends AbstractControllerResourceProvider {
   // Groups
   public static final String GROUP_GROUPNAME_PROPERTY_ID  = PropertyHelper.getPropertyId("Groups", "group_name");
   public static final String GROUP_LDAP_GROUP_PROPERTY_ID = PropertyHelper.getPropertyId("Groups", "ldap_group");
+  public static final String GROUP_GROUPTYPE_PROPERTY_ID  = PropertyHelper.getPropertyId("Groups", "group_type");
 
   private static Set<String> pkPropertyIds =
       new HashSet<String>(Arrays.asList(new String[]{
@@ -132,6 +133,9 @@ public class GroupResourceProvider extends AbstractControllerResourceProvider {
       setResourceProperty(resource, GROUP_LDAP_GROUP_PROPERTY_ID,
           groupResponse.isLdapGroup(), requestedIds);
 
+      setResourceProperty(resource, GROUP_GROUPTYPE_PROPERTY_ID,
+          groupResponse.getGroupType(), requestedIds);
+
       resources.add(resource);
     }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserPrivilegeResourceProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserPrivilegeResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserPrivilegeResourceProvider.java
index ba32a5f..0575c1d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserPrivilegeResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserPrivilegeResourceProvider.java
@@ -289,6 +289,9 @@ public class UserPrivilegeResourceProvider extends ReadOnlyResourceProvider {
         }
 
         if (userEntity == null) {
+            userEntity = userDAO.findUserByNameAndType(userName, UserType.PAM);
+        }
+        if (userEntity == null) {
           throw new SystemException("User " + userName + " was not found");
         }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/GroupDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/GroupDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/GroupDAO.java
index 255c5e6..8b5902c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/GroupDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/GroupDAO.java
@@ -29,13 +29,14 @@ import javax.persistence.TypedQuery;
 
 import org.apache.ambari.server.orm.RequiresSession;
 import org.apache.ambari.server.orm.entities.GroupEntity;
+import org.apache.ambari.server.orm.entities.PrincipalEntity;
+import org.apache.ambari.server.security.authorization.GroupType;
+
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import com.google.inject.persist.Transactional;
 
-import org.apache.ambari.server.orm.entities.PrincipalEntity;
-
 @Singleton
 public class GroupDAO {
   @Inject
@@ -65,6 +66,20 @@ public class GroupDAO {
     }
   }
 
+  @RequiresSession
+  public GroupEntity findGroupByNameAndType(String groupName, GroupType groupType) {
+    // do case insensitive compare
+    TypedQuery<GroupEntity> query = entityManagerProvider.get().createQuery(
+        "SELECT group_entity FROM GroupEntity group_entity WHERE group_entity.groupType=:type AND lower(group_entity.groupName)=lower(:name)", GroupEntity.class);
+    query.setParameter("type", groupType);
+    query.setParameter("name", groupName);
+    try {
+      return query.getSingleResult();
+    } catch (NoResultException e) {
+      return null;
+    }
+  }
+
   /**
    * Find the group entities for the given list of principals
    *

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ResourceDAO.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ResourceDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ResourceDAO.java
index e4ed9c6..e57f265 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ResourceDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ResourceDAO.java
@@ -26,6 +26,7 @@ import org.apache.ambari.server.orm.RequiresSession;
 import org.apache.ambari.server.orm.entities.ResourceEntity;
 
 import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
 import javax.persistence.TypedQuery;
 import java.util.List;
 
@@ -55,6 +56,26 @@ public class ResourceDAO {
   }
 
   /**
+   * Find a resource with the given resource type id.
+   *
+   * @param id  type id
+   *
+   * @return  a matching resource or null
+   */
+  @RequiresSession
+  public ResourceEntity findByResourceTypeId(Integer id) {
+    TypedQuery<ResourceEntity> query = entityManagerProvider.get().createQuery(
+        "SELECT resource FROM ResourceEntity resource WHERE resource.resourceType.id =:resourceTypeId",
+        ResourceEntity.class);
+    query.setParameter("resourceTypeId", id);
+    try {
+      return query.getSingleResult();
+    } catch (NoResultException e) {
+      return null;
+    }
+  }
+
+  /**
    * Find all resources.
    *
    * @return all resources or an empty List

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/GroupEntity.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/GroupEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/GroupEntity.java
index 00e233e..58b2e5d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/GroupEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/GroupEntity.java
@@ -19,9 +19,12 @@ package org.apache.ambari.server.orm.entities;
 
 import java.util.Set;
 
+import javax.persistence.Basic;
 import javax.persistence.CascadeType;
 import javax.persistence.Column;
 import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
@@ -35,6 +38,8 @@ import javax.persistence.Table;
 import javax.persistence.TableGenerator;
 import javax.persistence.UniqueConstraint;
 
+import org.apache.ambari.server.security.authorization.GroupType;
+
 @Entity
 @Table(name = "groups", uniqueConstraints = {@UniqueConstraint(columnNames = {"group_name", "ldap_group"})})
 @TableGenerator(name = "group_id_generator",
@@ -59,6 +64,11 @@ public class GroupEntity {
   @Column(name = "ldap_group")
   private Integer ldapGroup = 0;
 
+  @Column(name = "group_type")
+  @Enumerated(EnumType.STRING)
+  @Basic
+  private GroupType groupType = GroupType.LOCAL;
+
   @OneToMany(mappedBy = "group", cascade = CascadeType.ALL)
   private Set<MemberEntity> memberEntities;
 
@@ -99,6 +109,14 @@ public class GroupEntity {
     }
   }
 
+  public GroupType getGroupType() {
+    return groupType;
+  }
+
+  public void setgroupType(GroupType groupType) {
+    this.groupType = groupType;
+  }
+
   public Set<MemberEntity> getMemberEntities() {
     return memberEntities;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/security/ClientSecurityType.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/ClientSecurityType.java b/ambari-server/src/main/java/org/apache/ambari/server/security/ClientSecurityType.java
index 26d4da7..fa853a6 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/ClientSecurityType.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/ClientSecurityType.java
@@ -19,7 +19,8 @@ package org.apache.ambari.server.security;
 
 public enum ClientSecurityType {
   LOCAL("local"),
-  LDAP("ldap");
+  LDAP("ldap"),
+  PAM("pam");
 
   private String value;
   ClientSecurityType(String value) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariPamAuthenticationProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariPamAuthenticationProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariPamAuthenticationProvider.java
new file mode 100644
index 0000000..ab66271
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/AmbariPamAuthenticationProvider.java
@@ -0,0 +1,252 @@
+/**
+ * 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.security.authorization;
+
+import java.security.Principal;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.orm.dao.GroupDAO;
+import org.apache.ambari.server.orm.dao.UserDAO;
+import org.apache.ambari.server.orm.entities.GroupEntity;
+import org.apache.ambari.server.orm.entities.MemberEntity;
+import org.apache.ambari.server.orm.entities.UserEntity;
+import org.apache.ambari.server.security.ClientSecurityType;
+import org.jvnet.libpam.PAM;
+import org.jvnet.libpam.PAMException;
+import org.jvnet.libpam.UnixUser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+import com.google.inject.Inject;
+
+/**
+ * Provides PAM user authentication & authorization logic for Ambari Server
+ */
+
+public class AmbariPamAuthenticationProvider implements AuthenticationProvider {
+
+  @Inject
+  private Users users;
+  @Inject
+  protected UserDAO userDAO;
+  @Inject
+  protected GroupDAO groupDAO;
+
+  private static Logger LOG = LoggerFactory.getLogger(AmbariPamAuthenticationProvider.class);
+
+  private final Configuration configuration;
+
+  @Inject
+  public AmbariPamAuthenticationProvider(Configuration configuration) {
+    this.configuration = configuration;
+  }
+
+  /**
+   * Performs PAM Initialization
+   *
+   * @param authentication
+   * @return authentication
+   */
+
+  @Override
+  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+      if(isPamEnabled()){
+        PAM pam;
+        try{
+          //Set PAM configuration file (found under /etc/pam.d)
+          String pamConfig = configuration.getPamConfigurationFile();
+          pam = new PAM(pamConfig);
+
+        } catch(PAMException ex) {
+          LOG.error("Unable to Initialize PAM." + ex.getMessage());
+          throw new AuthenticationServiceException("Unable to Initialize PAM - ", ex);
+        }
+
+        return authenticateViaPam(pam, authentication);
+    } else {
+       return null;
+    }
+  }
+
+  /**
+   * Performs PAM Authentication
+   *
+   * @param pam
+   * @param authentication
+   * @return authentication
+   */
+
+  protected Authentication authenticateViaPam(PAM pam, Authentication authentication) throws AuthenticationException{
+    if(isPamEnabled()){
+      try {
+          String userName = String.valueOf(authentication.getPrincipal());
+          String passwd = String.valueOf(authentication.getCredentials());
+
+          // authenticate using PAM
+          UnixUser unixUser = pam.authenticate(userName,passwd);
+
+          //Get all the groups that user belongs to
+          //Change all group names to lower case.
+          Set<String> groups = new HashSet<String>();
+
+          for(String group: unixUser.getGroups()){
+            groups.add(group.toLowerCase());
+          }
+
+          ambariPamAuthorization(userName,groups);
+
+          Collection<AmbariGrantedAuthority> userAuthorities =
+              users.getUserAuthorities(userName, UserType.PAM);
+
+          final User user = users.getUser(userName, UserType.PAM);
+
+          Principal principal = new Principal() {
+            @Override
+            public String getName() {
+              return user.getUserName();
+            }
+          };
+
+          UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(principal, null, userAuthorities);
+          SecurityContextHolder.getContext().setAuthentication(token);
+          return token;
+
+        } catch (PAMException ex) {
+          LOG.error("Unable to sign in. Invalid username/password combination - " + ex.getMessage());
+          Throwable t = ex.getCause();
+          throw new PamAuthenticationException("Unable to sign in. Invalid username/password combination.",t);
+
+        } finally {
+          pam.dispose();
+        }
+
+      }
+      else {
+        return null;
+      }
+  }
+
+  @Override
+  public boolean supports(Class<?> authentication) {
+    return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
+  }
+
+  /**
+   * Check if PAM authentication is enabled in server properties
+   * @return true if enabled
+   */
+  private boolean isPamEnabled() {
+    return configuration.getClientSecurityType() == ClientSecurityType.PAM;
+  }
+
+  /**
+   * Check if PAM authentication is enabled in server properties
+   * @return true if enabled
+   */
+  private boolean isAutoGroupCreationAllowed() {
+    return configuration.getAutoGroupCreation().equals("true");
+  }
+
+
+  /**
+   * Performs PAM authorization by creating user & group(s)
+   *
+   * @param userName user name
+   * @param userGroups Collection of groups
+   * @return
+   */
+  private void ambariPamAuthorization(String userName,Set<String> userGroups){
+    try {
+      User existingUser = users.getUser(userName,UserType.PAM);
+
+      if (existingUser == null ) {
+        users.createUser(userName, null, UserType.PAM, true, false);
+      }
+
+      UserEntity userEntity = userDAO.findUserByNameAndType(userName, UserType.PAM);
+
+      if(isAutoGroupCreationAllowed()){
+        for(String userGroup: userGroups){
+          if(users.getGroupByNameAndType(userGroup, GroupType.PAM) == null){
+            users.createGroup(userGroup, GroupType.PAM);
+          }
+
+          final GroupEntity groupEntity = groupDAO.findGroupByNameAndType(userGroup, GroupType.PAM);
+
+          if (!isUserInGroup(userEntity, groupEntity)){
+            users.addMemberToGroup(userGroup,userName);
+          }
+        }
+
+        Set<String> ambariUserGroups = getUserGroups(userName, UserType.PAM);
+
+        for(String group: ambariUserGroups){
+          if(userGroups == null || !userGroups.contains(group)){
+            users.removeMemberFromGroup(group, userName);
+          }
+        }
+      }
+
+    } catch (AmbariException e) {
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * Performs a check if given user belongs to given group.
+   *
+   * @param userEntity user entity
+   * @param groupEntity group entity
+   * @return true if user presents in group
+   */
+  private boolean isUserInGroup(UserEntity userEntity, GroupEntity groupEntity) {
+    for (MemberEntity memberEntity: userEntity.getMemberEntities()) {
+      if (memberEntity.getGroup().equals(groupEntity)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Extracts all groups a user belongs to
+   *
+   * @param userName user name
+   * @return Collection of group names
+   */
+  private Set<String> getUserGroups(String userName, UserType userType) {
+    UserEntity userEntity = userDAO.findUserByNameAndType(userName, userType);
+    Set<String> groups = new HashSet<String>();
+    for (MemberEntity memberEntity: userEntity.getMemberEntities()) {
+      groups.add(memberEntity.getGroup().getGroupName());
+    }
+
+    return groups;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Group.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Group.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Group.java
index b20df8d..715c41c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Group.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Group.java
@@ -26,11 +26,13 @@ public class Group {
   private final int groupId;
   private final String groupName;
   private final boolean ldapGroup;
+  private final GroupType groupType;
 
   Group(GroupEntity groupEntity) {
     this.groupId = groupEntity.getGroupId();
     this.groupName = groupEntity.getGroupName();
     this.ldapGroup = groupEntity.getLdapGroup();
+    this.groupType = groupEntity.getGroupType();
   }
 
   public int getGroupId() {
@@ -45,6 +47,10 @@ public class Group {
     return ldapGroup;
   }
 
+  public GroupType getGroupType() {
+    return groupType;
+  }
+
   @Override
   public String toString() {
     return "Group [groupId=" + groupId + ", groupName=" + groupName

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/GroupType.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/GroupType.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/GroupType.java
new file mode 100644
index 0000000..d427f3a
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/GroupType.java
@@ -0,0 +1,25 @@
+/**
+ * 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.security.authorization;
+
+public enum GroupType {
+  LOCAL,
+  LDAP,
+  JWT,
+  PAM
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/PamAuthenticationException.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/PamAuthenticationException.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/PamAuthenticationException.java
new file mode 100644
index 0000000..6c09a67
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/PamAuthenticationException.java
@@ -0,0 +1,36 @@
+/**
+ * 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.security.authorization;
+
+import org.springframework.security.core.AuthenticationException;
+
+public class PamAuthenticationException extends AuthenticationException{
+
+   public PamAuthenticationException() {
+        this("The user authentication failed");
+     }
+
+  public PamAuthenticationException(String msg, Throwable t) {
+    super(msg, t);
+  }
+
+  public PamAuthenticationException(String msg) {
+    super(msg);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/UserType.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/UserType.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/UserType.java
index aa9f3e0..e60d58e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/UserType.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/UserType.java
@@ -20,5 +20,6 @@ package org.apache.ambari.server.security.authorization;
 public enum UserType {
   LOCAL,
   LDAP,
-  JWT
+  JWT,
+  PAM
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
index 8ac7ebb..2cd538c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/authorization/Users.java
@@ -41,6 +41,7 @@ import org.apache.ambari.server.orm.dao.PrincipalDAO;
 import org.apache.ambari.server.orm.dao.PrincipalTypeDAO;
 import org.apache.ambari.server.orm.dao.PrivilegeDAO;
 import org.apache.ambari.server.orm.dao.ResourceDAO;
+import org.apache.ambari.server.orm.dao.ResourceTypeDAO;
 import org.apache.ambari.server.orm.dao.UserDAO;
 import org.apache.ambari.server.orm.entities.GroupEntity;
 import org.apache.ambari.server.orm.entities.MemberEntity;
@@ -48,7 +49,10 @@ import org.apache.ambari.server.orm.entities.PermissionEntity;
 import org.apache.ambari.server.orm.entities.PrincipalEntity;
 import org.apache.ambari.server.orm.entities.PrincipalTypeEntity;
 import org.apache.ambari.server.orm.entities.PrivilegeEntity;
+import org.apache.ambari.server.orm.entities.ResourceEntity;
+import org.apache.ambari.server.orm.entities.ResourceTypeEntity;
 import org.apache.ambari.server.orm.entities.UserEntity;
+import org.apache.ambari.server.security.ClientSecurityType;
 import org.apache.ambari.server.security.ldap.LdapBatchDto;
 import org.apache.ambari.server.security.ldap.LdapUserGroupMemberDto;
 import org.apache.commons.lang.StringUtils;
@@ -88,6 +92,8 @@ public class Users {
   @Inject
   protected ResourceDAO resourceDAO;
   @Inject
+  protected ResourceTypeDAO resourceTypeDAO;
+  @Inject
   protected PrincipalTypeDAO principalTypeDAO;
   @Inject
   protected PasswordEncoder passwordEncoder;
@@ -127,6 +133,11 @@ public class Users {
     if (userEntity == null) {
       userEntity = userDAO.findUserByNameAndType(userName, UserType.JWT);
     }
+
+    if (userEntity == null) {
+        userEntity = userDAO.findUserByNameAndType(userName, UserType.PAM);
+    }
+
     return (null == userEntity) ? null : new User(userEntity);
   }
 
@@ -369,6 +380,18 @@ public class Users {
   }
 
   /**
+   * Gets group by given name & type.
+   *
+   * @param groupName group name
+   * @param groupType group type
+   * @return group
+   */
+  public Group getGroupByNameAndType(String groupName, GroupType groupType) {
+    final GroupEntity groupEntity = groupDAO.findGroupByNameAndType(groupName, groupType);
+    return (null == groupEntity) ? null : new Group(groupEntity);
+  }
+
+  /**
    * Gets group members.
    *
    * @param groupName group name
@@ -393,10 +416,10 @@ public class Users {
   }
 
   /**
-   * Creates new local group with provided name
+   * Creates new group with provided name & type
    */
   @Transactional
-  public synchronized void createGroup(String groupName) {
+  public synchronized void createGroup(String groupName, GroupType groupType) {
     // create an admin principal to represent this group
     PrincipalTypeEntity principalTypeEntity = principalTypeDAO.findById(PrincipalTypeEntity.GROUP_PRINCIPAL_TYPE);
     if (principalTypeEntity == null) {
@@ -412,6 +435,7 @@ public class Users {
     final GroupEntity groupEntity = new GroupEntity();
     groupEntity.setGroupName(groupName);
     groupEntity.setPrincipal(principalEntity);
+    groupEntity.setgroupType(groupType);
 
     groupDAO.create(groupEntity);
   }
@@ -480,6 +504,32 @@ public class Users {
   }
 
   /**
+   * Grants privilege to provided group.
+   *
+   * @param groupId group id
+   * @param resourceId resource id
+   * @param resourceType resource type
+   * @param permissionName permission name
+   */
+  public synchronized void grantPrivilegeToGroup(Integer groupId, Long resourceId, ResourceType resourceType, String permissionName) {
+    final GroupEntity group = groupDAO.findByPK(groupId);
+    final PrivilegeEntity privilege = new PrivilegeEntity();
+    ResourceTypeEntity resourceTypeEntity = new ResourceTypeEntity();
+    resourceTypeEntity.setId(resourceType.getId());
+    resourceTypeEntity.setName(resourceType.name());
+    privilege.setPermission(permissionDAO.findPermissionByNameAndType(permissionName,resourceTypeEntity));
+    privilege.setPrincipal(group.getPrincipal());
+    privilege.setResource(resourceDAO.findById(resourceId));
+    if (!group.getPrincipal().getPrivileges().contains(privilege)) {
+      privilegeDAO.create(privilege);
+      group.getPrincipal().getPrivileges().add(privilege);
+      principalDAO.merge(group.getPrincipal()); //explicit merge for Derby support
+      groupDAO.merge(group);
+      privilegeDAO.merge(privilege);
+    }
+  }
+
+  /**
    * Revokes AMBARI.ADMINISTRATOR privilege from provided user.
    *
    * @param userId user id

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog250.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog250.java b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog250.java
index 3425dd7..e81568c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog250.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/upgrade/UpgradeCatalog250.java
@@ -46,6 +46,8 @@ import com.google.inject.Injector;
 public class UpgradeCatalog250 extends AbstractUpgradeCatalog {
 
   protected static final String HOST_VERSION_TABLE = "host_version";
+  protected static final String GROUPS_TABLE = "groups";
+  protected static final String GROUP_TYPE_COL = "group_type";
   private static final String AMS_ENV = "ams-env";
   private static final String KAFKA_BROKER = "kafka-broker";
   private static final String KAFKA_TIMELINE_METRICS_HOST = "kafka.timeline.metrics.host";
@@ -109,6 +111,7 @@ public class UpgradeCatalog250 extends AbstractUpgradeCatalog {
   protected void executeDDLUpdates() throws AmbariException, SQLException {
     updateHostVersionTable();
     createComponentVersionTable();
+    updateGroupsTable();
     dbAccessor.addColumn("stage",
       new DBAccessor.DBColumnInfo("command_execution_type", String.class, 32, CommandExecutionType.STAGE.toString(),
         false));
@@ -140,6 +143,14 @@ public class UpgradeCatalog250 extends AbstractUpgradeCatalog {
     dbAccessor.addUniqueConstraint(HOST_VERSION_TABLE, "UQ_host_repo", "repo_version_id", "host_id");
   }
 
+  protected void updateGroupsTable() throws SQLException {
+    LOG.info("Updating the {} table", GROUPS_TABLE);
+
+    dbAccessor.addColumn(GROUPS_TABLE, new DBColumnInfo(GROUP_TYPE_COL, String.class, null, "LOCAL", false));
+    dbAccessor.executeQuery("UPDATE groups SET group_type='LDAP' WHERE ldap_group=1");
+    dbAccessor.addUniqueConstraint(GROUPS_TABLE, "UNQ_groups_0", "group_name", "group_type");
+  }
+
   protected void updateAMSConfigs() throws AmbariException {
     AmbariManagementController ambariManagementController = injector.getInstance(AmbariManagementController.class);
     Clusters clusters = ambariManagementController.getClusters();

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/python/ambari-server.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/python/ambari-server.py b/ambari-server/src/main/python/ambari-server.py
index d6c6c10..d43e0f2 100755
--- a/ambari-server/src/main/python/ambari-server.py
+++ b/ambari-server/src/main/python/ambari-server.py
@@ -52,8 +52,8 @@ from ambari_server.setupActions import BACKUP_ACTION, LDAP_SETUP_ACTION, LDAP_SY
   SETUP_ACTION, SETUP_SECURITY_ACTION,START_ACTION, STATUS_ACTION, STOP_ACTION, RESTART_ACTION, UPGRADE_ACTION, \
   UPGRADE_STACK_ACTION, SETUP_JCE_ACTION, SET_CURRENT_ACTION, START_ACTION, STATUS_ACTION, STOP_ACTION, UPGRADE_ACTION, \
   UPGRADE_STACK_ACTION, SETUP_JCE_ACTION, SET_CURRENT_ACTION, ENABLE_STACK_ACTION, SETUP_SSO_ACTION, \
-  DB_CLEANUP_ACTION, INSTALL_MPACK_ACTION, UPGRADE_MPACK_ACTION
-from ambari_server.setupSecurity import setup_ldap, sync_ldap, setup_master_key, setup_ambari_krb5_jaas
+  DB_CLEANUP_ACTION, INSTALL_MPACK_ACTION, UPGRADE_MPACK_ACTION, PAM_SETUP_ACTION
+from ambari_server.setupSecurity import setup_ldap, sync_ldap, setup_master_key, setup_ambari_krb5_jaas, setup_pam
 from ambari_server.userInput import get_validated_string_input
 
 from ambari_server_main import server_process_main
@@ -651,7 +651,8 @@ def create_user_action_map(args, options):
         SETUP_SSO_ACTION: UserActionRestart(setup_sso, options),
         DB_CLEANUP_ACTION: UserAction(db_cleanup, options),
         INSTALL_MPACK_ACTION: UserAction(install_mpack, options),
-        UPGRADE_MPACK_ACTION: UserAction(upgrade_mpack, options)
+        UPGRADE_MPACK_ACTION: UserAction(upgrade_mpack, options),
+        PAM_SETUP_ACTION: UserAction(setup_pam)
       }
   return action_map
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/python/ambari_server/setupActions.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/python/ambari_server/setupActions.py b/ambari-server/src/main/python/ambari_server/setupActions.py
index 697bc1d..c87e0b2 100644
--- a/ambari-server/src/main/python/ambari_server/setupActions.py
+++ b/ambari-server/src/main/python/ambari_server/setupActions.py
@@ -46,3 +46,4 @@ ENABLE_STACK_ACTION = "enable-stack"
 DB_CLEANUP_ACTION = "db-cleanup"
 INSTALL_MPACK_ACTION = "install-mpack"
 UPGRADE_MPACK_ACTION = "upgrade-mpack"
+PAM_SETUP_ACTION = "setup-pam"

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/python/ambari_server/setupSecurity.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/python/ambari_server/setupSecurity.py b/ambari-server/src/main/python/ambari_server/setupSecurity.py
index ef27ced..1508d27 100644
--- a/ambari-server/src/main/python/ambari_server/setupSecurity.py
+++ b/ambari-server/src/main/python/ambari_server/setupSecurity.py
@@ -67,8 +67,12 @@ REGEX_ANYTHING = ".*"
 
 CLIENT_SECURITY_KEY = "client.security"
 
+AUTO_GROUP_CREATION = "auto.group.creation"
+
 SERVER_API_LDAP_URL = 'ldap_sync_events'
 
+PAM_CONFIG_FILE = 'pam.configuration'
+
 
 def read_master_key(isReset=False, options = None):
   passwordPattern = ".*"
@@ -271,12 +275,17 @@ def sync_ldap(options):
           'root-level privileges'
     raise FatalException(4, err)
 
+  properties = get_ambari_properties()
+
+  if get_value_from_properties(properties,CLIENT_SECURITY_KEY,"") == 'pam':
+    err = "PAM is configured. Can not sync LDAP."
+    raise FatalException(1, err)
+
   server_status, pid = is_server_runing()
   if not server_status:
     err = 'Ambari Server is not running.'
     raise FatalException(1, err)
 
-  properties = get_ambari_properties()
   if properties == -1:
     raise FatalException(1, "Failed to read properties file.")
 
@@ -614,6 +623,11 @@ def setup_ldap(options):
     raise FatalException(4, err)
 
   properties = get_ambari_properties()
+
+  if get_value_from_properties(properties,CLIENT_SECURITY_KEY,"") == 'pam':
+    err = "PAM is configured. Can not setup LDAP."
+    raise FatalException(1, err)
+
   isSecure = get_is_secure(properties)
 
   ldap_property_list_reqd = init_ldap_properties_list_reqd(properties, options)
@@ -812,3 +826,40 @@ def ensure_can_start_under_current_user(ambari_user):
           "command as root, as sudo or as user \"{1}\"".format(current_user, ambari_user)
     raise FatalException(1, err)
   return current_user
+
+class PamPropTemplate:
+  def __init__(self, properties, i_prop_name, i_prop_val_pattern, i_prompt_regex, i_allow_empty_prompt, i_prop_name_default=None):
+    self.prop_name = i_prop_name
+    self.pam_prop_name = get_value_from_properties(properties, i_prop_name, i_prop_name_default)
+    self.pam_prop_val_prompt = i_prop_val_pattern.format(get_prompt_default(self.pam_prop_name))
+    self.prompt_regex = i_prompt_regex
+    self.allow_empty_prompt = i_allow_empty_prompt
+
+def setup_pam():
+  if not is_root():
+    err = 'Ambari-server setup-pam should be run with ' \
+          'root-level privileges'
+    raise FatalException(4, err)
+
+  properties = get_ambari_properties()
+
+  if get_value_from_properties(properties,CLIENT_SECURITY_KEY,"") == 'ldap':
+    err = "LDAP is configured. Can not setup PAM."
+    raise FatalException(1, err)
+
+  pam_property_value_map = {}
+  pam_property_value_map[CLIENT_SECURITY_KEY] = 'pam'
+
+  pamConfig = get_validated_string_input("Enter PAM configuration file: ", PAM_CONFIG_FILE, REGEX_ANYTHING,
+                                         "Invalid characters in the input!", False, False)
+
+  pam_property_value_map[PAM_CONFIG_FILE] = pamConfig
+
+  if get_YN_input("Do you want to allow automatic group creation [y/n] (y)? ", True):
+    pam_property_value_map[AUTO_GROUP_CREATION] = 'true'
+  else:
+    pam_property_value_map[AUTO_GROUP_CREATION] = 'false'
+
+  update_properties_2(properties, pam_property_value_map)
+  print 'Saving...done'
+  return 0

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
index 37a9757..09042b5 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-MySQL-CREATE.sql
@@ -295,6 +295,7 @@ CREATE TABLE groups (
   principal_id BIGINT NOT NULL,
   group_name VARCHAR(255) NOT NULL,
   ldap_group INTEGER NOT NULL DEFAULT 0,
+  group_type VARCHAR(255) NOT NULL DEFAULT 'LOCAL',
   CONSTRAINT PK_groups PRIMARY KEY (group_id),
   CONSTRAINT FK_groups_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id),
   CONSTRAINT UNQ_groups_0 UNIQUE (group_name, ldap_group));

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
index 15d6120..e2c2dd5 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Oracle-CREATE.sql
@@ -286,6 +286,7 @@ CREATE TABLE groups (
   principal_id NUMBER(19) NOT NULL,
   group_name VARCHAR2(255) NOT NULL,
   ldap_group NUMBER(10) DEFAULT 0,
+  group_type VARCHAR(255) DEFAULT 'LOCAL' NOT NULL,
   CONSTRAINT PK_groups PRIMARY KEY (group_id),
   CONSTRAINT FK_groups_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id),
   CONSTRAINT UNQ_groups_0 UNIQUE (group_name, ldap_group));

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
index 5a82a52..4e9a535 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-Postgres-CREATE.sql
@@ -295,6 +295,7 @@ CREATE TABLE groups (
   principal_id BIGINT NOT NULL,
   group_name VARCHAR(255) NOT NULL,
   ldap_group INTEGER NOT NULL DEFAULT 0,
+  group_type VARCHAR(255) NOT NULL DEFAULT 'LOCAL',
   CONSTRAINT PK_groups PRIMARY KEY (group_id),
   UNIQUE (ldap_group, group_name),
   CONSTRAINT FK_groups_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id));

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
index 659e4dc..0ba7df6 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLAnywhere-CREATE.sql
@@ -284,6 +284,7 @@ CREATE TABLE groups (
   principal_id NUMERIC(19) NOT NULL,
   group_name VARCHAR(255) NOT NULL,
   ldap_group INTEGER NOT NULL DEFAULT 0,
+  group_type VARCHAR(255) NOT NULL DEFAULT 'LOCAL',
   CONSTRAINT PK_groups PRIMARY KEY (group_id),
   CONSTRAINT FK_groups_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id),
   CONSTRAINT UNQ_groups_0 UNIQUE (group_name, ldap_group));

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
index e9a258a..d8cad6f 100644
--- a/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
+++ b/ambari-server/src/main/resources/Ambari-DDL-SQLServer-CREATE.sql
@@ -299,6 +299,7 @@ CREATE TABLE groups (
   principal_id BIGINT NOT NULL,
   group_name VARCHAR(255) NOT NULL,
   ldap_group INTEGER NOT NULL DEFAULT 0,
+  group_type VARCHAR(255) NOT NULL DEFAULT 'LOCAL',
   CONSTRAINT PK_groups PRIMARY KEY CLUSTERED (group_id),
   CONSTRAINT FK_groups_principal_id FOREIGN KEY (principal_id) REFERENCES adminprincipal(principal_id),
   CONSTRAINT UNQ_groups_0 UNIQUE (group_name, ldap_group));

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/resources/properties.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/properties.json b/ambari-server/src/main/resources/properties.json
index 6bbb323..b7e0988 100644
--- a/ambari-server/src/main/resources/properties.json
+++ b/ambari-server/src/main/resources/properties.json
@@ -183,6 +183,7 @@
     "Group":[
         "Groups/group_name",
         "Groups/ldap_group",
+        "Groups/group_type",
         "_"
     ],
     "Member":[

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml b/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
index 500c0bf..9eca920 100644
--- a/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
+++ b/ambari-server/src/main/resources/webapp/WEB-INF/spring-security.xml
@@ -32,6 +32,7 @@
 
   <authentication-manager alias="authenticationManager">
     <authentication-provider ref="ambariLocalAuthenticationProvider"/>
+    <authentication-provider ref="ambariPamAuthenticationProvider"/>
     <authentication-provider ref="ambariLdapAuthenticationProvider"/>
     <authentication-provider ref="ambariInternalAuthenticationProvider"/>
     <authentication-provider ref="kerberosServiceAuthenticationProvider"/>

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariPamAuthenticationProviderTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariPamAuthenticationProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariPamAuthenticationProviderTest.java
new file mode 100644
index 0000000..2a6c754
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/AmbariPamAuthenticationProviderTest.java
@@ -0,0 +1,97 @@
+/**
+ * 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.security.authorization;
+
+import static junit.framework.Assert.assertEquals;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+import org.apache.ambari.server.audit.AuditLoggerModule;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.orm.GuiceJpaInitializer;
+import org.apache.ambari.server.security.ClientSecurityType;
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.jvnet.libpam.PAM;
+import org.jvnet.libpam.UnixUser;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.persist.PersistService;
+
+import junit.framework.Assert;
+
+public class AmbariPamAuthenticationProviderTest {
+
+  private static Injector injector;
+
+  @Inject
+  private AmbariPamAuthenticationProvider authenticationProvider;
+  @Inject
+  Configuration configuration;
+
+  @Before
+  public void setUp() {
+    injector = Guice.createInjector(new AuditLoggerModule(), new AuthorizationTestModule());
+    injector.injectMembers(this);
+    injector.getInstance(GuiceJpaInitializer.class);
+    configuration.setClientSecurityType(ClientSecurityType.PAM);
+    configuration.setProperty(Configuration.PAM_CONFIGURATION_FILE, "ambari-pam");
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    injector.getInstance(PersistService.class).stop();
+  }
+
+  @Test(expected = AuthenticationException.class)
+  public void testBadCredential() throws Exception {
+    Authentication authentication = new UsernamePasswordAuthenticationToken("notFound", "wrong");
+    authenticationProvider.authenticate(authentication);
+  }
+
+  @Test
+  public void testAuthenticate() throws Exception {
+    PAM pam = createNiceMock(PAM.class);
+    UnixUser unixUser = createNiceMock(UnixUser.class);
+    expect(pam.authenticate(EasyMock.anyObject(String.class), EasyMock.anyObject(String.class))).andReturn(unixUser).atLeastOnce();
+    expect(unixUser.getGroups()).andReturn(new HashSet<String>(Arrays.asList("group"))).atLeastOnce();
+    EasyMock.replay(unixUser);
+    EasyMock.replay(pam);
+    Authentication authentication = new UsernamePasswordAuthenticationToken("allowedUser", "password");
+    Authentication result = authenticationProvider.authenticateViaPam(pam,authentication);
+    assertEquals("allowedUser", result.getName());
+  }
+
+  @Test
+  public void testDisabled() throws Exception {
+    configuration.setClientSecurityType(ClientSecurityType.LOCAL);
+    Authentication authentication = new UsernamePasswordAuthenticationToken("allowedUser", "password");
+    Authentication auth = authenticationProvider.authenticate(authentication);
+    Assert.assertTrue(auth == null);
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java
index f54ac5c..7d112fc 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/security/authorization/TestUsers.java
@@ -285,7 +285,7 @@ public class TestUsers {
 
   @Test
   public void testSetGroupLdap() throws Exception {
-    users.createGroup("group");
+    users.createGroup("group", GroupType.LOCAL);
 
     users.setGroupLdap("group");
     Assert.assertNotNull(users.getGroup("group"));
@@ -302,8 +302,8 @@ public class TestUsers {
   public void testCreateGetRemoveGroup() throws Exception {
     final String groupName = "engineering1";
     final String groupName2 = "engineering2";
-    users.createGroup(groupName);
-    users.createGroup(groupName2);
+    users.createGroup(groupName, GroupType.LOCAL);
+    users.createGroup(groupName2, GroupType.LOCAL);
 
     final Group group = users.getGroup(groupName);
     assertNotNull(group);
@@ -328,8 +328,8 @@ public class TestUsers {
   public void testMembers() throws Exception {
     final String groupName = "engineering";
     final String groupName2 = "engineering2";
-    users.createGroup(groupName);
-    users.createGroup(groupName2);
+    users.createGroup(groupName, GroupType.LOCAL);
+    users.createGroup(groupName2, GroupType.LOCAL);
     users.createUser("user1", "user1");
     users.createUser("user2", "user2");
     users.createUser("user3", "user3");

http://git-wip-us.apache.org/repos/asf/ambari/blob/b5a2bb8d/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog250Test.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog250Test.java b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog250Test.java
index 8ed81df..14fc20b 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog250Test.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/upgrade/UpgradeCatalog250Test.java
@@ -101,6 +101,11 @@ public class UpgradeCatalog250Test {
 
     // !!! setup capture for host_version
     dbAccessor.addUniqueConstraint("host_version", "UQ_host_repo", "repo_version_id", "host_id");
+
+    Capture<DBAccessor.DBColumnInfo> groupGroupType = newCapture();
+    dbAccessor.addColumn(eq(UpgradeCatalog250.GROUPS_TABLE), capture(groupGroupType));
+    dbAccessor.addUniqueConstraint("groups", "UNQ_groups_0", "group_name", "group_type");
+
     expectLastCall().once();
 
     // !!! setup capture for servicecomponent_version
@@ -143,6 +148,14 @@ public class UpgradeCatalog250Test {
     UpgradeCatalog250 upgradeCatalog250 = injector.getInstance(UpgradeCatalog250.class);
     upgradeCatalog250.executeDDLUpdates();
 
+    DBAccessor.DBColumnInfo capturedGroupTypeColumn = groupGroupType.getValue();
+    Assert.assertNotNull(capturedGroupTypeColumn);
+    Assert.assertEquals(UpgradeCatalog250.GROUP_TYPE_COL, capturedGroupTypeColumn.getName());
+    Assert.assertEquals(String.class, capturedGroupTypeColumn.getType());
+    Assert.assertEquals(null, capturedGroupTypeColumn.getLength());
+    Assert.assertEquals("LOCAL", capturedGroupTypeColumn.getDefaultValue());
+    Assert.assertEquals(false, capturedGroupTypeColumn.isNullable());
+
     verify(dbAccessor);
 
     // !!! check the captured for host_version


Mime
View raw message