blob: ddb8fd04dd835afb19f16920a176371ba72f3bcb [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.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(); }
}
}
}