HDFS-11421. Make WebHDFS' ACLs RegEx configurable. Contributed by Harsh J.

This commit is contained in:
Xiao Chen 2017-02-24 16:49:16 -08:00
parent d2b3ba9b8f
commit e24ed47d9a
8 changed files with 104 additions and 6 deletions

View File

@ -35,6 +35,8 @@ public interface HdfsClientConfigKeys {
String DFS_WEBHDFS_USER_PATTERN_KEY = String DFS_WEBHDFS_USER_PATTERN_KEY =
"dfs.webhdfs.user.provider.user.pattern"; "dfs.webhdfs.user.provider.user.pattern";
String DFS_WEBHDFS_USER_PATTERN_DEFAULT = "^[A-Za-z_][A-Za-z0-9._-]*[$]?$"; String DFS_WEBHDFS_USER_PATTERN_DEFAULT = "^[A-Za-z_][A-Za-z0-9._-]*[$]?$";
String DFS_WEBHDFS_ACL_PERMISSION_PATTERN_KEY =
"dfs.webhdfs.acl.provider.permission.pattern";
String DFS_WEBHDFS_ACL_PERMISSION_PATTERN_DEFAULT = String DFS_WEBHDFS_ACL_PERMISSION_PATTERN_DEFAULT =
"^(default:)?(user|group|mask|other):[[A-Za-z_][A-Za-z0-9._-]]*:([rwx-]{3})?(,(default:)?(user|group|mask|other):[[A-Za-z_][A-Za-z0-9._-]]*:([rwx-]{3})?)*$"; "^(default:)?(user|group|mask|other):[[A-Za-z_][A-Za-z0-9._-]]*:([rwx-]{3})?(,(default:)?(user|group|mask|other):[[A-Za-z_][A-Za-z0-9._-]]*:([rwx-]{3})?)*$";

View File

@ -183,10 +183,14 @@ public synchronized void initialize(URI uri, Configuration conf
) throws IOException { ) throws IOException {
super.initialize(uri, conf); super.initialize(uri, conf);
setConf(conf); setConf(conf);
/** set user pattern based on configuration file */
// set user and acl patterns based on configuration file
UserParam.setUserPattern(conf.get( UserParam.setUserPattern(conf.get(
HdfsClientConfigKeys.DFS_WEBHDFS_USER_PATTERN_KEY, HdfsClientConfigKeys.DFS_WEBHDFS_USER_PATTERN_KEY,
HdfsClientConfigKeys.DFS_WEBHDFS_USER_PATTERN_DEFAULT)); HdfsClientConfigKeys.DFS_WEBHDFS_USER_PATTERN_DEFAULT));
AclPermissionParam.setAclPermissionPattern(conf.get(
HdfsClientConfigKeys.DFS_WEBHDFS_ACL_PERMISSION_PATTERN_KEY,
HdfsClientConfigKeys.DFS_WEBHDFS_ACL_PERMISSION_PATTERN_DEFAULT));
boolean isOAuth = conf.getBoolean( boolean isOAuth = conf.getBoolean(
HdfsClientConfigKeys.DFS_WEBHDFS_OAUTH_ENABLED_KEY, HdfsClientConfigKeys.DFS_WEBHDFS_OAUTH_ENABLED_KEY,

View File

@ -24,6 +24,7 @@
import java.util.List; import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import com.google.common.annotations.VisibleForTesting;
import org.apache.hadoop.fs.permission.AclEntry; import org.apache.hadoop.fs.permission.AclEntry;
/** AclPermission parameter. */ /** AclPermission parameter. */
@ -33,7 +34,7 @@ public class AclPermissionParam extends StringParam {
/** Default parameter value. */ /** Default parameter value. */
public static final String DEFAULT = ""; public static final String DEFAULT = "";
private static final Domain DOMAIN = new Domain(NAME, private static Domain DOMAIN = new Domain(NAME,
Pattern.compile(DFS_WEBHDFS_ACL_PERMISSION_PATTERN_DEFAULT)); Pattern.compile(DFS_WEBHDFS_ACL_PERMISSION_PATTERN_DEFAULT));
/** /**
@ -49,6 +50,20 @@ public AclPermissionParam(List<AclEntry> acl) {
super(DOMAIN,parseAclSpec(acl).equals(DEFAULT) ? null : parseAclSpec(acl)); super(DOMAIN,parseAclSpec(acl).equals(DEFAULT) ? null : parseAclSpec(acl));
} }
@VisibleForTesting
public static Domain getAclPermissionPattern() {
return DOMAIN;
}
@VisibleForTesting
public static void setAclPermissionPattern(Domain dm) {
DOMAIN = dm;
}
public static void setAclPermissionPattern(String pattern) {
DOMAIN = new Domain(NAME, Pattern.compile(pattern));
}
@Override @Override
public String getName() { public String getName() {
return NAME; return NAME;

View File

@ -48,11 +48,12 @@
import org.apache.hadoop.fs.permission.FsCreateModes; import org.apache.hadoop.fs.permission.FsCreateModes;
import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hdfs.DFSClient; import org.apache.hadoop.hdfs.DFSClient;
import org.apache.hadoop.hdfs.DFSConfigKeys; import org.apache.hadoop.hdfs.client.HdfsClientConfigKeys;
import org.apache.hadoop.hdfs.client.HdfsDataInputStream; import org.apache.hadoop.hdfs.client.HdfsDataInputStream;
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier; import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
import org.apache.hadoop.hdfs.web.JsonUtil; import org.apache.hadoop.hdfs.web.JsonUtil;
import org.apache.hadoop.hdfs.web.WebHdfsFileSystem; import org.apache.hadoop.hdfs.web.WebHdfsFileSystem;
import org.apache.hadoop.hdfs.web.resources.AclPermissionParam;
import org.apache.hadoop.hdfs.web.resources.GetOpParam; import org.apache.hadoop.hdfs.web.resources.GetOpParam;
import org.apache.hadoop.hdfs.web.resources.PostOpParam; import org.apache.hadoop.hdfs.web.resources.PostOpParam;
import org.apache.hadoop.hdfs.web.resources.PutOpParam; import org.apache.hadoop.hdfs.web.resources.PutOpParam;
@ -112,8 +113,11 @@ public WebHdfsHandler(Configuration conf, Configuration confForCreate)
this.confForCreate = confForCreate; this.confForCreate = confForCreate;
/** set user pattern based on configuration file */ /** set user pattern based on configuration file */
UserParam.setUserPattern( UserParam.setUserPattern(
conf.get(DFSConfigKeys.DFS_WEBHDFS_USER_PATTERN_KEY, conf.get(HdfsClientConfigKeys.DFS_WEBHDFS_USER_PATTERN_KEY,
DFSConfigKeys.DFS_WEBHDFS_USER_PATTERN_DEFAULT)); HdfsClientConfigKeys.DFS_WEBHDFS_USER_PATTERN_DEFAULT));
AclPermissionParam.setAclPermissionPattern(
conf.get(HdfsClientConfigKeys.DFS_WEBHDFS_ACL_PERMISSION_PATTERN_KEY,
HdfsClientConfigKeys.DFS_WEBHDFS_ACL_PERMISSION_PATTERN_DEFAULT));
} }
@Override @Override

View File

@ -41,6 +41,7 @@
import org.apache.hadoop.hdfs.server.namenode.web.resources.NamenodeWebHdfsMethods; import org.apache.hadoop.hdfs.server.namenode.web.resources.NamenodeWebHdfsMethods;
import org.apache.hadoop.hdfs.web.AuthFilter; import org.apache.hadoop.hdfs.web.AuthFilter;
import org.apache.hadoop.hdfs.web.WebHdfsFileSystem; import org.apache.hadoop.hdfs.web.WebHdfsFileSystem;
import org.apache.hadoop.hdfs.web.resources.AclPermissionParam;
import org.apache.hadoop.hdfs.web.resources.Param; import org.apache.hadoop.hdfs.web.resources.Param;
import org.apache.hadoop.hdfs.web.resources.UserParam; import org.apache.hadoop.hdfs.web.resources.UserParam;
import org.apache.hadoop.http.HttpConfig; import org.apache.hadoop.http.HttpConfig;
@ -80,6 +81,9 @@ private void initWebHdfs(Configuration conf) throws IOException {
UserParam.setUserPattern(conf.get( UserParam.setUserPattern(conf.get(
HdfsClientConfigKeys.DFS_WEBHDFS_USER_PATTERN_KEY, HdfsClientConfigKeys.DFS_WEBHDFS_USER_PATTERN_KEY,
HdfsClientConfigKeys.DFS_WEBHDFS_USER_PATTERN_DEFAULT)); HdfsClientConfigKeys.DFS_WEBHDFS_USER_PATTERN_DEFAULT));
AclPermissionParam.setAclPermissionPattern(conf.get(
HdfsClientConfigKeys.DFS_WEBHDFS_ACL_PERMISSION_PATTERN_KEY,
HdfsClientConfigKeys.DFS_WEBHDFS_ACL_PERMISSION_PATTERN_DEFAULT));
// add authentication filter for webhdfs // add authentication filter for webhdfs
final String className = conf.get( final String className = conf.get(

View File

@ -2477,6 +2477,14 @@
</description> </description>
</property> </property>
<property>
<name>dfs.webhdfs.acl.provider.permission.pattern</name>
<value>^(default:)?(user|group|mask|other):[[A-Za-z_][A-Za-z0-9._-]]*:([rwx-]{3})?(,(default:)?(user|group|mask|other):[[A-Za-z_][A-Za-z0-9._-]]*:([rwx-]{3})?)*$</value>
<description>
Valid pattern for user and group names in webhdfs acl operations, it must be a valid java regex.
</description>
</property>
<property> <property>
<name>dfs.webhdfs.socket.connect-timeout</name> <name>dfs.webhdfs.socket.connect-timeout</name>
<value>60s</value> <value>60s</value>

View File

@ -49,6 +49,7 @@
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import com.google.common.collect.ImmutableList;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -64,6 +65,9 @@
import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.RemoteIterator; import org.apache.hadoop.fs.RemoteIterator;
import org.apache.hadoop.fs.StorageType; import org.apache.hadoop.fs.StorageType;
import org.apache.hadoop.fs.permission.AclEntry;
import org.apache.hadoop.fs.permission.AclEntryScope;
import org.apache.hadoop.fs.permission.AclEntryType;
import org.apache.hadoop.fs.permission.FsAction; import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hdfs.DFSConfigKeys; import org.apache.hadoop.hdfs.DFSConfigKeys;
@ -379,10 +383,17 @@ public Void run() throws IOException, URISyntaxException {
} }
@Test(timeout=300000) @Test(timeout=300000)
public void testNumericalUserName() throws Exception { public void testCustomizedUserAndGroupNames() throws Exception {
final Configuration conf = WebHdfsTestUtil.createConf(); final Configuration conf = WebHdfsTestUtil.createConf();
conf.setBoolean(DFSConfigKeys.DFS_NAMENODE_ACLS_ENABLED_KEY, true);
// Modify username pattern to allow numeric usernames
conf.set(HdfsClientConfigKeys.DFS_WEBHDFS_USER_PATTERN_KEY, "^[A-Za-z0-9_][A-Za-z0-9" + conf.set(HdfsClientConfigKeys.DFS_WEBHDFS_USER_PATTERN_KEY, "^[A-Za-z0-9_][A-Za-z0-9" +
"._-]*[$]?$"); "._-]*[$]?$");
// Modify acl pattern to allow numeric and "@" characters user/groups in ACL spec
conf.set(HdfsClientConfigKeys.DFS_WEBHDFS_ACL_PERMISSION_PATTERN_KEY,
"^(default:)?(user|group|mask|other):" +
"[[0-9A-Za-z_][@A-Za-z0-9._-]]*:([rwx-]{3})?(,(default:)?" +
"(user|group|mask|other):[[0-9A-Za-z_][@A-Za-z0-9._-]]*:([rwx-]{3})?)*$");
final MiniDFSCluster cluster = final MiniDFSCluster cluster =
new MiniDFSCluster.Builder(conf).numDataNodes(1).build(); new MiniDFSCluster.Builder(conf).numDataNodes(1).build();
try { try {
@ -391,6 +402,7 @@ public void testNumericalUserName() throws Exception {
.setPermission(new Path("/"), .setPermission(new Path("/"),
new FsPermission(FsAction.ALL, FsAction.ALL, FsAction.ALL)); new FsPermission(FsAction.ALL, FsAction.ALL, FsAction.ALL));
// Test a numeric username
UserGroupInformation.createUserForTesting("123", new String[]{"my-group"}) UserGroupInformation.createUserForTesting("123", new String[]{"my-group"})
.doAs(new PrivilegedExceptionAction<Void>() { .doAs(new PrivilegedExceptionAction<Void>() {
@Override @Override
@ -399,6 +411,21 @@ public Void run() throws IOException, URISyntaxException {
WebHdfsConstants.WEBHDFS_SCHEME); WebHdfsConstants.WEBHDFS_SCHEME);
Path d = new Path("/my-dir"); Path d = new Path("/my-dir");
Assert.assertTrue(fs.mkdirs(d)); Assert.assertTrue(fs.mkdirs(d));
// Test also specifying a default ACL with a numeric username
// and another of a groupname with '@'
fs.modifyAclEntries(d, ImmutableList.of(
new AclEntry.Builder()
.setPermission(FsAction.READ)
.setScope(AclEntryScope.DEFAULT)
.setType(AclEntryType.USER)
.setName("11010")
.build(),
new AclEntry.Builder()
.setPermission(FsAction.READ_WRITE)
.setType(AclEntryType.GROUP)
.setName("foo@bar")
.build()
));
return null; return null;
} }
}); });

View File

@ -352,6 +352,40 @@ public void testAclPermissionParam() {
} }
} }
@Test
public void testUserGroupOkAfterAlteringAclPattern() {
// Preserve default pattern value
AclPermissionParam.Domain oldDomain =
AclPermissionParam.getAclPermissionPattern();
// Override the pattern with one that accepts '@' and numbers
// in the first character of usernames/groupnames
String newPattern =
"^(default:)?(user|group|mask|other):" +
"[[0-9A-Za-z_][@A-Za-z0-9._-]]*:([rwx-]{3})?" +
"(,(default:)?(user|group|mask|other):" +
"[[0-9A-Za-z_][@A-Za-z0-9._-]]*:([rwx-]{3})?)*$";
try {
AclPermissionParam.setAclPermissionPattern(newPattern);
String numericUserSpec = "user:110201:rwx";
AclPermissionParam aclNumericUserParam =
new AclPermissionParam(numericUserSpec);
Assert.assertEquals(numericUserSpec, aclNumericUserParam.getValue());
String oddGroupSpec = "group:foo@bar:rwx";
AclPermissionParam aclGroupWithDomainParam =
new AclPermissionParam(oddGroupSpec);
Assert.assertEquals(oddGroupSpec, aclGroupWithDomainParam.getValue());
} finally {
// Revert back to the default rules for remainder of tests
AclPermissionParam.setAclPermissionPattern(oldDomain);
}
}
@Test @Test
public void testXAttrNameParam() { public void testXAttrNameParam() {
final XAttrNameParam p = new XAttrNameParam("user.a1"); final XAttrNameParam p = new XAttrNameParam("user.a1");