Java tutorial
/* * 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.ranger.services.hdfs; import static org.mockito.Mockito.when; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.fs.permission.FsAction; import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.hdfs.server.namenode.INode; import org.apache.hadoop.hdfs.server.namenode.INodeAttributeProvider.AccessControlEnforcer; import org.apache.hadoop.hdfs.server.namenode.INodeAttributes; import org.apache.hadoop.security.AccessControlException; import org.apache.hadoop.security.UserGroupInformation; import org.apache.ranger.authorization.hadoop.RangerHdfsAuthorizer; import org.apache.ranger.authorization.hadoop.config.RangerConfiguration; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.mockito.Mockito; /** * Direct tests for the {@link RangerHdfsAuthorizer} without going through the HDFS layer. * */ public class RangerHdfsAuthorizerTest { private final static int SNAPSHOT_ID = 1; private final static String FILE_OWNER = "fileOwner"; private final static String FILE_GROUP = "superGroup"; private static final FsPermission READ_ONLY = new FsPermission(FsAction.READ, FsAction.NONE, FsAction.NONE); private static final FsPermission EXEC_BY_OWNER = new FsPermission(FsAction.EXECUTE, FsAction.NONE, FsAction.NONE); private static RangerHdfsAuthorizer authorizer; private static AccessControlEnforcer rangerControlEnforcer; static class TestFileSystem { final String path; final String[] pathSegments; final INode[] nodes; final INodeAttributes[] attributes; final int ancestorIndex; TestFileSystem(String path) { this.path = path; pathSegments = path.split("/"); nodes = new INode[pathSegments.length]; attributes = new INode[pathSegments.length]; for (int i = 0; i < pathSegments.length; i++) { boolean file = i == pathSegments.length - 1; INode node = createNode(pathSegments, i, FILE_OWNER, FILE_GROUP, file); nodes[i] = node; attributes[i] = node; } ancestorIndex = nodes.length - 2; } public void setFilePermission(FsPermission fsPermission) { when(nodes[nodes.length - 1].getFsPermission()).thenReturn(fsPermission); } public void setParentDirectoryPermission(FsPermission fsPermission) { when(nodes[nodes.length - 2].getFsPermission()).thenReturn(fsPermission); } /** * Checks that the <b>directory</b> access is <b>allowed</b> for the given user in the given groups. * Throws an exception, if not. */ public void checkDirAccess(FsAction access, String userName, String... groups) throws AccessControlException { final UserGroupInformation user = UserGroupInformation.createUserForTesting(userName, groups); rangerControlEnforcer.checkPermission(FILE_OWNER, FILE_GROUP, user, Arrays.copyOf(attributes, attributes.length - 1), Arrays.copyOf(nodes, nodes.length - 1), new byte[0][0], SNAPSHOT_ID, path, ancestorIndex - 1, false /* doCheckOwner */, null /* ancestorAccess */, null /* parentAccess */ , access, null /* subAccess */ , false /* ignoreEmptyDir */); } /** * Checks that the <b>file</b> access is <b>allowed</b> for the given user in the given groups. * Throws an exception, if not. */ public void checkAccess(FsAction access, String userName, String... groups) throws AccessControlException { final UserGroupInformation user = UserGroupInformation.createUserForTesting(userName, groups); rangerControlEnforcer.checkPermission(FILE_OWNER, FILE_GROUP, user, attributes, nodes, new byte[0][0], SNAPSHOT_ID, path, ancestorIndex, false /* doCheckOwner */, null /* ancestorAccess */, null /* parentAccess */ , access, null /* subAccess */ , false /* ignoreEmptyDir */); } /** * Checks that the <b>file</b> access is <b>blocked</b> for the given user in the given groups. * Throws an exception, if not. */ public void checkAccessBlocked(FsAction access, String userName, String... groups) throws AccessControlException { try { checkAccess(access, userName, groups); Assert.fail("Access should be blocked for " + path + " access=" + access + " for user=" + userName + " groups=" + groups); } catch (AccessControlException ace) { Assert.assertNotNull(ace); } } /** * Checks that the <b>directory</b> access is <b>blocked</b> for the given user in the given groups. * Throws an exception, if not. */ public void checkDirAccessBlocked(FsAction access, String userName, String... groups) throws AccessControlException { try { checkDirAccess(access, userName, groups); Assert.fail("Access should be blocked for parent directory of " + path + " access=" + access + " for user=" + userName + " groups=" + Arrays.asList(groups)); } catch (AccessControlException ace) { Assert.assertNotNull(ace); } } } @BeforeClass public static void setup() { try { File file = File.createTempFile("hdfs-version-site", ".xml"); file.deleteOnExit(); try (final FileOutputStream outStream = new FileOutputStream(file); final OutputStreamWriter writer = new OutputStreamWriter(outStream, StandardCharsets.UTF_8)) { writer.write( "<configuration>\n" + " <property>\n" + " <name>hdfs.version</name>\n" + " <value>hdfs_version_3.0</value>\n" + " </property>\n" + "</configuration>\n"); } RangerConfiguration config = RangerConfiguration.getInstance(); config.addResource(new org.apache.hadoop.fs.Path(file.toURI())); } catch (Exception exception) { Assert.fail("Cannot create hdfs-version-site file:[" + exception.getMessage() + "]"); } authorizer = new RangerHdfsAuthorizer(); authorizer.start(); AccessControlEnforcer accessControlEnforcer = Mockito.mock(AccessControlEnforcer.class); rangerControlEnforcer = authorizer.getExternalAccessControlEnforcer(accessControlEnforcer); } @AfterClass public static void teardown() { authorizer.stop(); } @Test public void testAccessControlEnforcer() { Assert.assertNotNull("rangerControlEnforcer", rangerControlEnforcer); } @Test public void readTest() throws AccessControlException { readPath("/tmp/tmpdir/data-file2"); } @Test public void readTestUsingTagPolicy() throws Exception { final TestFileSystem fs = new TestFileSystem("/tmp/tmpdir6/data-file2"); fs.setFilePermission(READ_ONLY); // Try to read the files as "bob" - this should be allowed (by the policy - user) fs.checkAccess(null, "bob"); // traverse check fs.checkAccess(FsAction.READ, "bob"); // read check // Now try to read the file as "alice" - this should be allowed (by the policy - group) fs.checkAccess(null, "alice", "IT"); // traverse check fs.checkAccess(FsAction.READ, "alice", "IT"); // read check // Now try to read the file as unknown user "eve" - this should not be allowed fs.checkAccess(null, "eve"); // traverse check fs.checkAccessBlocked(FsAction.READ, "eve"); // read deny check to public // Now try to read the file as user "dave" - this should not be allowed fs.checkAccess(null, "dave"); // traverse check fs.checkAccessBlocked(FsAction.READ, "dave"); // read deny check for public } @Test public void writeTest() throws AccessControlException { final TestFileSystem fs = new TestFileSystem("/tmp/tmpdir2/data-file3"); // Try to write to the file as "bob" - this should be allowed (by the policy - user) fs.checkAccess(null, "bob"); // traverse check fs.checkAccess(FsAction.WRITE, "bob"); // write check // Now try to write to the file as "alice" - this should be allowed (by the policy - group) fs.checkAccess(null, "alice", "IT"); // traverse check fs.checkAccess(FsAction.WRITE, "alice", "IT"); // write check // Now try to write the file as unknown user "eve" - this should not be allowed fs.checkAccessBlocked(null, "eve"); // traverse check fs.checkAccessBlocked(FsAction.WRITE, "eve"); // write deny check for public } @Test public void executeTest() throws AccessControlException { final TestFileSystem fs = new TestFileSystem("/tmp/tmpdir3/data-file2"); fs.setFilePermission(READ_ONLY); fs.setParentDirectoryPermission(EXEC_BY_OWNER); // Try to list the files as "bob" - this should be allowed (by the policy - user) fs.checkDirAccess(null, "bob"); // traverse check fs.checkDirAccess(FsAction.READ_EXECUTE, "bob"); // dir list check // Try to list the directory as "alice" - this should be allowed (by the policy - group) fs.checkDirAccess(null, "alice", "IT"); // traverse check fs.checkDirAccess(FsAction.READ_EXECUTE, "alice", "IT"); // dir list check // Now try to list the directory as unknown user "eve" - this should not be allowed fs.checkDirAccessBlocked(null, "eve"); // traverse check fs.checkDirAccessBlocked(FsAction.READ_EXECUTE, "eve"); // dir list deny check for public } @Test public void HDFSFileNameTokenReadTest() throws AccessControlException { readPath("/tmp/tmpdir4/data-file"); readFailWithPath("/tmp/tmpdir4/t/abc"); } @Test public void HDFSBaseFileNameTokenReadTest() throws AccessControlException { readPath("/tmp/tmpdir5/data-file.txt"); readFailWithPath("/tmp/tmpdir5/data-file.csv"); readFailWithPath("/tmp/tmpdir5/t/data-file.txt"); } private void readFailWithPath(String path) throws AccessControlException { final TestFileSystem fs = new TestFileSystem(path); fs.setFilePermission(READ_ONLY); // Now try to read the file as "bob" - this should NOT be allowed fs.checkAccessBlocked(FsAction.READ, "bob"); // read check // Now try to read the file as "alice" - this should NOT be allowed fs.checkAccessBlocked(FsAction.READ, "alice", "IT"); // read check // Now try to read the file as unknown user "eve" - this should not be allowed fs.checkAccessBlocked(FsAction.READ, "eve", "IT"); // read deny check for public } private void readPath(String path) throws AccessControlException { final TestFileSystem fs = new TestFileSystem(path); fs.setFilePermission(READ_ONLY); // Try to read the files as "bob" - this should be allowed (by the policy - user) fs.checkAccess(null, "bob"); // traverse check fs.checkAccess(FsAction.READ, "bob"); // read check // Now try to read the file as "alice" - this should be allowed (by the policy - group) fs.checkAccess(null, "alice", "IT"); // traverse check fs.checkAccess(FsAction.READ, "alice", "IT"); // read check // Now try to read the file as unknown user "eve" - this should not be allowed fs.checkAccessBlocked(null, "eve"); // traverse deny check for public fs.checkAccessBlocked(FsAction.READ, "eve"); // read deny check for public } private static INode createNode(String[] pathSegments, int i, String owner, String group, boolean file) { String fullPath = StringUtils.join(pathSegments, '/', 0, i + 1); String name = pathSegments[i]; INode mock = Mockito.mock(INode.class); try { when(mock.getLocalNameBytes()).thenReturn(name.getBytes("UTF-8")); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } when(mock.getUserName()).thenReturn(owner); when(mock.getGroupName()).thenReturn(group); when(mock.getFullPathName()).thenReturn(fullPath); when(mock.isFile()).thenReturn(file); when(mock.isDirectory()).thenReturn(!file); when(mock.toString()).thenReturn(name); return mock; } }