1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package org.apache.hadoop.hdfs.server.namenode;
19 
20 import java.io.IOException;
21 import java.security.PrivilegedExceptionAction;
22 import java.util.HashSet;
23 import java.util.Map;
24 import java.util.Set;
25 
26 import com.google.common.collect.ImmutableList;
27 import org.apache.hadoop.conf.Configuration;
28 import org.apache.hadoop.fs.FileStatus;
29 import org.apache.hadoop.fs.FileSystem;
30 import org.apache.hadoop.fs.Path;
31 import org.apache.hadoop.fs.XAttr;
32 import org.apache.hadoop.fs.permission.*;
33 import org.apache.hadoop.hdfs.DFSConfigKeys;
34 import org.apache.hadoop.hdfs.HdfsConfiguration;
35 import org.apache.hadoop.hdfs.MiniDFSCluster;
36 import org.apache.hadoop.hdfs.server.namenode.INodeAttributeProvider.AccessControlEnforcer;
37 import org.apache.hadoop.security.AccessControlException;
38 import org.apache.hadoop.security.UserGroupInformation;
39 import org.junit.After;
40 import org.junit.Assert;
41 import org.junit.Before;
42 import org.junit.Test;
43 
44 import com.google.common.collect.Lists;
45 
46 public class TestINodeAttributeProvider {
47   private MiniDFSCluster miniDFS;
48   private static final Set<String> CALLED = new HashSet<String>();
49 
50   public static class MyAuthorizationProvider extends INodeAttributeProvider {
51 
52     public static class MyAccessControlEnforcer implements AccessControlEnforcer {
53 
54       @Override
checkPermission(String fsOwner, String supergroup, UserGroupInformation ugi, INodeAttributes[] inodeAttrs, INode[] inodes, byte[][] pathByNameArr, int snapshotId, String path, int ancestorIndex, boolean doCheckOwner, FsAction ancestorAccess, FsAction parentAccess, FsAction access, FsAction subAccess, boolean ignoreEmptyDir)55       public void checkPermission(String fsOwner, String supergroup,
56           UserGroupInformation ugi, INodeAttributes[] inodeAttrs,
57           INode[] inodes, byte[][] pathByNameArr, int snapshotId, String path,
58           int ancestorIndex, boolean doCheckOwner, FsAction ancestorAccess,
59           FsAction parentAccess, FsAction access, FsAction subAccess,
60           boolean ignoreEmptyDir) throws AccessControlException {
61         CALLED.add("checkPermission|" + ancestorAccess + "|" + parentAccess + "|" + access);
62       }
63     }
64 
65     @Override
start()66     public void start() {
67       CALLED.add("start");
68     }
69 
70     @Override
stop()71     public void stop() {
72       CALLED.add("stop");
73     }
74 
75     @Override
getAttributes(String[] pathElements, final INodeAttributes inode)76     public INodeAttributes getAttributes(String[] pathElements,
77         final INodeAttributes inode) {
78       CALLED.add("getAttributes");
79       final boolean useDefault = useDefault(pathElements);
80       return new INodeAttributes() {
81         @Override
82         public boolean isDirectory() {
83           return inode.isDirectory();
84         }
85 
86         @Override
87         public byte[] getLocalNameBytes() {
88           return inode.getLocalNameBytes();
89         }
90 
91         @Override
92         public String getUserName() {
93           return (useDefault) ? inode.getUserName() : "foo";
94         }
95 
96         @Override
97         public String getGroupName() {
98           return (useDefault) ? inode.getGroupName() : "bar";
99         }
100 
101         @Override
102         public FsPermission getFsPermission() {
103           return (useDefault) ? inode.getFsPermission()
104                               : new FsPermission(getFsPermissionShort());
105         }
106 
107         @Override
108         public short getFsPermissionShort() {
109           return (useDefault) ? inode.getFsPermissionShort()
110                               : (short) getPermissionLong();
111         }
112 
113         @Override
114         public long getPermissionLong() {
115           return (useDefault) ? inode.getPermissionLong() : 0770;
116         }
117 
118         @Override
119         public AclFeature getAclFeature() {
120           AclFeature f;
121           if (useDefault) {
122             f = inode.getAclFeature();
123           } else {
124             AclEntry acl = new AclEntry.Builder().setType(AclEntryType.GROUP).
125                 setPermission(FsAction.ALL).setName("xxx").build();
126             f = new AclFeature(AclEntryStatusFormat.toInt(
127                 Lists.newArrayList(acl)));
128           }
129           return f;
130         }
131 
132         @Override
133         public XAttrFeature getXAttrFeature() {
134           XAttrFeature x;
135           if (useDefault) {
136             x = inode.getXAttrFeature();
137           } else {
138             x = new XAttrFeature(ImmutableList.copyOf(
139                     Lists.newArrayList(
140                             new XAttr.Builder().setName("test")
141                                     .setValue(new byte[] {1, 2})
142                                     .build())));
143           }
144           return x;
145         }
146 
147         @Override
148         public long getModificationTime() {
149           return (useDefault) ? inode.getModificationTime() : 0;
150         }
151 
152         @Override
153         public long getAccessTime() {
154           return (useDefault) ? inode.getAccessTime() : 0;
155         }
156       };
157 
158     }
159 
160     @Override
getExternalAccessControlEnforcer( AccessControlEnforcer deafultEnforcer)161     public AccessControlEnforcer getExternalAccessControlEnforcer(
162         AccessControlEnforcer deafultEnforcer) {
163       return new MyAccessControlEnforcer();
164     }
165 
useDefault(String[] pathElements)166     private boolean useDefault(String[] pathElements) {
167       return (pathElements.length < 2) ||
168           !(pathElements[0].equals("user") && pathElements[1].equals("authz"));
169     }
170 
171   }
172 
173   @Before
174   public void setUp() throws IOException {
175     CALLED.clear();
176     Configuration conf = new HdfsConfiguration();
177     conf.set(DFSConfigKeys.DFS_NAMENODE_INODE_ATTRIBUTES_PROVIDER_KEY,
178         MyAuthorizationProvider.class.getName());
179     conf.setBoolean(DFSConfigKeys.DFS_NAMENODE_ACLS_ENABLED_KEY, true);
180     EditLogFileOutputStream.setShouldSkipFsyncForTesting(true);
181     miniDFS = new MiniDFSCluster.Builder(conf).build();
182   }
183 
184   @After
185   public void cleanUp() throws IOException {
186     CALLED.clear();
187     if (miniDFS != null) {
188       miniDFS.shutdown();
189     }
190     Assert.assertTrue(CALLED.contains("stop"));
191   }
192 
193   @Test
194   public void testDelegationToProvider() throws Exception {
195     Assert.assertTrue(CALLED.contains("start"));
196     FileSystem fs = FileSystem.get(miniDFS.getConfiguration(0));
197     fs.mkdirs(new Path("/tmp"));
198     fs.setPermission(new Path("/tmp"), new FsPermission((short) 0777));
199     UserGroupInformation ugi = UserGroupInformation.createUserForTesting("u1",
200         new String[]{"g1"});
201     ugi.doAs(new PrivilegedExceptionAction<Void>() {
202       @Override
203       public Void run() throws Exception {
204         FileSystem fs = FileSystem.get(miniDFS.getConfiguration(0));
205         CALLED.clear();
206         fs.mkdirs(new Path("/tmp/foo"));
207         Assert.assertTrue(CALLED.contains("getAttributes"));
208         Assert.assertTrue(CALLED.contains("checkPermission|null|null|null"));
209         Assert.assertTrue(CALLED.contains("checkPermission|WRITE|null|null"));
210         CALLED.clear();
211         fs.listStatus(new Path("/tmp/foo"));
212         Assert.assertTrue(CALLED.contains("getAttributes"));
213         Assert.assertTrue(
214             CALLED.contains("checkPermission|null|null|READ_EXECUTE"));
215         CALLED.clear();
216         fs.getAclStatus(new Path("/tmp/foo"));
217         Assert.assertTrue(CALLED.contains("getAttributes"));
218         Assert.assertTrue(CALLED.contains("checkPermission|null|null|null"));
219         return null;
220       }
221     });
222   }
223 
224   @Test
225   public void testCustomProvider() throws Exception {
226     FileSystem fs = FileSystem.get(miniDFS.getConfiguration(0));
227     fs.mkdirs(new Path("/user/xxx"));
228     FileStatus status = fs.getFileStatus(new Path("/user/xxx"));
229     Assert.assertEquals(System.getProperty("user.name"), status.getOwner());
230     Assert.assertEquals("supergroup", status.getGroup());
231     Assert.assertEquals(new FsPermission((short) 0755), status.getPermission());
232     fs.mkdirs(new Path("/user/authz"));
233     Path p = new Path("/user/authz");
234     status = fs.getFileStatus(p);
235     Assert.assertEquals("foo", status.getOwner());
236     Assert.assertEquals("bar", status.getGroup());
237     Assert.assertEquals(new FsPermission((short) 0770), status.getPermission());
238     AclStatus aclStatus = fs.getAclStatus(p);
239     Assert.assertEquals(1, aclStatus.getEntries().size());
240     Assert.assertEquals(AclEntryType.GROUP, aclStatus.getEntries().get(0)
241             .getType());
242     Assert.assertEquals("xxx", aclStatus.getEntries().get(0)
243             .getName());
244     Assert.assertEquals(FsAction.ALL, aclStatus.getEntries().get(0)
245             .getPermission());
246     Map<String, byte[]> xAttrs = fs.getXAttrs(p);
247     Assert.assertTrue(xAttrs.containsKey("user.test"));
248     Assert.assertEquals(2, xAttrs.get("user.test").length);
249   }
250 
251 }
252