| /** |
| * 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.hdfs; |
| |
| import static org.junit.Assert.assertEquals; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.PrintStream; |
| import java.net.URI; |
| import java.security.PrivilegedExceptionAction; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| |
| import org.apache.commons.lang.StringUtils; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.fs.FileSystem; |
| import org.apache.hadoop.fs.FileSystemTestHelper; |
| import org.apache.hadoop.fs.FsShell; |
| import org.apache.hadoop.fs.Path; |
| import org.apache.hadoop.fs.permission.FsPermission; |
| import org.apache.hadoop.hdfs.MiniDFSCluster; |
| import org.apache.hadoop.security.UserGroupInformation; |
| import org.junit.Test; |
| |
| /** |
| * This test covers privilege related aspects of FsShell |
| * |
| */ |
| public class TestFsShellPermission { |
| |
| static private final String TEST_ROOT = "/testroot"; |
| |
| static UserGroupInformation createUGI(String ownername, String groupName) { |
| return UserGroupInformation.createUserForTesting(ownername, |
| new String[]{groupName}); |
| } |
| |
| private class FileEntry { |
| private String path; |
| private boolean isDir; |
| private String owner; |
| private String group; |
| private String permission; |
| public FileEntry(String path, boolean isDir, |
| String owner, String group, String permission) { |
| this.path = path; |
| this.isDir = isDir; |
| this.owner = owner; |
| this.group = group; |
| this.permission = permission; |
| } |
| String getPath() { return path; } |
| boolean isDirectory() { return isDir; } |
| String getOwner() { return owner; } |
| String getGroup() { return group; } |
| String getPermission() { return permission; } |
| } |
| |
| private void createFiles(FileSystem fs, String topdir, |
| FileEntry[] entries) throws IOException { |
| for (FileEntry entry : entries) { |
| String newPathStr = topdir + "/" + entry.getPath(); |
| Path newPath = new Path(newPathStr); |
| if (entry.isDirectory()) { |
| fs.mkdirs(newPath); |
| } else { |
| FileSystemTestHelper.createFile(fs, newPath); |
| } |
| fs.setPermission(newPath, new FsPermission(entry.getPermission())); |
| fs.setOwner(newPath, entry.getOwner(), entry.getGroup()); |
| } |
| } |
| |
| /** delete directory and everything underneath it.*/ |
| private static void deldir(FileSystem fs, String topdir) throws IOException { |
| fs.delete(new Path(topdir), true); |
| } |
| |
| static String execCmd(FsShell shell, final String[] args) throws Exception { |
| ByteArrayOutputStream baout = new ByteArrayOutputStream(); |
| PrintStream out = new PrintStream(baout, true); |
| PrintStream old = System.out; |
| System.setOut(out); |
| int ret = shell.run(args); |
| out.close(); |
| System.setOut(old); |
| return String.valueOf(ret); |
| } |
| |
| /* |
| * Each instance of TestDeleteHelper captures one testing scenario. |
| * |
| * To create all files listed in fileEntries, and then delete as user |
| * doAsuser the deleteEntry with command+options specified in cmdAndOptions. |
| * |
| * When expectedToDelete is true, the deleteEntry is expected to be deleted; |
| * otherwise, it's not expected to be deleted. At the end of test, |
| * the existence of deleteEntry is checked against expectedToDelete |
| * to ensure the command is finished with expected result |
| */ |
| private class TestDeleteHelper { |
| private FileEntry[] fileEntries; |
| private FileEntry deleteEntry; |
| private String cmdAndOptions; |
| private boolean expectedToDelete; |
| |
| final String doAsGroup; |
| final UserGroupInformation userUgi; |
| |
| public TestDeleteHelper( |
| FileEntry[] fileEntries, |
| FileEntry deleteEntry, |
| String cmdAndOptions, |
| String doAsUser, |
| boolean expectedToDelete) { |
| this.fileEntries = fileEntries; |
| this.deleteEntry = deleteEntry; |
| this.cmdAndOptions = cmdAndOptions; |
| this.expectedToDelete = expectedToDelete; |
| |
| doAsGroup = doAsUser.equals("hdfs")? "supergroup" : "users"; |
| userUgi = createUGI(doAsUser, doAsGroup); |
| } |
| |
| public void execute(Configuration conf, FileSystem fs) throws Exception { |
| fs.mkdirs(new Path(TEST_ROOT)); |
| |
| createFiles(fs, TEST_ROOT, fileEntries); |
| final FsShell fsShell = new FsShell(conf); |
| final String deletePath = TEST_ROOT + "/" + deleteEntry.getPath(); |
| |
| String[] tmpCmdOpts = StringUtils.split(cmdAndOptions); |
| ArrayList<String> tmpArray = new ArrayList<String>(Arrays.asList(tmpCmdOpts)); |
| tmpArray.add(deletePath); |
| final String[] cmdOpts = tmpArray.toArray(new String[tmpArray.size()]); |
| userUgi.doAs(new PrivilegedExceptionAction<String>() { |
| public String run() throws Exception { |
| return execCmd(fsShell, cmdOpts); |
| } |
| }); |
| |
| boolean deleted = !fs.exists(new Path(deletePath)); |
| assertEquals(expectedToDelete, deleted); |
| |
| deldir(fs, TEST_ROOT); |
| } |
| } |
| |
| private TestDeleteHelper genDeleteEmptyDirHelper(final String cmdOpts, |
| final String targetPerm, |
| final String asUser, |
| boolean expectedToDelete) { |
| FileEntry[] files = { |
| new FileEntry("userA", true, "userA", "users", "755"), |
| new FileEntry("userA/userB", true, "userB", "users", targetPerm) |
| }; |
| FileEntry deleteEntry = files[1]; |
| return new TestDeleteHelper(files, deleteEntry, cmdOpts, asUser, |
| expectedToDelete); |
| } |
| |
| // Expect target to be deleted |
| private TestDeleteHelper genRmrEmptyDirWithReadPerm() { |
| return genDeleteEmptyDirHelper("-rm -r", "744", "userA", true); |
| } |
| |
| // Expect target to be deleted |
| private TestDeleteHelper genRmrEmptyDirWithNoPerm() { |
| return genDeleteEmptyDirHelper("-rm -r", "700", "userA", true); |
| } |
| |
| // Expect target to be deleted |
| private TestDeleteHelper genRmrfEmptyDirWithNoPerm() { |
| return genDeleteEmptyDirHelper("-rm -r -f", "700", "userA", true); |
| } |
| |
| private TestDeleteHelper genDeleteNonEmptyDirHelper(final String cmd, |
| final String targetPerm, |
| final String asUser, |
| boolean expectedToDelete) { |
| FileEntry[] files = { |
| new FileEntry("userA", true, "userA", "users", "755"), |
| new FileEntry("userA/userB", true, "userB", "users", targetPerm), |
| new FileEntry("userA/userB/xyzfile", false, "userB", "users", |
| targetPerm) |
| }; |
| FileEntry deleteEntry = files[1]; |
| return new TestDeleteHelper(files, deleteEntry, cmd, asUser, |
| expectedToDelete); |
| } |
| |
| // Expect target not to be deleted |
| private TestDeleteHelper genRmrNonEmptyDirWithReadPerm() { |
| return genDeleteNonEmptyDirHelper("-rm -r", "744", "userA", false); |
| } |
| |
| // Expect target not to be deleted |
| private TestDeleteHelper genRmrNonEmptyDirWithNoPerm() { |
| return genDeleteNonEmptyDirHelper("-rm -r", "700", "userA", false); |
| } |
| |
| // Expect target to be deleted |
| private TestDeleteHelper genRmrNonEmptyDirWithAllPerm() { |
| return genDeleteNonEmptyDirHelper("-rm -r", "777", "userA", true); |
| } |
| |
| // Expect target not to be deleted |
| private TestDeleteHelper genRmrfNonEmptyDirWithNoPerm() { |
| return genDeleteNonEmptyDirHelper("-rm -r -f", "700", "userA", false); |
| } |
| |
| // Expect target to be deleted |
| public TestDeleteHelper genDeleteSingleFileNotAsOwner() throws Exception { |
| FileEntry[] files = { |
| new FileEntry("userA", true, "userA", "users", "755"), |
| new FileEntry("userA/userB", false, "userB", "users", "700") |
| }; |
| FileEntry deleteEntry = files[1]; |
| return new TestDeleteHelper(files, deleteEntry, "-rm -r", "userA", true); |
| } |
| |
| @Test |
| public void testDelete() throws Exception { |
| Configuration conf = null; |
| MiniDFSCluster cluster = null; |
| try { |
| conf = new Configuration(); |
| cluster = new MiniDFSCluster.Builder(conf).numDataNodes(2).build(); |
| |
| String nnUri = FileSystem.getDefaultUri(conf).toString(); |
| FileSystem fs = FileSystem.get(URI.create(nnUri), conf); |
| |
| ArrayList<TestDeleteHelper> ta = new ArrayList<TestDeleteHelper>(); |
| |
| // Add empty dir tests |
| ta.add(genRmrEmptyDirWithReadPerm()); |
| ta.add(genRmrEmptyDirWithNoPerm()); |
| ta.add(genRmrfEmptyDirWithNoPerm()); |
| |
| // Add non-empty dir tests |
| ta.add(genRmrNonEmptyDirWithReadPerm()); |
| ta.add(genRmrNonEmptyDirWithNoPerm()); |
| ta.add(genRmrNonEmptyDirWithAllPerm()); |
| ta.add(genRmrfNonEmptyDirWithNoPerm()); |
| |
| // Add single tile test |
| ta.add(genDeleteSingleFileNotAsOwner()); |
| |
| // Run all tests |
| for(TestDeleteHelper t : ta) { |
| t.execute(conf, fs); |
| } |
| } finally { |
| if (cluster != null) { cluster.shutdown(); } |
| } |
| } |
| } |