Repository: incubator-ranger Updated Branches: refs/heads/master 15c818c9e -> 3682de522
RANGER-1031 - Add test for the HIVE plugin Signed-off-by: Velmurugan Periasamy <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/incubator-ranger/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ranger/commit/63d6c830 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ranger/tree/63d6c830 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ranger/diff/63d6c830 Branch: refs/heads/master Commit: 63d6c8309110127a5d33da4a0073b31b22b11ea6 Parents: 15c818c Author: Colm O hEigeartaigh <[email protected]> Authored: Tue Jun 14 10:47:06 2016 +0100 Committer: Velmurugan Periasamy <[email protected]> Committed: Wed Jul 6 12:03:36 2016 -0400 ---------------------------------------------------------------------- hive-agent/pom.xml | 11 + .../services/hive/HIVERangerAuthorizerTest.java | 505 ++++++++++++++++ .../services/hive/RangerAdminClientImpl.java | 84 +++ .../services/hive/client/HiveClientTester.java | 115 ---- .../hive/client/TestRangerServiceHive.java | 137 ----- hive-agent/src/test/resource/log4j.properties | 34 -- .../src/test/resources/hive-policies.json | 592 +++++++++++++++++++ hive-agent/src/test/resources/log4j.properties | 34 ++ .../src/test/resources/ranger-hive-security.xml | 45 ++ hive-agent/src/test/resources/wordcount.txt | 14 + pom.xml | 1 + 11 files changed, 1286 insertions(+), 286 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/63d6c830/hive-agent/pom.xml ---------------------------------------------------------------------- diff --git a/hive-agent/pom.xml b/hive-agent/pom.xml index 316553f..fc5be01 100644 --- a/hive-agent/pom.xml +++ b/hive-agent/pom.xml @@ -121,4 +121,15 @@ <version>${httpcomponents.httpcore.version}</version> </dependency> </dependencies> + <build> + <testResources> + <testResource> + <directory>src/test/resources</directory> + <includes> + <include>**/*</include> + </includes> + <filtering>true</filtering> + </testResource> + </testResources> + </build> </project> http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/63d6c830/hive-agent/src/test/java/org/apache/ranger/services/hive/HIVERangerAuthorizerTest.java ---------------------------------------------------------------------- diff --git a/hive-agent/src/test/java/org/apache/ranger/services/hive/HIVERangerAuthorizerTest.java b/hive-agent/src/test/java/org/apache/ranger/services/hive/HIVERangerAuthorizerTest.java new file mode 100644 index 0000000..4a80207 --- /dev/null +++ b/hive-agent/src/test/java/org/apache/ranger/services/hive/HIVERangerAuthorizerTest.java @@ -0,0 +1,505 @@ +/* + * 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.hive; + +import java.io.File; +import java.net.ServerSocket; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.PrivilegedExceptionAction; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.apache.hadoop.fs.FileUtil; +import org.apache.hadoop.hive.conf.HiveConf; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hive.service.server.HiveServer2; +import org.junit.Assert; + +/** + * + * Here we plug the Ranger RangerHiveAuthorizerFactory into HIVE. + * + * A custom RangerAdminClient is plugged into Ranger in turn, which loads security policies from a local file. These policies were + * generated in the Ranger Admin UI for a service called "HIVETest": + * + * a) A user "bob" can do a select/update on the table "words" + * b) A group called "IT" can do a select only on the "count" column in "words" + * c) "bob" can create any database + * + */ +public class HIVERangerAuthorizerTest { + + private static final File hdfsBaseDir = new File("./target/hdfs/").getAbsoluteFile(); + private static HiveServer2 hiveServer; + private static int port; + + @org.junit.BeforeClass + public static void setup() throws Exception { + // Get a random port + ServerSocket serverSocket = new ServerSocket(0); + port = serverSocket.getLocalPort(); + serverSocket.close(); + + HiveConf conf = new HiveConf(); + + // Warehouse + File warehouseDir = new File("./target/hdfs/warehouse").getAbsoluteFile(); + conf.set(HiveConf.ConfVars.METASTOREWAREHOUSE.varname, warehouseDir.getPath()); + + // Scratchdir + File scratchDir = new File("./target/hdfs/scratchdir").getAbsoluteFile(); + conf.set("hive.exec.scratchdir", scratchDir.getPath()); + + // Create a temporary directory for the Hive metastore + File metastoreDir = new File("./target/rangerauthzmetastore/").getAbsoluteFile(); + conf.set(HiveConf.ConfVars.METASTORECONNECTURLKEY.varname, + String.format("jdbc:derby:;databaseName=%s;create=true", metastoreDir.getPath())); + + conf.set(HiveConf.ConfVars.METASTORE_AUTO_CREATE_ALL.varname, "true"); + conf.set(HiveConf.ConfVars.HIVE_SERVER2_THRIFT_PORT.varname, "" + port); + + // Enable authorization + conf.set(HiveConf.ConfVars.HIVE_AUTHORIZATION_ENABLED.varname, "true"); + conf.set(HiveConf.ConfVars.HIVE_SERVER2_ENABLE_DOAS.varname, "true"); + conf.set(HiveConf.ConfVars.HIVE_AUTHORIZATION_MANAGER.varname, + "org.apache.ranger.authorization.hive.authorizer.RangerHiveAuthorizerFactory"); + + hiveServer = new HiveServer2(); + hiveServer.init(conf); + hiveServer.start(); + + Class.forName("org.apache.hive.jdbc.HiveDriver"); + + // Create database + String initialUrl = "jdbc:hive2://localhost:" + port; + Connection connection = DriverManager.getConnection(initialUrl, "admin", "admin"); + Statement statement = connection.createStatement(); + + statement.execute("CREATE DATABASE rangerauthz"); + + statement.close(); + connection.close(); + + // Load data into HIVE + String url = "jdbc:hive2://localhost:" + port + "/rangerauthz"; + connection = DriverManager.getConnection(url, "admin", "admin"); + statement = connection.createStatement(); + // statement.execute("CREATE TABLE WORDS (word STRING, count INT)"); + statement.execute("create table words (word STRING, count INT) row format delimited fields terminated by '\t' stored as textfile"); + + // Copy "wordcount.txt" to "target" to avoid overwriting it during load + java.io.File inputFile = new java.io.File(HIVERangerAuthorizerTest.class.getResource("../../../../../wordcount.txt").toURI()); + Path outputPath = Paths.get(inputFile.toPath().getParent().getParent().toString() + java.io.File.separator + "wordcountout.txt"); + Files.copy(inputFile.toPath(), outputPath); + + statement.execute("LOAD DATA INPATH '" + outputPath + "' OVERWRITE INTO TABLE words"); + + // Just test to make sure it's working + ResultSet resultSet = statement.executeQuery("SELECT * FROM words where count == '100'"); + resultSet.next(); + Assert.assertEquals("Mr.", resultSet.getString(1)); + + statement.close(); + connection.close(); + } + + @org.junit.AfterClass + public static void cleanup() throws Exception { + hiveServer.stop(); + FileUtil.fullyDelete(hdfsBaseDir); + File metastoreDir = new File("./target/rangerauthzmetastore/").getAbsoluteFile(); + FileUtil.fullyDelete(metastoreDir); + } + + // this should be allowed (by the policy - user) + @org.junit.Test + public void testHiveSelectAllAsBob() throws Exception { + + String url = "jdbc:hive2://localhost:" + port + "/rangerauthz"; + Connection connection = DriverManager.getConnection(url, "bob", "bob"); + Statement statement = connection.createStatement(); + + ResultSet resultSet = statement.executeQuery("SELECT * FROM words where count == '100'"); + resultSet.next(); + Assert.assertEquals("Mr.", resultSet.getString(1)); + Assert.assertEquals(100, resultSet.getInt(2)); + + statement.close(); + connection.close(); + } + + // the "IT" group doesn't have permission to select all + @org.junit.Test + public void testHiveSelectAllAsAlice() throws Exception { + + UserGroupInformation ugi = UserGroupInformation.createUserForTesting("alice", new String[] {"IT"}); + ugi.doAs(new PrivilegedExceptionAction<Void>() { + public Void run() throws Exception { + String url = "jdbc:hive2://localhost:" + port + "/rangerauthz"; + Connection connection = DriverManager.getConnection(url, "alice", "alice"); + Statement statement = connection.createStatement(); + + try { + statement.executeQuery("SELECT * FROM words where count == '100'"); + Assert.fail("Failure expected on an unauthorized call"); + } catch (SQLException ex) { + // expected + } + + statement.close(); + connection.close(); + return null; + } + }); + } + + // this should be allowed (by the policy - user) + @org.junit.Test + public void testHiveSelectSpecificColumnAsBob() throws Exception { + + String url = "jdbc:hive2://localhost:" + port + "/rangerauthz"; + Connection connection = DriverManager.getConnection(url, "bob", "bob"); + Statement statement = connection.createStatement(); + + ResultSet resultSet = statement.executeQuery("SELECT count FROM words where count == '100'"); + resultSet.next(); + Assert.assertEquals(100, resultSet.getInt(1)); + + statement.close(); + connection.close(); + } + + // this should be allowed (by the policy - group) + @org.junit.Test + public void testHiveSelectSpecificColumnAsAlice() throws Exception { + + UserGroupInformation ugi = UserGroupInformation.createUserForTesting("alice", new String[] {"IT"}); + ugi.doAs(new PrivilegedExceptionAction<Void>() { + + public Void run() throws Exception { + String url = "jdbc:hive2://localhost:" + port + "/rangerauthz"; + Connection connection = DriverManager.getConnection(url, "alice", "alice"); + Statement statement = connection.createStatement(); + + ResultSet resultSet = statement.executeQuery("SELECT count FROM words where count == '100'"); + resultSet.next(); + Assert.assertEquals(100, resultSet.getInt(1)); + + statement.close(); + connection.close(); + return null; + } + }); + } + + // An unknown user shouldn't be allowed + @org.junit.Test + public void testHiveSelectSpecificColumnAsEve() throws Exception { + + String url = "jdbc:hive2://localhost:" + port + "/rangerauthz"; + Connection connection = DriverManager.getConnection(url, "eve", "eve"); + Statement statement = connection.createStatement(); + + try { + statement.executeQuery("SELECT count FROM words where count == '100'"); + Assert.fail("Failure expected on an unauthorized call"); + } catch (SQLException ex) { + // expected + } + + statement.close(); + connection.close(); + } + + // test "alice", but in the wrong group + @org.junit.Test + public void testHiveSelectSpecificColumnAsAliceWrongGroup() throws Exception { + + UserGroupInformation ugi = UserGroupInformation.createUserForTesting("alice", new String[] {"DevOps"}); + ugi.doAs(new PrivilegedExceptionAction<Void>() { + + public Void run() throws Exception { + String url = "jdbc:hive2://localhost:" + port + "/rangerauthz"; + Connection connection = DriverManager.getConnection(url, "alice", "alice"); + Statement statement = connection.createStatement(); + + try { + statement.executeQuery("SELECT count FROM words where count == '100'"); + Assert.fail("Failure expected on an unauthorized call"); + } catch (SQLException ex) { + // expected + } + + statement.close(); + connection.close(); + return null; + } + }); + } + + // this should be allowed (by the policy - user) + @org.junit.Test + public void testHiveUpdateAllAsBob() throws Exception { + + String url = "jdbc:hive2://localhost:" + port + "/rangerauthz"; + Connection connection = DriverManager.getConnection(url, "bob", "bob"); + Statement statement = connection.createStatement(); + + statement.execute("insert into words (word, count) values ('newword', 5)"); + + ResultSet resultSet = statement.executeQuery("SELECT * FROM words where word == 'newword'"); + resultSet.next(); + Assert.assertEquals("newword", resultSet.getString(1)); + Assert.assertEquals(5, resultSet.getInt(2)); + + statement.close(); + connection.close(); + } + + // this should not be allowed as "alice" can't insert into the table + @org.junit.Test + public void testHiveUpdateAllAsAlice() throws Exception { + UserGroupInformation ugi = UserGroupInformation.createUserForTesting("alice", new String[] {"IT"}); + ugi.doAs(new PrivilegedExceptionAction<Void>() { + public Void run() throws Exception { + String url = "jdbc:hive2://localhost:" + port + "/rangerauthz"; + Connection connection = DriverManager.getConnection(url, "alice", "alice"); + Statement statement = connection.createStatement(); + + try { + statement.execute("insert into words (word, count) values ('newword2', 5)"); + Assert.fail("Failure expected on an unauthorized call"); + } catch (SQLException ex) { + // expected + } + + + statement.close(); + connection.close(); + return null; + } + }); + } + + @org.junit.Test + public void testHiveCreateDropDatabase() throws Exception { + + String url = "jdbc:hive2://localhost:" + port; + + // Try to create a database as "bob" - this should be allowed + Connection connection = DriverManager.getConnection(url, "bob", "bob"); + Statement statement = connection.createStatement(); + + statement.execute("CREATE DATABASE bobtemp"); + + statement.close(); + connection.close(); + + // Try to create a database as "alice" - this should not be allowed + connection = DriverManager.getConnection(url, "alice", "alice"); + statement = connection.createStatement(); + + try { + statement.execute("CREATE DATABASE alicetemp"); + Assert.fail("Failure expected on an unauthorized call"); + } catch (SQLException ex) { + // expected + } + + // Try to drop a database as "bob" - this should not be allowed + connection = DriverManager.getConnection(url, "bob", "bob"); + statement = connection.createStatement(); + + try { + statement.execute("drop DATABASE bobtemp"); + Assert.fail("Failure expected on an unauthorized call"); + } catch (SQLException ex) { + // expected + } + + // Try to drop a database as "admin" - this should be allowed + connection = DriverManager.getConnection(url, "admin", "admin"); + statement = connection.createStatement(); + + statement.execute("drop DATABASE bobtemp"); + + statement.close(); + connection.close(); + } + + @org.junit.Test + public void testBobSelectOnDifferentDatabase() throws Exception { + + String url = "jdbc:hive2://localhost:" + port; + + // Create a database as "admin" + Connection connection = DriverManager.getConnection(url, "admin", "admin"); + Statement statement = connection.createStatement(); + + statement.execute("CREATE DATABASE admintemp"); + + statement.close(); + connection.close(); + + // Create a "words" table in "admintemp" + url = "jdbc:hive2://localhost:" + port + "/admintemp"; + connection = DriverManager.getConnection(url, "admin", "admin"); + statement = connection.createStatement(); + statement.execute("CREATE TABLE WORDS (word STRING, count INT)"); + + statement.close(); + connection.close(); + + // Now try to read it as "bob" + connection = DriverManager.getConnection(url, "bob", "bob"); + statement = connection.createStatement(); + + try { + statement.executeQuery("SELECT count FROM words where count == '100'"); + Assert.fail("Failure expected on an unauthorized call"); + } catch (SQLException ex) { + // expected + } + + statement.close(); + connection.close(); + + // Drop the table and database as "admin" + connection = DriverManager.getConnection(url, "admin", "admin"); + statement = connection.createStatement(); + + statement.execute("drop TABLE words"); + statement.execute("drop DATABASE admintemp"); + + statement.close(); + connection.close(); + } + + @org.junit.Test + public void testBobSelectOnDifferentTables() throws Exception { + + // Create a "words2" table in "rangerauthz" + String url = "jdbc:hive2://localhost:" + port + "/rangerauthz"; + Connection connection = DriverManager.getConnection(url, "admin", "admin"); + Statement statement = connection.createStatement(); + statement.execute("CREATE TABLE WORDS2 (word STRING, count INT)"); + + statement.close(); + connection.close(); + + // Now try to read it as "bob" + connection = DriverManager.getConnection(url, "bob", "bob"); + statement = connection.createStatement(); + + try { + statement.executeQuery("SELECT count FROM words2 where count == '100'"); + Assert.fail("Failure expected on an unauthorized call"); + } catch (SQLException ex) { + // expected + } + + statement.close(); + connection.close(); + + // Drop the table as "admin" + connection = DriverManager.getConnection(url, "admin", "admin"); + statement = connection.createStatement(); + + statement.execute("drop TABLE words2"); + + statement.close(); + connection.close(); + } + + @org.junit.Test + public void testBobAlter() throws Exception { + + String url = "jdbc:hive2://localhost:" + port + "/rangerauthz"; + + // Create a new table as admin + Connection connection = DriverManager.getConnection(url, "admin", "admin"); + Statement statement = connection.createStatement(); + statement.execute("CREATE TABLE WORDS2 (word STRING, count INT)"); + + statement.close(); + connection.close(); + + // Try to add a new column in words as "bob" - this should fail + url = "jdbc:hive2://localhost:" + port + "/rangerauthz"; + connection = DriverManager.getConnection(url, "bob", "bob"); + statement = connection.createStatement(); + + try { + statement.execute("ALTER TABLE WORDS2 ADD COLUMNS (newcol STRING)"); + Assert.fail("Failure expected on an unauthorized call"); + } catch (SQLException ex) { + // expected + } + + statement.close(); + connection.close(); + + // Now alter it as "admin" + connection = DriverManager.getConnection(url, "admin", "admin"); + statement = connection.createStatement(); + + statement.execute("ALTER TABLE WORDS2 ADD COLUMNS (newcol STRING)"); + + statement.close(); + connection.close(); + + // Try to alter it as "bob" - this should fail + connection = DriverManager.getConnection(url, "bob", "bob"); + statement = connection.createStatement(); + + try { + statement.execute("ALTER TABLE WORDS2 REPLACE COLUMNS (word STRING, count INT)"); + Assert.fail("Failure expected on an unauthorized call"); + } catch (SQLException ex) { + // expected + } + + statement.close(); + connection.close(); + + // Now alter it as "admin" + connection = DriverManager.getConnection(url, "admin", "admin"); + statement = connection.createStatement(); + + statement.execute("ALTER TABLE WORDS2 REPLACE COLUMNS (word STRING, count INT)"); + + statement.close(); + connection.close(); + + // Drop the table as "admin" + connection = DriverManager.getConnection(url, "admin", "admin"); + statement = connection.createStatement(); + + statement.execute("drop TABLE words2"); + + statement.close(); + connection.close(); + } + + +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/63d6c830/hive-agent/src/test/java/org/apache/ranger/services/hive/RangerAdminClientImpl.java ---------------------------------------------------------------------- diff --git a/hive-agent/src/test/java/org/apache/ranger/services/hive/RangerAdminClientImpl.java b/hive-agent/src/test/java/org/apache/ranger/services/hive/RangerAdminClientImpl.java new file mode 100644 index 0000000..32e1966 --- /dev/null +++ b/hive-agent/src/test/java/org/apache/ranger/services/hive/RangerAdminClientImpl.java @@ -0,0 +1,84 @@ +/* + * 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.hive; + +import java.io.File; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.util.List; + +import org.apache.ranger.admin.client.RangerAdminClient; +import org.apache.ranger.plugin.util.GrantRevokeRequest; +import org.apache.ranger.plugin.util.ServicePolicies; +import org.apache.ranger.plugin.util.ServiceTags; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * A test implementation of the RangerAdminClient interface that just reads policies in from a file and returns them + */ +public class RangerAdminClientImpl implements RangerAdminClient { + private static final Logger LOG = LoggerFactory.getLogger(RangerAdminClientImpl.class); + private final static String cacheFilename = "hive-policies.json"; + private Gson gson; + + public void init(String serviceName, String appId, String configPropertyPrefix) { + Gson gson = null; + try { + gson = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z").setPrettyPrinting().create(); + } catch(Throwable excp) { + LOG.error("RangerAdminClientImpl: failed to create GsonBuilder object", excp); + } + this.gson = gson; + } + + public ServicePolicies getServicePoliciesIfUpdated(long lastKnownVersion) throws Exception { + + String basedir = System.getProperty("basedir"); + if (basedir == null) { + basedir = new File(".").getCanonicalPath(); + } + + java.nio.file.Path cachePath = FileSystems.getDefault().getPath(basedir, "/src/test/resources/" + cacheFilename); + byte[] cacheBytes = Files.readAllBytes(cachePath); + + return gson.fromJson(new String(cacheBytes), ServicePolicies.class); + } + + public void grantAccess(GrantRevokeRequest request) throws Exception { + + } + + public void revokeAccess(GrantRevokeRequest request) throws Exception { + + } + + public ServiceTags getServiceTagsIfUpdated(long lastKnownVersion) throws Exception { + return null; + + } + + public List<String> getTagTypes(String tagTypePattern) throws Exception { + return null; + } + + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/63d6c830/hive-agent/src/test/java/org/apache/ranger/services/hive/client/HiveClientTester.java ---------------------------------------------------------------------- diff --git a/hive-agent/src/test/java/org/apache/ranger/services/hive/client/HiveClientTester.java b/hive-agent/src/test/java/org/apache/ranger/services/hive/client/HiveClientTester.java deleted file mode 100644 index 1ff0f35..0000000 --- a/hive-agent/src/test/java/org/apache/ranger/services/hive/client/HiveClientTester.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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.hive.client; - - -import java.io.IOException; -import java.io.InputStream; -import java.util.HashMap; -import java.util.List; -import java.util.Properties; - - -public class HiveClientTester { - - public static void main(String[] args) throws Throwable { - - HiveClient hc = null ; - - if (args.length <= 2) { - System.err.println("USAGE: java " + HiveClientTester.class.getName() + " dataSourceName propertyFile <databaseName> <tableName> <columnName>") ; - System.exit(1) ; - } - - - try { - - Properties conf = new Properties() ; - - InputStream in = HiveClientTester.class.getClassLoader().getResourceAsStream(args[1]) ; - try { - conf.load(in); - } - finally { - if (in != null) { - try { - in.close(); - } - catch(IOException ioe) { - // Ignore IOException when closing the stream - } - } - } - - HashMap<String,String> prop = new HashMap<String,String>() ; - for(Object key : conf.keySet()) { - Object val = conf.get(key) ; - prop.put((String)key, (String)val) ; - } - - - hc = new HiveClient(args[0], prop) ; - - - if (args.length == 3) { - List<String> dbList = hc.getDatabaseList(args[2],null) ; - if (dbList.size() == 0) { - System.out.println("No database found with db filter [" + args[2] + "]") ; - } - else { - for (String str : dbList ) { - System.out.println("database: " + str ) ; - } - } - } - else if (args.length == 4) { - List<String> tableList = hc.getTableList(args[3], null, null) ; - if (tableList.size() == 0) { - System.out.println("No tables found under database[" + args[2] + "] with table filter [" + args[3] + "]") ; - } - else { - for(String str : tableList) { - System.out.println("Table: " + str) ; - } - } - } - else if (args.length == 5) { - List<String> columnList = hc.getColumnList(args[4], null, null, null) ; - if (columnList.size() == 0) { - System.out.println("No columns found for db:" + args[2] + ", table: [" + args[3] + "], with column filter [" + args[4] + "]") ; - } - else { - for (String str : columnList ) { - System.out.println("Column: " + str) ; - } - } - } - - } - finally { - if (hc != null) { - hc.close(); - } - } - - } - - -} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/63d6c830/hive-agent/src/test/java/org/apache/ranger/services/hive/client/TestRangerServiceHive.java ---------------------------------------------------------------------- diff --git a/hive-agent/src/test/java/org/apache/ranger/services/hive/client/TestRangerServiceHive.java b/hive-agent/src/test/java/org/apache/ranger/services/hive/client/TestRangerServiceHive.java deleted file mode 100644 index a676b13..0000000 --- a/hive-agent/src/test/java/org/apache/ranger/services/hive/client/TestRangerServiceHive.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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.hive.client; - -import static org.junit.Assert.*; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.ranger.plugin.client.HadoopException; -import org.apache.ranger.plugin.model.RangerService; -import org.apache.ranger.plugin.model.RangerServiceDef; -import org.apache.ranger.plugin.service.ResourceLookupContext; -import org.apache.ranger.services.hive.RangerServiceHive; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - - -public class TestRangerServiceHive { - - static final String sdName = "svcDef-Hive"; - static final String serviceName = "HiveDef"; - HashMap<String, Object> responseData = null; - Map<String, String> configs = null; - RangerServiceHive svcHive = null; - RangerServiceDef sd = null; - RangerService svc = null; - ResourceLookupContext lookupContext = null; - - - @Before - public void setup() { - configs = new HashMap<String,String>(); - lookupContext = new ResourceLookupContext(); - - buildHbaseConnectionConfig(); - buildLookupContext(); - - sd = new RangerServiceDef(sdName, "org.apache.ranger.services.hive.RangerServiceHive", "TestHiveService", "test servicedef description", null, null, null, null, null, null, null); - svc = new RangerService(sdName, serviceName, "unit test hive resource lookup and validateConfig", null, configs); - svcHive = new RangerServiceHive(); - svcHive.init(sd, svc); - } - - @Test - public void testValidateConfig() { - - /* TODO: does this test require a live Hive environment? - * - HashMap<String,Object> ret = null; - String errorMessage = null; - - try { - ret = svcHive.validateConfig(); - }catch (Exception e) { - errorMessage = e.getMessage(); - if ( e instanceof HadoopException) { - errorMessage = "HadoopException"; - } - } - - if ( errorMessage != null) { - assertTrue(errorMessage.contains("HadoopException")); - } else { - assertNotNull(ret); - } - * - */ - } - - - @Test - public void testLookUpResource() { - /* TODO: does this test require a live Hive environment? - * - List<String> ret = new ArrayList<String>(); - String errorMessage = null; - try { - ret = svcHive.lookupResource(lookupContext); - }catch (Exception e) { - errorMessage = e.getMessage(); - if ( e instanceof HadoopException) { - errorMessage = "HadoopException"; - } - } - if ( errorMessage != null) { - assertTrue(errorMessage.contains("HadoopException")); - } else { - assertNull(ret); - } - * - */ - - } - - public void buildHbaseConnectionConfig() { - configs.put("username", "hiveuser"); - configs.put("password", "*******"); - configs.put("jdbc.driverClassName", "org.apache.hive.jdbc.HiveDriver"); - configs.put("jdbc.url ", "jdbc:hive2://localhost:10000/default"); - } - - public void buildLookupContext() { - Map<String, List<String>> resourceMap = new HashMap<String,List<String>>(); - resourceMap.put("database", null); - lookupContext.setUserInput("x"); - lookupContext.setResourceName("database"); - lookupContext.setResources(resourceMap); - } - - @After - public void tearDown() { - sd = null; - svc = null; - } - -} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/63d6c830/hive-agent/src/test/resource/log4j.properties ---------------------------------------------------------------------- diff --git a/hive-agent/src/test/resource/log4j.properties b/hive-agent/src/test/resource/log4j.properties deleted file mode 100644 index f7ab2ba..0000000 --- a/hive-agent/src/test/resource/log4j.properties +++ /dev/null @@ -1,34 +0,0 @@ -# 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. - -##-- To prevent junits from cluttering the build run by default all test runs send output to null appender -log4j.appender.devnull=org.apache.log4j.varia.NullAppender -ranger.root.logger=FATAL,devnull - -##-- uncomment the following line during during development/debugging so see debug messages during test run to be emitted to console -# ranger.root.logger=DEBUG,console -log4j.rootLogger=${ranger.root.logger} - -# Logging Threshold -log4j.threshold=ALL - -# -# console -# Add "console" to rootlogger above if you want to use this -# -log4j.appender.console=org.apache.log4j.ConsoleAppender -log4j.appender.console.target=System.err -log4j.appender.console.layout=org.apache.log4j.PatternLayout -log4j.appender.console.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2}: %m%n http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/63d6c830/hive-agent/src/test/resources/hive-policies.json ---------------------------------------------------------------------- diff --git a/hive-agent/src/test/resources/hive-policies.json b/hive-agent/src/test/resources/hive-policies.json new file mode 100644 index 0000000..6b86751 --- /dev/null +++ b/hive-agent/src/test/resources/hive-policies.json @@ -0,0 +1,592 @@ +{ + "serviceName": "HIVETest", + "serviceId": 7, + "policyVersion": 12, + "policyUpdateTime": "20160610-11:07:20.000-+0100", + "policies": [ + { + "service": "HIVETest", + "name": "HIVETest-1-20160609091836", + "description": "Default Policy for Service: HIVETest", + "resourceSignature": "6e79c1c989c79b7e53af663d3bdc2de6", + "isAuditEnabled": true, + "resources": { + "database": { + "values": [ + "*" + ], + "isExcludes": false, + "isRecursive": false + }, + "column": { + "values": [ + "*" + ], + "isExcludes": false, + "isRecursive": false + }, + "table": { + "values": [ + "*" + ], + "isExcludes": false, + "isRecursive": false + } + }, + "policyItems": [ + { + "accesses": [ + { + "type": "select", + "isAllowed": true + }, + { + "type": "update", + "isAllowed": true + }, + { + "type": "create", + "isAllowed": true + }, + { + "type": "drop", + "isAllowed": true + }, + { + "type": "alter", + "isAllowed": true + }, + { + "type": "index", + "isAllowed": true + }, + { + "type": "lock", + "isAllowed": true + }, + { + "type": "all", + "isAllowed": true + } + ], + "users": [ + "admin" + ], + "groups": [], + "conditions": [], + "delegateAdmin": true + }, + { + "accesses": [ + { + "type": "create", + "isAllowed": true + } + ], + "users": [ + "bob" + ], + "groups": [], + "conditions": [], + "delegateAdmin": false + } + ], + "denyPolicyItems": [], + "allowExceptions": [], + "denyExceptions": [], + "dataMaskPolicyItems": [], + "rowFilterPolicyItems": [], + "id": 30, + "guid": "1465463916045_476_1125", + "isEnabled": true, + "createdBy": "Admin", + "updatedBy": "Admin", + "createTime": "20160609-10:18:36.000-+0100", + "updateTime": "20160609-11:09:59.000-+0100", + "version": 2 + }, + { + "service": "HIVETest", + "name": "HIVETest-2-20160609091836", + "description": "Default Policy for Service: HIVETest", + "resourceSignature": "c834ed2b8c7462d2aa8bbffdb05226c8", + "isAuditEnabled": true, + "resources": { + "database": { + "values": [ + "*" + ], + "isExcludes": false, + "isRecursive": false + }, + "udf": { + "values": [ + "*" + ], + "isExcludes": false, + "isRecursive": false + } + }, + "policyItems": [ + { + "accesses": [ + { + "type": "select", + "isAllowed": true + }, + { + "type": "update", + "isAllowed": true + }, + { + "type": "create", + "isAllowed": true + }, + { + "type": "drop", + "isAllowed": true + }, + { + "type": "alter", + "isAllowed": true + }, + { + "type": "index", + "isAllowed": true + }, + { + "type": "lock", + "isAllowed": true + }, + { + "type": "all", + "isAllowed": true + } + ], + "users": [ + "admin" + ], + "groups": [], + "conditions": [], + "delegateAdmin": true + } + ], + "denyPolicyItems": [], + "allowExceptions": [], + "denyExceptions": [], + "dataMaskPolicyItems": [], + "rowFilterPolicyItems": [], + "id": 31, + "guid": "1465463916203_470_1126", + "isEnabled": true, + "createdBy": "Admin", + "updatedBy": "Admin", + "createTime": "20160609-10:18:36.000-+0100", + "updateTime": "20160609-10:18:36.000-+0100", + "version": 1 + }, + { + "service": "HIVETest", + "name": "SelectUpdateAllWords", + "description": "", + "resourceSignature": "e6175b643348d03ee0aab3a381257e6e", + "isAuditEnabled": true, + "resources": { + "database": { + "values": [ + "rangerauthz" + ], + "isExcludes": false, + "isRecursive": false + }, + "column": { + "values": [ + "*" + ], + "isExcludes": false, + "isRecursive": false + }, + "table": { + "values": [ + "words" + ], + "isExcludes": false, + "isRecursive": false + } + }, + "policyItems": [ + { + "accesses": [ + { + "type": "select", + "isAllowed": true + }, + { + "type": "update", + "isAllowed": true + } + ], + "users": [ + "bob" + ], + "groups": [], + "conditions": [], + "delegateAdmin": false + } + ], + "denyPolicyItems": [], + "allowExceptions": [], + "denyExceptions": [], + "dataMaskPolicyItems": [], + "rowFilterPolicyItems": [], + "id": 32, + "guid": "1465464434667_623_1161", + "isEnabled": true, + "createdBy": "Admin", + "updatedBy": "Admin", + "createTime": "20160609-10:27:14.000-+0100", + "updateTime": "20160610-10:59:16.000-+0100", + "version": 7 + }, + { + "service": "HIVETest", + "name": "SelectCountWords", + "description": "", + "resourceSignature": "ec1331571f87b9c87464086d961f4e89", + "isAuditEnabled": true, + "resources": { + "database": { + "values": [ + "rangerauthz" + ], + "isExcludes": false, + "isRecursive": false + }, + "column": { + "values": [ + "count" + ], + "isExcludes": false, + "isRecursive": false + }, + "table": { + "values": [ + "words" + ], + "isExcludes": false, + "isRecursive": false + } + }, + "policyItems": [ + { + "accesses": [ + { + "type": "select", + "isAllowed": true + } + ], + "users": [], + "groups": [ + "IT" + ], + "conditions": [], + "delegateAdmin": false + } + ], + "denyPolicyItems": [], + "allowExceptions": [], + "denyExceptions": [], + "dataMaskPolicyItems": [], + "rowFilterPolicyItems": [], + "id": 33, + "guid": "1465466645682_874_1202", + "isEnabled": true, + "createdBy": "Admin", + "updatedBy": "Admin", + "createTime": "20160609-11:04:05.000-+0100", + "updateTime": "20160610-11:07:20.000-+0100", + "version": 5 + }, + { + "service": "HIVETest", + "name": "TmpUpdatePolicy", + "description": "", + "resourceSignature": "87d65f69544e63cfe302db8f9d006b6b", + "isAuditEnabled": true, + "resources": { + "database": { + "values": [ + "*" + ], + "isExcludes": false, + "isRecursive": false + }, + "column": { + "values": [ + "*" + ], + "isExcludes": false, + "isRecursive": false + }, + "table": { + "values": [ + "*__tmp__*" + ], + "isExcludes": false, + "isRecursive": false + } + }, + "policyItems": [ + { + "accesses": [ + { + "type": "select", + "isAllowed": true + } + ], + "users": [ + "bob" + ], + "groups": [ + "IT" + ], + "conditions": [], + "delegateAdmin": false + } + ], + "denyPolicyItems": [], + "allowExceptions": [], + "denyExceptions": [], + "dataMaskPolicyItems": [], + "rowFilterPolicyItems": [], + "id": 34, + "guid": "1465553055683_559_1400", + "isEnabled": true, + "createdBy": "Admin", + "updatedBy": "Admin", + "createTime": "20160610-11:04:15.000-+0100", + "updateTime": "20160610-11:04:15.000-+0100", + "version": 1 + } + ], + "serviceDef": { + "name": "hive", + "implClass": "org.apache.ranger.services.hive.RangerServiceHive", + "label": "Hive Server2", + "description": "Hive Server2", + "options": {}, + "configs": [ + { + "itemId": 1, + "name": "username", + "type": "string", + "mandatory": true, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Username" + }, + { + "itemId": 2, + "name": "password", + "type": "password", + "mandatory": true, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Password" + }, + { + "itemId": 3, + "name": "jdbc.driverClassName", + "type": "string", + "mandatory": true, + "defaultValue": "org.apache.hive.jdbc.HiveDriver", + "validationRegEx": "", + "validationMessage": "", + "uiHint": "" + }, + { + "itemId": 4, + "name": "jdbc.url", + "type": "string", + "mandatory": true, + "defaultValue": "", + "validationRegEx": "", + "validationMessage": "", + "uiHint": "" + }, + { + "itemId": 5, + "name": "commonNameForCertificate", + "type": "string", + "mandatory": false, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Common Name for Certificate" + } + ], + "resources": [ + { + "itemId": 1, + "name": "database", + "type": "string", + "level": 10, + "mandatory": true, + "lookupSupported": true, + "recursiveSupported": false, + "excludesSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": "true", + "ignoreCase": "true" + }, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Hive Database", + "description": "Hive Database" + }, + { + "itemId": 2, + "name": "table", + "type": "string", + "level": 20, + "parent": "database", + "mandatory": true, + "lookupSupported": true, + "recursiveSupported": false, + "excludesSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": "true", + "ignoreCase": "true" + }, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Hive Table", + "description": "Hive Table" + }, + { + "itemId": 3, + "name": "udf", + "type": "string", + "level": 20, + "parent": "database", + "mandatory": true, + "lookupSupported": true, + "recursiveSupported": false, + "excludesSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": "true", + "ignoreCase": "true" + }, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Hive UDF", + "description": "Hive UDF" + }, + { + "itemId": 4, + "name": "column", + "type": "string", + "level": 30, + "parent": "table", + "mandatory": true, + "lookupSupported": true, + "recursiveSupported": false, + "excludesSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": "true", + "ignoreCase": "true" + }, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Hive Column", + "description": "Hive Column" + } + ], + "accessTypes": [ + { + "itemId": 1, + "name": "select", + "label": "select", + "impliedGrants": [] + }, + { + "itemId": 2, + "name": "update", + "label": "update", + "impliedGrants": [] + }, + { + "itemId": 3, + "name": "create", + "label": "Create", + "impliedGrants": [] + }, + { + "itemId": 4, + "name": "drop", + "label": "Drop", + "impliedGrants": [] + }, + { + "itemId": 5, + "name": "alter", + "label": "Alter", + "impliedGrants": [] + }, + { + "itemId": 6, + "name": "index", + "label": "Index", + "impliedGrants": [] + }, + { + "itemId": 7, + "name": "lock", + "label": "Lock", + "impliedGrants": [] + }, + { + "itemId": 8, + "name": "all", + "label": "All", + "impliedGrants": [ + "select", + "update", + "create", + "drop", + "alter", + "index", + "lock" + ] + } + ], + "policyConditions": [], + "contextEnrichers": [], + "enums": [], + "dataMaskDef": { + "maskTypes": [], + "accessTypes": [], + "resources": [] + }, + "rowFilterDef": { + "accessTypes": [], + "resources": [] + }, + "id": 3, + "guid": "3e1afb5a-184a-4e82-9d9c-87a5cacc243c", + "isEnabled": true, + "createTime": "20160314-14:39:35.000-+0000", + "updateTime": "20160314-14:39:35.000-+0000", + "version": 1 + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/63d6c830/hive-agent/src/test/resources/log4j.properties ---------------------------------------------------------------------- diff --git a/hive-agent/src/test/resources/log4j.properties b/hive-agent/src/test/resources/log4j.properties new file mode 100644 index 0000000..f7ab2ba --- /dev/null +++ b/hive-agent/src/test/resources/log4j.properties @@ -0,0 +1,34 @@ +# 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. + +##-- To prevent junits from cluttering the build run by default all test runs send output to null appender +log4j.appender.devnull=org.apache.log4j.varia.NullAppender +ranger.root.logger=FATAL,devnull + +##-- uncomment the following line during during development/debugging so see debug messages during test run to be emitted to console +# ranger.root.logger=DEBUG,console +log4j.rootLogger=${ranger.root.logger} + +# Logging Threshold +log4j.threshold=ALL + +# +# console +# Add "console" to rootlogger above if you want to use this +# +log4j.appender.console=org.apache.log4j.ConsoleAppender +log4j.appender.console.target=System.err +log4j.appender.console.layout=org.apache.log4j.PatternLayout +log4j.appender.console.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2}: %m%n http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/63d6c830/hive-agent/src/test/resources/ranger-hive-security.xml ---------------------------------------------------------------------- diff --git a/hive-agent/src/test/resources/ranger-hive-security.xml b/hive-agent/src/test/resources/ranger-hive-security.xml new file mode 100644 index 0000000..46fd28e --- /dev/null +++ b/hive-agent/src/test/resources/ranger-hive-security.xml @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<!-- + 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. +--> +<?xml-stylesheet type="text/xsl" href="configuration.xsl"?> +<configuration xmlns:xi="http://www.w3.org/2001/XInclude"> + + <property> + <name>ranger.plugin.hive.service.name</name> + <value>HIVETest</value> + <description> + Name of the Ranger service containing policies for this SampleApp instance + </description> + </property> + + <property> + <name>ranger.plugin.hive.policy.source.impl</name> + <value>org.apache.ranger.services.hive.RangerAdminClientImpl</value> + <description> + Policy source. + </description> + </property> + + <property> + <name>ranger.plugin.hive.policy.cache.dir</name> + <value>${project.build.directory}</value> + <description> + Directory where Ranger policies are cached after successful retrieval from the source + </description> + </property> + +</configuration> http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/63d6c830/hive-agent/src/test/resources/wordcount.txt ---------------------------------------------------------------------- diff --git a/hive-agent/src/test/resources/wordcount.txt b/hive-agent/src/test/resources/wordcount.txt new file mode 100644 index 0000000..d6b8503 --- /dev/null +++ b/hive-agent/src/test/resources/wordcount.txt @@ -0,0 +1,14 @@ +Most 2 +Moth'?" 1 +Mother 2 +Moving 1 +Mr. 100 +Mrs. 87 +Mulgrave 2 +Munich 1 +Murdered 1 +Murray 3 +Murray's 8 +Murray._ 1 +Murray?" 1 +Murray_. 2 http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/63d6c830/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 158e6db..72fbd52 100644 --- a/pom.xml +++ b/pom.xml @@ -531,6 +531,7 @@ <exclude>**/testdata/*.json</exclude> <exclude>atlassian-ide-plugin.xml</exclude> <exclude>**/.pydevproject</exclude> + <exclude>**/derby.log</exclude> </excludes> </configuration> </plugin>
