Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
FsPermissionChecker.java
Go to the documentation of this file.
1 // Copyright 2013 Cloudera Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.cloudera.impala.util;
16 
17 import java.io.IOException;
18 import java.util.Arrays;
19 import java.util.HashSet;
20 import java.util.Set;
21 import java.util.Map;
22 import java.util.List;
23 
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26 
27 import org.apache.hadoop.fs.permission.AclEntry;
28 import org.apache.hadoop.fs.permission.AclEntryType;
29 import org.apache.hadoop.fs.permission.AclStatus;
30 import org.apache.hadoop.fs.permission.AclStatus;
31 import org.apache.hadoop.fs.permission.AclEntryScope;
32 import org.apache.hadoop.fs.FileStatus;
33 import org.apache.hadoop.fs.FileSystem;
34 import org.apache.hadoop.fs.Path;
35 import org.apache.hadoop.fs.permission.FsAction;
36 import org.apache.hadoop.fs.permission.FsPermission;
37 import org.apache.hadoop.security.UserGroupInformation;
38 import org.apache.hadoop.hdfs.protocol.AclException;
39 import org.apache.hadoop.ipc.RemoteException;
40 
41 import com.google.common.base.Preconditions;
42 import com.google.common.collect.ImmutableList;
43 import com.google.common.collect.Maps;
44 import com.google.common.collect.Lists;
45 
50 public class FsPermissionChecker {
51  private final static Logger LOG = LoggerFactory.getLogger(FsPermissionChecker.class);
52  private final static FsPermissionChecker instance_;
53  protected final String user_;
54  private final Set<String> groups_ = new HashSet<String>();
55 
56  static {
57  try {
59  } catch (IOException e) {
60  throw new RuntimeException(
61  "Error initializing FsPermissionChecker: " + e.getMessage(), e);
62  }
63  }
64 
65  private FsPermissionChecker() throws IOException {
66  UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
67  groups_.addAll(Arrays.asList(ugi.getGroupNames()));
68  // Todo: should add HDFS supergroup as well
69  user_ = ugi.getShortUserName();
70  }
71 
72 
73  private static List<AclEntryType> ACL_TYPE_PRIORITY =
74  ImmutableList.of(AclEntryType.USER, AclEntryType.GROUP, AclEntryType.OTHER);
75 
80  public class Permissions {
81  private final FileStatus fileStatus_;
82  private final FsPermission permissions_;
83  private final AclStatus aclStatus_;
84  private Map<AclEntryType, List<AclEntry>> entriesByTypes_ = Maps.newHashMap();
85  private AclEntry mask_;
86 
90  protected Permissions(FileStatus fileStatus, AclStatus aclStatus) {
91  Preconditions.checkNotNull(fileStatus);
92  fileStatus_ = fileStatus;
93  permissions_ = fileStatus.getPermission();
94  aclStatus_ = aclStatus;
95  if (aclStatus_ == null) return;
96 
97  // Group the ACLs by type, so that we can apply them in correct priority order. Not
98  // clear from documentation whether aclStatus_.getEntries() guarantees this
99  // ordering, so this is defensive.
100  for (AclEntryType t: ACL_TYPE_PRIORITY) {
101  entriesByTypes_.put(t, Lists.<AclEntry>newArrayList());
102  }
103 
104  List<AclEntry> fullAclList =
105  getAclFromPermAndEntries(permissions_, aclStatus_.getEntries());
106  for (AclEntry e: fullAclList) {
107  if (e.getType() == AclEntryType.MASK && e.getScope() != AclEntryScope.DEFAULT) {
108  mask_ = e;
109  } else if (isApplicableAcl(e)) {
110  entriesByTypes_.get(e.getType()).add(e);
111  }
112  }
113  }
114 
119  private boolean shouldApplyMask(AclEntry acl) {
120  if (mask_ == null) return false;
121 
122  switch (acl.getType()) {
123  case USER:
124  return acl.getName() != null;
125  case GROUP:
126  return true;
127  }
128  return false;
129  }
130 
134  private boolean isApplicableAcl(AclEntry e) {
135  // Default ACLs are not used for permission checking, but instead control the
136  // permissions received by child directories
137  if (e.getScope() == AclEntryScope.DEFAULT) return false;
138 
139  switch (e.getType()) {
140  case USER:
141  String aclUser = e.getName() == null ? aclStatus_.getOwner() : e.getName();
142  return FsPermissionChecker.this.user_.equals(aclUser);
143  case GROUP:
144  String aclGroup = e.getName() == null ? aclStatus_.getGroup() : e.getName();
145  return FsPermissionChecker.this.groups_.contains(aclGroup);
146  case OTHER:
147  return true;
148  case MASK:
149  return false;
150  default:
151  LOG.warn("Unknown Acl type: " + e.getType());
152  return false;
153  }
154  }
155 
162  private Boolean checkAcls(FsAction action) {
163  // ACLs may not be enabled, so we need this ternary logic. If no ACLs are available,
164  // returning null causes us to fall back to standard ugo permissions.
165  if (aclStatus_ == null) return null;
166 
167  // Remember if there is an applicable ACL entry, including owner user, named user,
168  // owning group, named group.
169  boolean foundMatch = false;
170  for (AclEntryType t: ACL_TYPE_PRIORITY) {
171  for (AclEntry e: entriesByTypes_.get(t)) {
172  if (t == AclEntryType.OTHER) {
173  // Processed all ACL entries except the OTHER entry.
174  // If found applicable ACL entries but none of them contain requested
175  // permission, deny access. Otherwise check OTHER entry.
176  return foundMatch ? false : e.getPermission().implies(action);
177  }
178  // If there is an applicable mask, 'action' is allowed iff both the mask and
179  // the underlying ACL permit it.
180  if (e.getPermission().implies(action)) {
181  if (shouldApplyMask(e)) {
182  if (mask_.getPermission().implies(action)) return true;
183  } else {
184  return true;
185  }
186  }
187  // User ACL entry has priority, no need to continue check.
188  if (t == AclEntryType.USER) return false;
189 
190  foundMatch = true;
191  }
192  }
193  return false;
194  }
195 
200  public boolean checkPermissions(FsAction action) {
201  Boolean aclPerms = checkAcls(action);
202  if (aclPerms != null) return aclPerms;
203 
204  // Check user, group and then 'other' permissions in turn.
205  if (FsPermissionChecker.this.user_.equals(fileStatus_.getOwner())) {
206  // If the user matches, we must return their access rights whether or not the user
207  // is allowed to access without checking the group. This is counter-intuitive if
208  // the user cannot access the file, but the group permissions would allow it, but
209  // is consistent with UNIX behaviour.
210  return permissions_.getUserAction().implies(action);
211  }
212 
213  if (FsPermissionChecker.this.groups_.contains(fileStatus_.getGroup())) {
214  return permissions_.getGroupAction().implies(action);
215  }
216  return permissions_.getOtherAction().implies(action);
217  }
218 
219  public boolean canRead() { return checkPermissions(FsAction.READ); }
220  public boolean canWrite() { return checkPermissions(FsAction.WRITE); }
221  public boolean canReadAndWrite() { return canRead() && canWrite(); }
222 
223  // This was originally lifted from Hadoop. Won't need it if HDFS-7177 is resolved.
224  // getAclStatus() returns just extended ACL entries, the default file permissions
225  // like "user::,group::,other::" are not included. We need to combine them together
226  // to get full logic ACL list.
227  private List<AclEntry> getAclFromPermAndEntries(FsPermission perm,
228  List<AclEntry> entries) {
229  // File permission always have 3 items.
230  List<AclEntry> aclEntries = Lists.newArrayListWithCapacity(entries.size() + 3);
231 
232  // Owner entry implied by owner permission bits.
233  aclEntries.add(new AclEntry.Builder()
234  .setScope(AclEntryScope.ACCESS)
235  .setType(AclEntryType.USER)
236  .setPermission(perm.getUserAction())
237  .build());
238 
239  // All extended access ACL entries add by "-setfacl" other than default file
240  // permission.
241  boolean hasAccessAcl = false;
242  for (AclEntry entry: entries) {
243  // AclEntry list should be ordered, all ACCESS one are in first half, DEFAULT one
244  // are in second half, so no need to continue here.
245  if (entry.getScope() == AclEntryScope.DEFAULT) break;
246  hasAccessAcl = true;
247  aclEntries.add(entry);
248  }
249 
250  // Mask entry implied by group permission bits, or group entry if there is
251  // no access ACL (only default ACL).
252  aclEntries.add(new AclEntry.Builder()
253  .setScope(AclEntryScope.ACCESS)
254  .setType(hasAccessAcl ? AclEntryType.MASK : AclEntryType.GROUP)
255  .setPermission(perm.getGroupAction())
256  .build());
257 
258  // Other entry implied by other bits.
259  aclEntries.add(new AclEntry.Builder()
260  .setScope(AclEntryScope.ACCESS)
261  .setType(AclEntryType.OTHER)
262  .setPermission(perm.getOtherAction())
263  .build());
264 
265  return aclEntries;
266  }
267  }
268 
273  public Permissions getPermissions(FileSystem fs, Path path) throws IOException {
274  Preconditions.checkNotNull(fs);
275  Preconditions.checkNotNull(path);
276  AclStatus aclStatus = null;
277  try {
278  aclStatus = fs.getAclStatus(path);
279  } catch (AclException ex) {
280  LOG.trace("No ACLs retrieved, skipping ACLs check (HDFS will enforce ACLs)", ex);
281  } catch (UnsupportedOperationException ex) {
282  LOG.trace("No ACLs retrieved, unsupported", ex);
283  }
284  return new Permissions(fs.getFileStatus(path), aclStatus);
285  }
286 
290  public static FsPermissionChecker getInstance() { return instance_; }
291 }
string path("/usr/lib/sasl2:/usr/lib64/sasl2:/usr/local/lib/sasl2:/usr/lib/x86_64-linux-gnu/sasl2")
static final FsPermissionChecker instance_
Permissions(FileStatus fileStatus, AclStatus aclStatus)
Permissions getPermissions(FileSystem fs, Path path)
List< AclEntry > getAclFromPermAndEntries(FsPermission perm, List< AclEntry > entries)