blob: 922d3308421587a5521952593b0cbaed55ba8457 [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.hadoop.security.authorize;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;
import java.util.Arrays;
import java.util.List;
import java.util.LinkedList;
import java.util.ListIterator;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableFactories;
import org.apache.hadoop.io.WritableFactory;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.Groups;
import org.apache.hadoop.conf.Configuration;
/**
* Class representing a configured access control list.
*/
@InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce"})
@InterfaceStability.Evolving
public class AccessControlList implements Writable {
static { // register a ctor
WritableFactories.setFactory
(AccessControlList.class,
new WritableFactory() {
public Writable newInstance() { return new AccessControlList(); }
});
}
// Indicates an ACL string that represents access to all users
public static final String WILDCARD_ACL_VALUE = "*";
private static final int INITIAL_CAPACITY = 256;
// Set of users who are granted access.
private Set<String> users;
// Set of groups which are granted access
private Set<String> groups;
// Whether all users are granted access.
private boolean allAllowed;
private Groups groupsMapping = Groups.getUserToGroupsMappingService(new Configuration());
/**
* This constructor exists primarily for AccessControlList to be Writable.
*/
public AccessControlList() {
}
/**
* Construct a new ACL from a String representation of the same.
*
* The String is a a comma separated list of users and groups.
* The user list comes first and is separated by a space followed
* by the group list. For e.g. "user1,user2 group1,group2"
*
* @param aclString String representation of the ACL
*/
public AccessControlList(String aclString) {
buildACL(aclString);
}
/**
* Build ACL from the given string, format of the string is
* user1,...,userN group1,...,groupN
*
* @param aclString build ACL from this string
*/
private void buildACL(String aclString) {
users = new TreeSet<String>();
groups = new TreeSet<String>();
if (isWildCardACLValue(aclString)) {
allAllowed = true;
} else {
allAllowed = false;
String[] userGroupStrings = aclString.split(" ", 2);
if (userGroupStrings.length >= 1) {
List<String> usersList = new LinkedList<String>(
Arrays.asList(userGroupStrings[0].split(",")));
cleanupList(usersList);
addToSet(users, usersList);
}
if (userGroupStrings.length == 2) {
List<String> groupsList = new LinkedList<String>(
Arrays.asList(userGroupStrings[1].split(",")));
cleanupList(groupsList);
addToSet(groups, groupsList);
groupsMapping.cacheGroupsAdd(groupsList);
}
}
}
/**
* Checks whether ACL string contains wildcard
*
* @param aclString check this ACL string for wildcard
* @return true if ACL string contains wildcard false otherwise
*/
private boolean isWildCardACLValue(String aclString) {
if (aclString.contains(WILDCARD_ACL_VALUE) &&
aclString.trim().equals(WILDCARD_ACL_VALUE)) {
return true;
}
return false;
}
public boolean isAllAllowed() {
return allAllowed;
}
/**
* Add user to the names of users allowed for this service.
*
* @param user
* The user name
*/
public void addUser(String user) {
if (isWildCardACLValue(user)) {
throw new IllegalArgumentException("User " + user + " can not be added");
}
if (!isAllAllowed()) {
users.add(user);
}
}
/**
* Add group to the names of groups allowed for this service.
*
* @param group
* The group name
*/
public void addGroup(String group) {
if (isWildCardACLValue(group)) {
throw new IllegalArgumentException("Group " + group + " can not be added");
}
if (!isAllAllowed()) {
List<String> groupsList = new LinkedList<String>();
groupsList.add(group);
groupsMapping.cacheGroupsAdd(groupsList);
groups.add(group);
}
}
/**
* Remove user from the names of users allowed for this service.
*
* @param user
* The user name
*/
public void removeUser(String user) {
if (isWildCardACLValue(user)) {
throw new IllegalArgumentException("User " + user + " can not be removed");
}
if (!isAllAllowed()) {
users.remove(user);
}
}
/**
* Remove group from the names of groups allowed for this service.
*
* @param group
* The group name
*/
public void removeGroup(String group) {
if (isWildCardACLValue(group)) {
throw new IllegalArgumentException("Group " + group
+ " can not be removed");
}
if (!isAllAllowed()) {
groups.remove(group);
}
}
/**
* Get the names of users allowed for this service.
* @return the set of user names. the set must not be modified.
*/
Set<String> getUsers() {
return users;
}
/**
* Get the names of user groups allowed for this service.
* @return the set of group names. the set must not be modified.
*/
Set<String> getGroups() {
return groups;
}
public boolean isUserAllowed(UserGroupInformation ugi) {
if (allAllowed || users.contains(ugi.getShortUserName())) {
return true;
} else {
for(String group: ugi.getGroupNames()) {
if (groups.contains(group)) {
return true;
}
}
}
return false;
}
/**
* Cleanup list, remove empty strings, trim leading/trailing spaces
*
* @param list clean this list
*/
private static final void cleanupList(List<String> list) {
ListIterator<String> i = list.listIterator();
while(i.hasNext()) {
String s = i.next();
if(s.length() == 0) {
i.remove();
} else {
s = s.trim();
i.set(s);
}
}
}
/**
* Add list to a set
*
* @param set add list to this set
* @param list add items of this list to the set
*/
private static final void addToSet(Set<String> set, List<String> list) {
for(String s : list) {
set.add(s);
}
}
/**
* Returns descriptive way of users and groups that are part of this ACL.
* Use {@link #getAclString()} to get the exact String that can be given to
* the constructor of AccessControlList to create a new instance.
*/
@Override
public String toString() {
String str = null;
if (allAllowed) {
str = "All users are allowed";
}
else if (users.isEmpty() && groups.isEmpty()) {
str = "No users are allowed";
}
else {
String usersStr = null;
String groupsStr = null;
if (!users.isEmpty()) {
usersStr = users.toString();
}
if (!groups.isEmpty()) {
groupsStr = groups.toString();
}
if (!users.isEmpty() && !groups.isEmpty()) {
str = "Users " + usersStr + " and members of the groups "
+ groupsStr + " are allowed";
}
else if (!users.isEmpty()) {
str = "Users " + usersStr + " are allowed";
}
else {// users is empty array and groups is nonempty
str = "Members of the groups "
+ groupsStr + " are allowed";
}
}
return str;
}
/**
* Returns the access control list as a String that can be used for building a
* new instance by sending it to the constructor of {@link AccessControlList}.
*/
public String getAclString() {
StringBuilder sb = new StringBuilder(INITIAL_CAPACITY);
if (allAllowed) {
sb.append('*');
}
else {
sb.append(getUsersString());
sb.append(" ");
sb.append(getGroupsString());
}
return sb.toString();
}
/**
* Serializes the AccessControlList object
*/
public void write(DataOutput out) throws IOException {
String aclString = getAclString();
Text.writeString(out, aclString);
}
/**
* Deserializes the AccessControlList object
*/
public void readFields(DataInput in) throws IOException {
String aclString = Text.readString(in);
buildACL(aclString);
}
/**
* Returns comma-separated concatenated single String of the set 'users'
*
* @return comma separated list of users
*/
private String getUsersString() {
return getString(users);
}
/**
* Returns comma-separated concatenated single String of the set 'groups'
*
* @return comma separated list of groups
*/
private String getGroupsString() {
return getString(groups);
}
/**
* Returns comma-separated concatenated single String of all strings of
* the given set
*
* @param strings set of strings to concatenate
*/
private String getString(Set<String> strings) {
StringBuilder sb = new StringBuilder(INITIAL_CAPACITY);
boolean first = true;
for(String str: strings) {
if (!first) {
sb.append(",");
} else {
first = false;
}
sb.append(str);
}
return sb.toString();
}
}