You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by rl...@apache.org on 2016/11/15 14:20:48 UTC
ambari git commit: AMBARI-12263. Support PAM as authentication
mechanism for accessing Ambari UI/REST (Vishal Ghugare via rlevas)
Repository: ambari
Updated Branches:
refs/heads/trunk 7df30b172 -> b5a2bb8dd
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/trunk
Commit: b5a2bb8ddbc7badcdd459b443077d429c5e8235d
Parents: 7df30b1
Author: Vishal Ghugare <gh...@us.ibm.com>
Authored: Tue Nov 15 09:19:06 2016 -0500
Committer: Robert Levas <rl...@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