|
@@ -0,0 +1,274 @@
|
|
|
+/**
|
|
|
+ * 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(); }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|