blob: 77ab0652ad2a47b6612bd263e262fb070f1918cc [file] [log] [blame]
/**
* 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.tez.common.security;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.classification.InterfaceAudience.Private;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.yarn.api.records.ApplicationAccessType;
import org.apache.tez.dag.api.TezConfiguration;
import com.google.common.annotations.VisibleForTesting;
/**
* Class to manage ACLs for the Tez AM and DAGs and provides functionality to check whether
* a user is authorized to take certain actions.
*/
@Private
public class ACLManager {
private static final Log LOG = LogFactory.getLog(ACLManager.class);
static final String WILDCARD_ACL_VALUE = "*";
private final String dagUser;
private final String amUser;
private final Map<ACLType, Set<String>> users;
private final Map<ACLType, Set<String>> groups;
private final boolean aclsEnabled;
public ACLManager(String amUser) {
this(amUser, new Configuration(false));
}
public ACLManager(String amUser, Configuration conf) {
this.amUser = amUser;
this.dagUser = null;
this.users = new HashMap<ACLType, Set<String>>();
this.groups = new HashMap<ACLType, Set<String>>();
aclsEnabled = conf.getBoolean(TezConfiguration.TEZ_AM_ACLS_ENABLED,
TezConfiguration.TEZ_AM_ACLS_ENABLED_DEFAULT);
if (!aclsEnabled) {
return;
}
ACLConfigurationParser parser = new ACLConfigurationParser(conf);
if (parser.getAllowedUsers() != null) {
this.users.putAll(parser.getAllowedUsers());
}
if (parser.getAllowedGroups() != null) {
this.groups.putAll(parser.getAllowedGroups());
}
}
public ACLManager(ACLManager amACLManager, String dagUser, Configuration dagConf) {
this.amUser = amACLManager.amUser;
this.dagUser = dagUser;
this.users = amACLManager.users;
this.groups = amACLManager.groups;
this.aclsEnabled = amACLManager.aclsEnabled;
if (!aclsEnabled) {
return;
}
ACLConfigurationParser parser = new ACLConfigurationParser(dagConf, true);
if (parser.getAllowedUsers() != null) {
this.users.putAll(parser.getAllowedUsers());
}
if (parser.getAllowedGroups() != null) {
this.groups.putAll(parser.getAllowedGroups());
}
}
@VisibleForTesting
boolean checkAccess(UserGroupInformation ugi, ACLType aclType) {
if (!aclsEnabled) {
return true;
}
String user = ugi.getShortUserName();
Collection<String> userGroups = Arrays.asList(ugi.getGroupNames());
if (amUser.equals(user)) {
return true;
}
if (EnumSet.of(ACLType.DAG_MODIFY_ACL, ACLType.DAG_VIEW_ACL).contains(aclType)) {
if (dagUser != null && dagUser.equals(user)) {
return true;
}
}
if (users != null && !users.isEmpty()) {
Set<String> set = users.get(aclType);
if (set != null) {
if (set.contains(WILDCARD_ACL_VALUE)) {
return true;
}
if (set.contains(user)) {
return true;
}
}
}
if (userGroups != null && !userGroups.isEmpty()
&& groups != null && !groups.isEmpty()) {
Set<String> set = groups.get(aclType);
if (set != null) {
for (String userGrp : userGroups) {
if (set.contains(userGrp)) {
return true;
}
}
}
}
return false;
}
public boolean checkAMViewAccess(UserGroupInformation ugi) {
return checkAccess(ugi, ACLType.AM_VIEW_ACL);
}
public boolean checkAMModifyAccess(UserGroupInformation ugi) {
return checkAccess(ugi, ACLType.AM_MODIFY_ACL);
}
public boolean checkDAGViewAccess(UserGroupInformation ugi) {
return checkAccess(ugi, ACLType.AM_VIEW_ACL)
|| checkAccess(ugi, ACLType.DAG_VIEW_ACL);
}
public boolean checkDAGModifyAccess(UserGroupInformation ugi) {
return checkAccess(ugi, ACLType.AM_MODIFY_ACL)
|| checkAccess(ugi, ACLType.DAG_MODIFY_ACL);
}
public Map<ApplicationAccessType, String> toYARNACls() {
Map<ApplicationAccessType, String> acls = new HashMap<ApplicationAccessType, String>(2);
if (!this.aclsEnabled) {
acls.put(ApplicationAccessType.VIEW_APP, "*");
acls.put(ApplicationAccessType.MODIFY_APP, "*");
return acls;
}
acls.put(ApplicationAccessType.VIEW_APP, amUser);
acls.put(ApplicationAccessType.MODIFY_APP, amUser);
boolean viewAclsWildCard = false;
boolean modifyAclsWildCard = false;
if (users != null && !users.isEmpty()) {
for (Entry<ACLType, Set<String>> entry : users.entrySet()) {
if (entry.getKey().equals(ACLType.AM_VIEW_ACL)) {
if (entry.getValue().contains(WILDCARD_ACL_VALUE)) {
acls.put(ApplicationAccessType.VIEW_APP, "*");
viewAclsWildCard = true;
continue;
} else if (!entry.getValue().isEmpty()) {
String aclsStr = acls.get(ApplicationAccessType.VIEW_APP);
String commaSepList = toCommaSeparatedString(entry.getValue());
if (!commaSepList.isEmpty()) {
aclsStr += "," + commaSepList;
}
acls.put(ApplicationAccessType.VIEW_APP, aclsStr);
}
} else if (entry.getKey().equals(ACLType.AM_MODIFY_ACL)) {
if (entry.getValue().contains(WILDCARD_ACL_VALUE)) {
acls.put(ApplicationAccessType.MODIFY_APP, "*");
modifyAclsWildCard = true;
continue;
} else if (!entry.getValue().isEmpty()) {
String aclsStr = acls.get(ApplicationAccessType.MODIFY_APP);
String commaSepList = toCommaSeparatedString(entry.getValue());
if (!commaSepList.isEmpty()) {
aclsStr += "," + commaSepList;
}
acls.put(ApplicationAccessType.MODIFY_APP, aclsStr);
}
}
}
}
if (groups != null && !groups.isEmpty()) {
for (Entry<ACLType, Set<String>> entry : groups.entrySet()) {
if (entry.getKey().equals(ACLType.AM_VIEW_ACL)
&& !viewAclsWildCard && !entry.getValue().isEmpty()) {
// Append groups only if wild card not set
String aclsStr = acls.containsKey(ApplicationAccessType.VIEW_APP) ?
acls.get(ApplicationAccessType.VIEW_APP) : "";
aclsStr += " " + toCommaSeparatedString(entry.getValue());
acls.put(ApplicationAccessType.VIEW_APP, aclsStr);
} else if (entry.getKey().equals(ACLType.AM_MODIFY_ACL)
&& !modifyAclsWildCard && !entry.getValue().isEmpty()) {
// Append groups only if wild card not set
String aclsStr = acls.containsKey(ApplicationAccessType.MODIFY_APP) ?
acls.get(ApplicationAccessType.MODIFY_APP) : "";
aclsStr += " " + toCommaSeparatedString(entry.getValue());
acls.put(ApplicationAccessType.MODIFY_APP, aclsStr);
}
}
}
return acls;
}
static String toCommaSeparatedString(Collection<String> collection) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (String s : collection) {
if (!first) {
sb.append(",");
} else {
first = false;
}
sb.append(s);
}
return sb.toString();
}
}