This is an automated email from the ASF dual-hosted git repository.
spolavarapu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push:
new a07bde1 RANGER-2927: Python client libraries for Publicv2APIs
a07bde1 is described below
commit a07bde1024518126b4881b0e71f4fd30b8405edd
Author: Abhishek Kumar <[email protected]>
AuthorDate: Fri Aug 14 19:26:42 2020 -0400
RANGER-2927: Python client libraries for Publicv2APIs
Signed-off-by: Sailaja Polavarapu <[email protected]>
---
.gitignore | 1 +
intg/.gitignore | 3 +-
intg/pom.xml | 107 ++++-
intg/scripts/build.sh | 23 +
intg/scripts/setup.py | 30 ++
.../main/java/org/apache/ranger/RangerClient.java | 8 +-
intg/src/main/python/__init__.py | 17 +
intg/src/main/python/ranger/client/__init__.py | 17 +
.../src/main/python/ranger/client/ranger_client.py | 496 +++++++++++++++++++++
intg/src/main/python/ranger/model/__init__.py | 17 +
.../ranger/model/grant_revoke_role_request.py | 39 ++
intg/src/main/python/ranger/model/ranger_base.py | 34 ++
intg/src/main/python/ranger/model/ranger_policy.py | 127 ++++++
intg/src/main/python/ranger/model/ranger_role.py | 48 ++
.../python/ranger/model/ranger_security_zone.py | 48 ++
.../src/main/python/ranger/model/ranger_service.py | 43 ++
.../main/python/ranger/model/ranger_service_def.py | 136 ++++++
intg/src/main/resources/logging.conf | 40 ++
intg/src/test/python/test_ranger_client.py | 109 +++++
pom.xml | 5 +-
.../distro/src/main/assembly/sample-client.xml | 21 +-
ranger-examples/sample-client/.gitignore | 3 +-
.../sample-client/conf/config.properties | 21 +
ranger-examples/sample-client/pom.xml | 38 ++
.../sample-client/scripts/run-pyclient.sh | 50 +++
ranger-examples/sample-client/scripts/setup.py | 30 ++
.../sample-client/src/main/python/sample_client.py | 110 +++++
27 files changed, 1611 insertions(+), 10 deletions(-)
diff --git a/.gitignore b/.gitignore
index c2def9d..e351815 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@
.classpath
.project
/target/
+/venv/
winpkg/target
.DS_Store
.idea
diff --git a/intg/.gitignore b/intg/.gitignore
index a6f89c2..897593c 100644
--- a/intg/.gitignore
+++ b/intg/.gitignore
@@ -1 +1,2 @@
-/target/
\ No newline at end of file
+/target/
+/venv/
\ No newline at end of file
diff --git a/intg/pom.xml b/intg/pom.xml
index c8eb330..83b87d8 100644
--- a/intg/pom.xml
+++ b/intg/pom.xml
@@ -19,7 +19,6 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <packaging>jar</packaging>
<parent>
<artifactId>ranger</artifactId>
<groupId>org.apache.ranger</groupId>
@@ -28,6 +27,112 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>ranger-intg</artifactId>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pmd-plugin</artifactId>
+ <version>${maven.pmd.plugin.version}</version>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>check</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <rulesets>
+
<ruleset>${project.parent.basedir}/dev-support/ranger-pmd-ruleset.xml</ruleset>
+ </rulesets>
+ <sourceEncoding>UTF-8</sourceEncoding>
+ <failOnViolation>true</failOnViolation>
+ <linkXRef>false</linkXRef>
+ <includeTests>true</includeTests>
+ <verbose>true</verbose>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-clean-plugin</artifactId>
+ <version>2.6.1</version>
+ <configuration>
+ <filesets>
+ <fileset>
+ <directory>venv</directory>
+ </fileset>
+ </filesets>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>${maven.exec.plugin.version}</version>
+ <executions>
+ <execution>
+ <configuration>
+ <skip>${skipTests}</skip>
+ <executable>python3</executable>
+ <arguments>
+ <argument>-m</argument>
+ <argument>venv</argument>
+ <argument>venv</argument>
+ </arguments>
+ </configuration>
+ <id>create-venv</id>
+ <phase>validate</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ </execution>
+ <execution>
+ <phase>test</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <configuration>
+ <skip>${skipTests}</skip>
+ <executable>bash</executable>
+ <commandlineArgs>scripts/build.sh</commandlineArgs>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>2.7</version>
+ <executions>
+ <execution>
+ <id>copy-resources</id>
+ <phase>initialize</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>venv/pylib</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/python</directory>
+ <includes>
+ <include>**/*.py</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>src/test/python</directory>
+ </resource>
+ <resource>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
<dependencies>
<dependency>
<groupId>org.apache.ranger</groupId>
diff --git a/intg/scripts/build.sh b/intg/scripts/build.sh
new file mode 100644
index 0000000..a875da1
--- /dev/null
+++ b/intg/scripts/build.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# 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.
+
+cd venv || exit
+source bin/activate
+INSTALL="python3 ./../scripts/setup.py install"
+$INSTALL >> build.log
+TEST="python3 build/lib/pylib/test_ranger_client.py"
+$TEST
diff --git a/intg/scripts/setup.py b/intg/scripts/setup.py
new file mode 100644
index 0000000..9ce9773
--- /dev/null
+++ b/intg/scripts/setup.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+
+#
+# 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.
+
+from setuptools import setup, find_packages
+
+# External dependencies
+requirements = ['requests>=2.24']
+
+setup(
+ name='pyclient',
+ packages=['pylib', 'pylib/ranger/client', 'pylib/ranger/model'],
+ data_files=[('../../lib/resources',
['./../src/main/resources/logging.conf'])],
+ install_requires=requirements,
+ zip_safe=False
+)
diff --git a/intg/src/main/java/org/apache/ranger/RangerClient.java
b/intg/src/main/java/org/apache/ranger/RangerClient.java
index 0e2fe56..bef3857 100644
--- a/intg/src/main/java/org/apache/ranger/RangerClient.java
+++ b/intg/src/main/java/org/apache/ranger/RangerClient.java
@@ -468,7 +468,7 @@ public class RangerClient {
}
if (clientResponse == null) {
- throw new RangerServiceException(api, clientResponse);
+ throw new RangerServiceException(api, null);
} else if (clientResponse.getStatus() ==
api.getExpectedStatus().getStatusCode()) {
if (responseType != null) {
ret = clientResponse.getEntity(responseType);
@@ -542,11 +542,9 @@ public class RangerClient {
try {
URI uri = new URI(path);
- if (uri != null) {
- URI normalizedUri = uri.normalize();
+ URI normalizedUri = uri.normalize();
- ret = normalizedUri.toString();
- }
+ ret = normalizedUri.toString();
} catch (Exception e) {
LOG.error("getNormalizedPath() caught exception for path={}",
path, e);
diff --git a/intg/src/main/python/__init__.py b/intg/src/main/python/__init__.py
new file mode 100644
index 0000000..ed9d0b3
--- /dev/null
+++ b/intg/src/main/python/__init__.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+#
+# 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.
diff --git a/intg/src/main/python/ranger/client/__init__.py
b/intg/src/main/python/ranger/client/__init__.py
new file mode 100644
index 0000000..ed9d0b3
--- /dev/null
+++ b/intg/src/main/python/ranger/client/__init__.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+#
+# 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.
diff --git a/intg/src/main/python/ranger/client/ranger_client.py
b/intg/src/main/python/ranger/client/ranger_client.py
new file mode 100644
index 0000000..319261d
--- /dev/null
+++ b/intg/src/main/python/ranger/client/ranger_client.py
@@ -0,0 +1,496 @@
+#!/usr/bin/env python
+
+#
+# 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.
+
+
+import enum
+import json
+import logging
+import requests
+import logging.config
+
+from os import path
+from http import HTTPStatus
+from requests import Session, Response
+
+from ranger.model.ranger_role import RangerRole
+from ranger.model.ranger_policy import RangerPolicy
+from ranger.model.ranger_service import RangerService
+from ranger.model.ranger_service_def import RangerServiceDef
+from ranger.model.ranger_security_zone import RangerSecurityZone
+
+
+APPLICATION_JSON = 'application/json'
+LOG_CONFIG = path.dirname(__file__) + '/../../../resources/logging.conf'
+
+logging.config.fileConfig(LOG_CONFIG, disable_existing_loggers=False)
+requests.packages.urllib3.disable_warnings()
+
+
+class Message:
+ def __init__(self, name=None, rbKey=None, message=None, objectId=None,
fieldName=None):
+ self.name = name
+ self.rbKey = rbKey
+ self.message = message
+ self.objectId = objectId
+ self.fieldName = fieldName
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class RESTResponse:
+ def __init__(self, httpStatusCode=None, statusCode=None, msgDesc=None,
messageList=None):
+ self.httpStatusCode = httpStatusCode
+ self.statusCode = statusCode
+ self.msgDesc = msgDesc
+ self.messageList = messageList if messageList is not None else []
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class HttpMethod(enum.Enum):
+ GET = "GET"
+ PUT = "PUT"
+ POST = "POST"
+ DELETE = "DELETE"
+
+
+class API:
+ def __init__(self, path, method, expected_status,
consumes=APPLICATION_JSON, produces=APPLICATION_JSON):
+ self.log = logging.getLogger(__name__)
+ self.path = path
+ self.method = method
+ self.expected_status = expected_status
+ self.consumes = consumes
+ self.produces = produces
+
+ def call(self, session, baseUrl, params, request):
+ session.headers['Accept'] = self.consumes
+ session.headers['Content-type'] = self.produces
+
+ if self.log.isEnabledFor(logging.DEBUG):
+ self.log.debug('==> call({},{},{})'.format(self, params, request))
+
self.log.debug('------------------------------------------------------')
+ self.log.debug('Call : {} {}'.format(self.method,
self.path))
+ self.log.debug('Content-type : {}'.format(self.consumes))
+ self.log.debug('Accept : {}'.format(self.produces))
+
+ if request is not None:
+ self.log.debug("Request : {}".format(request))
+
+ path = baseUrl + self.path
+
+ if self.method == HttpMethod.GET:
+ client_response = session.get(path)
+ elif self.method == HttpMethod.POST:
+ client_response = session.post(path, data=request.__repr__())
+ elif self.method == HttpMethod.PUT:
+ client_response = session.put(path, data=request.__repr__())
+ elif self.method == HttpMethod.DELETE:
+ client_response = session.delete(path, params=params)
+ else:
+ raise Exception('Unsupported HTTP Method {}'.format(self.method))
+
+ if client_response.status_code == self.expected_status:
+ if client_response.content:
+ if self.log.isEnabledFor(logging.DEBUG):
+ self.log.debug('Response: {}'.format(client_response.text))
+ else:
+ return None
+ elif client_response.status_code == HTTPStatus.SERVICE_UNAVAILABLE:
+ self.log.error('Ranger Admin unavailable. HTTP Status:
{}'.format(HTTPStatus.SERVICE_UNAVAILABLE))
+ self.log.error("client_response=: {}" + str(client_response))
+ return None
+ else:
+ raise Exception(client_response.text)
+
+ __json = json.loads(str(json.dumps(client_response.json())))
+
+ if self.log.isEnabledFor(logging.DEBUG):
+ self.log.debug('<== call({},{},{}), result = {}'.format(self,
params, request, client_response))
+
+ return __json
+
+ def apply_url_params(self, params):
+ try:
+ return API(self.path.format(**params), self.method,
self.expected_status, self.consumes, self.produces)
+ except (KeyError, TypeError) as e:
+ self.log.error('Arguments not formatted properly' + str(e))
+ raise e
+
+
+class RangerClient:
+ # Query Params
+ PARAM_ID = "id"
+ PARAM_NAME = "name"
+ PARAM_DAYS = "days"
+ PARAM_EXEC_USER = "execUser"
+ PARAM_POLICY_NAME = "policyname"
+ PARAM_SERVICE_NAME = "serviceName"
+ PARAM_RELOAD_SERVICE_POLICIES_CACHE = "reloadServicePoliciesCache"
+
+ # URIs
+ URI_BASE = "/service/public/v2/api"
+
+ URI_SERVICEDEF = URI_BASE + "/servicedef"
+ URI_SERVICEDEF_BY_ID = URI_SERVICEDEF + "/{id}"
+ URI_SERVICEDEF_BY_NAME = URI_SERVICEDEF + "/name/{name}"
+
+ URI_SERVICE = URI_BASE + "/service"
+ URI_SERVICE_BY_ID = URI_SERVICE + "/{id}"
+ URI_SERVICE_BY_NAME = URI_SERVICE + "/name/{name}"
+ URI_POLICIES_IN_SERVICE = URI_SERVICE + "/{serviceName}/policy"
+
+ URI_POLICY = URI_BASE + "/policy"
+ URI_APPLY_POLICY = URI_POLICY + "/apply"
+ URI_POLICY_BY_ID = URI_POLICY + "/{id}"
+ URI_POLICY_BY_NAME = URI_SERVICE + "/{serviceName}/policy/{policyname}"
+
+ URI_ROLE = URI_BASE + "/roles"
+ URI_ROLE_NAMES = URI_ROLE + "/names"
+ URI_ROLE_BY_ID = URI_ROLE + "/{id}"
+ URI_ROLE_BY_NAME = URI_ROLE + "/name/{name}"
+ URI_USER_ROLES = URI_ROLE + "/user/{name}"
+ URI_GRANT_ROLE = URI_ROLE + "/grant/{name}"
+ URI_REVOKE_ROLE = URI_ROLE + "/revoke/{name}"
+
+ URI_ZONE = URI_BASE + "/zones"
+ URI_ZONE_BY_ID = URI_ZONE + "/{id}"
+ URI_ZONE_BY_NAME = URI_ZONE + "/name/{name}"
+
+ URI_PLUGIN_INFO = URI_BASE + "/plugins/info"
+ URI_POLICY_DELTAS = URI_BASE + "/server/policydeltas"
+
+ # APIs
+ CREATE_SERVICEDEF = API(URI_SERVICEDEF, HttpMethod.POST,
HTTPStatus.OK)
+ UPDATE_SERVICEDEF_BY_ID = API(URI_SERVICEDEF_BY_ID, HttpMethod.PUT,
HTTPStatus.OK)
+ UPDATE_SERVICEDEF_BY_NAME = API(URI_SERVICEDEF_BY_NAME, HttpMethod.PUT,
HTTPStatus.OK)
+ DELETE_SERVICEDEF_BY_ID = API(URI_SERVICEDEF_BY_ID, HttpMethod.DELETE,
HTTPStatus.NO_CONTENT)
+ DELETE_SERVICEDEF_BY_NAME = API(URI_SERVICEDEF_BY_NAME, HttpMethod.DELETE,
HTTPStatus.NO_CONTENT)
+ GET_SERVICEDEF_BY_ID = API(URI_SERVICEDEF_BY_ID, HttpMethod.GET,
HTTPStatus.OK)
+ GET_SERVICEDEF_BY_NAME = API(URI_SERVICEDEF_BY_NAME, HttpMethod.GET,
HTTPStatus.OK)
+ FIND_SERVICEDEFS = API(URI_SERVICEDEF, HttpMethod.GET,
HTTPStatus.OK)
+
+ CREATE_SERVICE = API(URI_SERVICE, HttpMethod.POST, HTTPStatus.OK)
+ UPDATE_SERVICE_BY_ID = API(URI_SERVICE_BY_ID, HttpMethod.PUT,
HTTPStatus.OK)
+ UPDATE_SERVICE_BY_NAME = API(URI_SERVICE_BY_NAME, HttpMethod.PUT,
HTTPStatus.OK)
+ DELETE_SERVICE_BY_ID = API(URI_SERVICE_BY_ID, HttpMethod.DELETE,
HTTPStatus.NO_CONTENT)
+ DELETE_SERVICE_BY_NAME = API(URI_SERVICE_BY_NAME, HttpMethod.DELETE,
HTTPStatus.NO_CONTENT)
+ GET_SERVICE_BY_ID = API(URI_SERVICE_BY_ID, HttpMethod.GET,
HTTPStatus.OK)
+ GET_SERVICE_BY_NAME = API(URI_SERVICE_BY_NAME, HttpMethod.GET,
HTTPStatus.OK)
+ FIND_SERVICES = API(URI_SERVICE, HttpMethod.GET, HTTPStatus.OK)
+
+ CREATE_POLICY = API(URI_POLICY, HttpMethod.POST, HTTPStatus.OK)
+ UPDATE_POLICY_BY_ID = API(URI_POLICY_BY_ID, HttpMethod.PUT,
HTTPStatus.OK)
+ UPDATE_POLICY_BY_NAME = API(URI_POLICY_BY_NAME, HttpMethod.PUT,
HTTPStatus.OK)
+ APPLY_POLICY = API(URI_APPLY_POLICY, HttpMethod.POST,
HTTPStatus.OK)
+ DELETE_POLICY_BY_ID = API(URI_POLICY_BY_ID, HttpMethod.DELETE,
HTTPStatus.NO_CONTENT)
+ DELETE_POLICY_BY_NAME = API(URI_POLICY, HttpMethod.DELETE,
HTTPStatus.NO_CONTENT)
+ GET_POLICY_BY_ID = API(URI_POLICY_BY_ID, HttpMethod.GET,
HTTPStatus.OK)
+ GET_POLICY_BY_NAME = API(URI_POLICY_BY_NAME, HttpMethod.GET,
HTTPStatus.OK)
+ GET_POLICIES_IN_SERVICE = API(URI_POLICIES_IN_SERVICE, HttpMethod.GET,
HTTPStatus.OK)
+ FIND_POLICIES = API(URI_POLICY, HttpMethod.GET, HTTPStatus.OK)
+
+ CREATE_ZONE = API(URI_ZONE, HttpMethod.POST, HTTPStatus.OK)
+ UPDATE_ZONE_BY_ID = API(URI_ZONE_BY_ID, HttpMethod.PUT, HTTPStatus.OK)
+ UPDATE_ZONE_BY_NAME = API(URI_ZONE_BY_NAME, HttpMethod.PUT, HTTPStatus.OK)
+ DELETE_ZONE_BY_ID = API(URI_ZONE_BY_ID, HttpMethod.DELETE,
HTTPStatus.NO_CONTENT)
+ DELETE_ZONE_BY_NAME = API(URI_ZONE_BY_NAME, HttpMethod.DELETE,
HTTPStatus.NO_CONTENT)
+ GET_ZONE_BY_ID = API(URI_ZONE_BY_ID, HttpMethod.GET, HTTPStatus.OK)
+ GET_ZONE_BY_NAME = API(URI_ZONE_BY_NAME, HttpMethod.GET, HTTPStatus.OK)
+ FIND_ZONES = API(URI_ZONE, HttpMethod.GET, HTTPStatus.OK)
+
+ CREATE_ROLE = API(URI_ROLE, HttpMethod.POST, HTTPStatus.OK)
+ UPDATE_ROLE_BY_ID = API(URI_ROLE_BY_ID, HttpMethod.PUT, HTTPStatus.OK)
+ DELETE_ROLE_BY_ID = API(URI_ROLE_BY_ID, HttpMethod.DELETE,
HTTPStatus.NO_CONTENT)
+ DELETE_ROLE_BY_NAME = API(URI_ROLE_BY_NAME, HttpMethod.DELETE,
HTTPStatus.NO_CONTENT)
+ GET_ROLE_BY_ID = API(URI_ROLE_BY_ID, HttpMethod.GET, HTTPStatus.OK)
+ GET_ROLE_BY_NAME = API(URI_ROLE_BY_NAME, HttpMethod.GET, HTTPStatus.OK)
+ GET_ALL_ROLE_NAMES = API(URI_ROLE_NAMES, HttpMethod.GET, HTTPStatus.OK)
+ GET_USER_ROLES = API(URI_USER_ROLES, HttpMethod.GET, HTTPStatus.OK)
+ GRANT_ROLE = API(URI_GRANT_ROLE, HttpMethod.PUT, HTTPStatus.OK)
+ REVOKE_ROLE = API(URI_REVOKE_ROLE, HttpMethod.PUT, HTTPStatus.OK)
+ FIND_ROLES = API(URI_ROLE, HttpMethod.GET, HTTPStatus.OK)
+
+ GET_PLUGIN_INFO = API(URI_PLUGIN_INFO, HttpMethod.GET, HTTPStatus.OK)
+ DELETE_POLICY_DELTAS = API(URI_POLICY_DELTAS, HttpMethod.DELETE,
HTTPStatus.NO_CONTENT)
+
+ def __init__(self, url, username, password):
+ self.log = logging.getLogger(__name__)
+ self.__password = password
+ self.url = url
+ self.username = username
+ self.session = Session()
+ self.session.auth = (username, password)
+ self.session.verify = False
+
+ def __call_api(self, api, params, request):
+ return api.call(self.session, self.url, params, request)
+
+ def __call_api_with_url_params(self, api, urlParams, params, request):
+ return api.apply_url_params(urlParams).call(self.session, self.url,
params, request)
+
+ # Service Definition APIs
+ def create_service_def(self, serviceDef):
+ ret = self.__call_api(RangerClient.CREATE_SERVICEDEF, {}, serviceDef)
+
+ return RangerServiceDef(**ret) if ret is not None else None
+
+ def update_service_def_by_id(self, serviceDefId, serviceDef):
+ ret =
self.__call_api_with_url_params(RangerClient.UPDATE_SERVICEDEF_BY_ID, {
RangerClient.PARAM_ID: serviceDefId }, {}, serviceDef)
+
+ return RangerServiceDef(**ret) if ret is not None else None
+
+ def update_service_def(self, serviceDefName, serviceDef):
+ ret =
self.__call_api_with_url_params(RangerClient.UPDATE_SERVICEDEF_BY_NAME, {
RangerClient.PARAM_NAME: serviceDefName }, {}, serviceDef)
+
+ return RangerServiceDef(**ret) if ret is not None else None
+
+ def delete_service_def_by_id(self, serviceDefId):
+ self.__call_api_with_url_params(RangerClient.DELETE_SERVICEDEF_BY_ID,
{ RangerClient.PARAM_ID: serviceDefId }, {}, {})
+
+ return None
+
+ def delete_service_def(self, serviceDefName):
+
self.__call_api_with_url_params(RangerClient.DELETE_SERVICEDEF_BY_NAME, {
RangerClient.PARAM_NAME: serviceDefName }, {}, {})
+
+ return None
+
+ def get_service_def_by_id(self, serviceDefId):
+ ret =
self.__call_api_with_url_params(RangerClient.GET_SERVICEDEF_BY_ID, {
RangerClient.PARAM_ID: serviceDefId }, {}, {})
+
+ return RangerServiceDef(**ret) if ret is not None else None
+
+ def get_service_def(self, serviceDefName):
+ ret =
self.__call_api_with_url_params(RangerClient.GET_SERVICEDEF_BY_NAME, {
RangerClient.PARAM_NAME: serviceDefName }, {}, {})
+
+ return RangerServiceDef(**ret) if ret is not None else None
+
+ def find_service_defs(self, filter):
+ ret = self.__call_api(RangerClient.FIND_SERVICEDEFS, filter, {})
+
+ return list(ret) if ret is not None else None
+
+ # Service APIs
+ def create_service(self, service):
+ ret = self.__call_api(RangerClient.CREATE_SERVICE, {}, service)
+
+ return RangerService(**ret) if ret is not None else None
+
+ def update_service_by_id(self, serviceId, service):
+ ret =
self.__call_api_with_url_params(RangerClient.UPDATE_SERVICE_BY_ID, {
RangerClient.PARAM_ID: serviceId }, {}, service)
+
+ return RangerService(**ret) if ret is not None else None
+
+ def update_service(self, serviceName, service):
+ ret =
self.__call_api_with_url_params(RangerClient.UPDATE_SERVICE_BY_NAME, {
RangerClient.PARAM_NAME: serviceName }, {}, service)
+
+ return RangerService(**ret) if ret is not None else None
+
+ def delete_service_by_id(self, serviceId):
+ self.__call_api_with_url_params(RangerClient.DELETE_SERVICE_BY_ID, {
RangerClient.PARAM_ID: serviceId }, {}, {})
+
+ return None
+
+ def delete_service(self, serviceName):
+ self.__call_api_with_url_params(RangerClient.DELETE_SERVICE_BY_NAME, {
RangerClient.PARAM_NAME: serviceName }, {}, {})
+
+ return None
+
+ def get_service_by_id(self, serviceId):
+ ret = self.__call_api_with_url_params(RangerClient.GET_SERVICE_BY_ID,
{ RangerClient.PARAM_ID: serviceId }, {}, {})
+
+ return RangerService(**ret) if ret is not None else None
+
+ def get_service(self, serviceName):
+ ret =
self.__call_api_with_url_params(RangerClient.GET_SERVICE_BY_NAME, {
RangerClient.PARAM_NAME: serviceName }, {}, {})
+
+ return RangerService(**ret) if ret is not None else None
+
+ def find_services(self, filter):
+ ret = self.__call_api(RangerClient.FIND_SERVICES, filter, {})
+
+ return list(ret) if ret is not None else None
+
+ # Policy APIs
+ def create_policy(self, policy):
+ ret = self.__call_api(RangerClient.CREATE_POLICY, {}, policy)
+
+ return RangerPolicy(**ret) if ret is not None else None
+
+ def update_policy_by_id(self, policyId, policy):
+ ret =
self.__call_api_with_url_params(RangerClient.UPDATE_POLICY_BY_ID, {
RangerClient.PARAM_ID: policyId }, {}, policy)
+
+ return RangerPolicy(**ret) if ret is not None else None
+
+ def update_policy(self, serviceName, policyName, policy):
+ path_params = { RangerClient.PARAM_SERVICE_NAME: serviceName,
RangerClient.PARAM_POLICY_NAME: policyName }
+ ret =
self.__call_api_with_url_params(RangerClient.UPDATE_POLICY_BY_NAME,
path_params, {}, policy)
+
+ return RangerPolicy(**ret) if ret is not None else None
+
+ def apply_policy(self, policy):
+ ret = self.__call_api(RangerClient.APPLY_POLICY, {}, policy)
+
+ return RangerPolicy(**ret) if ret is not None else None
+
+ def delete_policy_by_id(self, policyId):
+ self.__call_api_with_url_params(RangerClient.DELETE_POLICY_BY_ID, {
RangerClient.PARAM_ID: policyId }, {}, {})
+
+ return None
+
+ def delete_policy(self, serviceName, policyName):
+ query_params = { RangerClient.PARAM_POLICY_NAME: policyName,
'servicename': serviceName }
+
+ self.__call_api(RangerClient.DELETE_POLICY_BY_NAME, query_params, {})
+
+ return None
+
+ def get_policy_by_id(self, policyId):
+ ret = self.__call_api_with_url_params(RangerClient.GET_POLICY_BY_ID, {
RangerClient.PARAM_ID: policyId }, {}, {})
+
+ return RangerPolicy(**ret) if ret is not None else None
+
+ def get_policy(self, serviceName, policyName):
+ path_params = {RangerClient.PARAM_SERVICE_NAME: serviceName,
RangerClient.PARAM_POLICY_NAME: policyName}
+ ret =
self.__call_api_with_url_params(RangerClient.GET_POLICY_BY_NAME, path_params,
{}, {})
+
+ return RangerPolicy(**ret) if ret is not None else None
+
+ def get_policies_in_service(self, serviceName):
+ ret =
self.__call_api_with_url_params(RangerClient.GET_POLICIES_IN_SERVICE, {
RangerClient.PARAM_SERVICE_NAME: serviceName }, {}, {})
+
+ return list(ret) if ret is not None else None
+
+ def find_policies(self, filter):
+ ret = self.__call_api(RangerClient.FIND_POLICIES, filter, {})
+
+ return list(ret) if ret is not None else None
+
+ # SecurityZone APIs
+ def create_security_zone(self, securityZone):
+ ret = self.__call_api(RangerClient.CREATE_ZONE, {}, securityZone)
+
+ return RangerSecurityZone(**ret) if ret is not None else None
+
+ def update_security_zone_by_id(self, zoneId, securityZone):
+ ret = self.__call_api_with_url_params(RangerClient.UPDATE_ZONE_BY_ID,
{ RangerClient.PARAM_ID: zoneId }, {}, securityZone)
+
+ return RangerSecurityZone(**ret) if ret is not None else None
+
+ def update_security_zone(self, zoneName, securityZone):
+ ret =
self.__call_api_with_url_params(RangerClient.UPDATE_ZONE_BY_NAME, {
RangerClient.PARAM_NAME: zoneName }, {}, securityZone)
+
+ return RangerSecurityZone(**ret) if ret is not None else None
+
+ def delete_security_zone_by_id(self, zoneId):
+ self.__call_api_with_url_params(RangerClient.DELETE_ZONE_BY_ID, {
RangerClient.PARAM_ID: zoneId }, {}, {})
+ return None
+
+ def delete_security_zone(self, zoneName):
+ self.__call_api_with_url_params(RangerClient.DELETE_ZONE_BY_NAME, {
RangerClient.PARAM_NAME: zoneName }, {}, {})
+
+ return None
+
+ def get_security_zone_by_id(self, zoneId):
+ ret = self.__call_api_with_url_params(RangerClient.GET_ZONE_BY_ID, {
RangerClient.PARAM_ID: zoneId }, {}, {})
+
+ return RangerSecurityZone(**ret) if ret is not None else None
+
+ def get_security_zone(self, zoneName):
+ ret = self.__call_api_with_url_params(RangerClient.GET_ZONE_BY_NAME, {
RangerClient.PARAM_NAME: zoneName }, {}, {})
+
+ return RangerSecurityZone(**ret) if ret is not None else None
+
+ def find_security_zones(self, filter):
+ ret = self.__call_api(RangerClient.FIND_ZONES, filter, {})
+
+ return list(ret) if ret is not None else None
+
+ # Role APIs
+ def create_role(self, serviceName, role):
+ query_params = {RangerClient.PARAM_SERVICE_NAME, serviceName}
+ ret = self.__call_api(RangerClient.CREATE_ROLE, query_params,
role)
+
+ return RangerRole(**ret) if ret is not None else None
+
+ def update_role(self, roleId, role):
+ ret = self.__call_api_with_url_params(RangerClient.UPDATE_ROLE_BY_ID,
{ RangerClient.PARAM_ID: roleId }, {}, role)
+
+ return RangerRole(**ret) if ret is not None else None
+
+ def delete_role_by_id(self, roleId):
+ self.__call_api_with_url_params(RangerClient.DELETE_ROLE_BY_ID, {
RangerClient.PARAM_ID: roleId }, {}, {})
+
+ return None
+
+ def delete_role(self, roleName, execUser, serviceName):
+ query_params = { RangerClient.PARAM_EXEC_USER: execUser,
RangerClient.PARAM_SERVICE_NAME: serviceName }
+
+ self.__call_api_with_url_params(RangerClient.DELETE_ROLE_BY_NAME, {
RangerClient.PARAM_NAME: roleName }, query_params, {})
+
+ return None
+
+ def get_role_by_id(self, roleId):
+ ret = self.__call_api_with_url_params(RangerClient.GET_ROLE_BY_ID, {
RangerClient.PARAM_ID: roleId }, {}, {})
+
+ return RangerRole(**ret) if ret is not None else None
+
+ def get_role(self, roleName, execUser, serviceName):
+ query_params = { RangerClient.PARAM_EXEC_USER: execUser,
RangerClient.PARAM_SERVICE_NAME: serviceName }
+ ret =
self.__call_api_with_url_params(RangerClient.GET_ROLE_BY_NAME, {
RangerClient.PARAM_NAME: roleName }, query_params, {})
+
+ return RangerRole(**ret) if ret is not None else None
+
+ def get_all_role_names(self, execUser, serviceName):
+ query_params = { RangerClient.PARAM_EXEC_USER: execUser,
RangerClient.PARAM_SERVICE_NAME: serviceName }
+ ret =
self.__call_api_with_url_params(RangerClient.GET_ALL_ROLE_NAMES, {
RangerClient.PARAM_NAME: serviceName }, query_params, {})
+
+ return list(ret) if ret is not None else None
+
+ def get_user_roles(self, user):
+ ret = self.__call_api_with_url_params(RangerClient.GET_USER_ROLES, {
RangerClient.PARAM_NAME: user }, {}, {})
+
+ return list(ret) if ret is not None else None
+
+ def find_roles(self, filter):
+ ret = self.__call_api(RangerClient.FIND_ROLES, filter, {})
+
+ return list(ret) if ret is not None else None
+
+ def grant_role(self, serviceName, request):
+ ret = self.__call_api_with_url_params(RangerClient.GRANT_ROLE, {
RangerClient.PARAM_NAME: serviceName }, {}, request)
+
+ return RESTResponse(**ret) if ret is not None else None
+
+ def revoke_role(self, serviceName, request):
+ ret = self.__call_api_with_url_params(RangerClient.REVOKE_ROLE, {
RangerClient.PARAM_NAME: serviceName }, {}, request)
+
+ return RESTResponse(**ret) if ret is not None else None
+
+ # Admin APIs
+ def delete_policy_deltas(self, days, reloadServicePoliciesCache):
+ query_params = {
+ RangerClient.PARAM_DAYS: days,
+ RangerClient.PARAM_RELOAD_SERVICE_POLICIES_CACHE:
reloadServicePoliciesCache
+ }
+
+ self.__call_api(RangerClient.DELETE_POLICY_DELTAS, query_params, {})
+
+ return None
diff --git a/intg/src/main/python/ranger/model/__init__.py
b/intg/src/main/python/ranger/model/__init__.py
new file mode 100644
index 0000000..ed9d0b3
--- /dev/null
+++ b/intg/src/main/python/ranger/model/__init__.py
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+#
+# 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.
diff --git a/intg/src/main/python/ranger/model/grant_revoke_role_request.py
b/intg/src/main/python/ranger/model/grant_revoke_role_request.py
new file mode 100644
index 0000000..fb0eab2
--- /dev/null
+++ b/intg/src/main/python/ranger/model/grant_revoke_role_request.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+#
+# 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.
+
+import json
+
+
+class GrantRevokeRoleRequest:
+ def __init__(self, grantor=None, grantorGroups=None, targetRoles=None,
users=None, groups=None, roles=None, grantOption=None, clientIPAddress=None,
clientType=None, requestData=None, sessionId=None, clusterName=None):
+ self.grantor = grantor
+ self.grantorGroups = grantorGroups if grantorGroups is not None else
[]
+ self.targetRoles = targetRoles if targetRoles is not None else []
+ self.users = users if users is not None else []
+ self.groups = groups if groups is not None else []
+ self.roles = roles if roles is not None else []
+ self.grantOption = grantOption if grantOption is not None else
False
+ self.clientIPAddress = clientIPAddress
+ self.clientType = clientType
+ self.requestData = requestData
+ self.sessionId = sessionId
+ self.clusterName = clusterName
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
diff --git a/intg/src/main/python/ranger/model/ranger_base.py
b/intg/src/main/python/ranger/model/ranger_base.py
new file mode 100644
index 0000000..ae8de74
--- /dev/null
+++ b/intg/src/main/python/ranger/model/ranger_base.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+#
+# 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.
+
+import json
+
+
+class RangerBase:
+ def __init__(self, id=None, guid=None, createdBy=None, updatedBy=None,
createTime=None, updateTime=None, version=None, isEnabled=None):
+ self.id = id
+ self.guid = guid
+ self.isEnabled = isEnabled if isEnabled is not None else True
+ self.createdBy = createdBy
+ self.updatedBy = updatedBy
+ self.createTime = createTime
+ self.updateTime = updateTime
+ self.version = version
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
diff --git a/intg/src/main/python/ranger/model/ranger_policy.py
b/intg/src/main/python/ranger/model/ranger_policy.py
new file mode 100644
index 0000000..9195f4b
--- /dev/null
+++ b/intg/src/main/python/ranger/model/ranger_policy.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+
+#
+# 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.
+
+import json
+
+from ranger.model.ranger_base import RangerBase
+
+
+class RangerPolicyResource:
+ def __init__(self, values=None, isExcludes=None, isRecursive=None):
+ self.values = values if values is not None else []
+ self.isExcludes = isExcludes if isExcludes is not None else False
+ self.isRecursive = isRecursive if isRecursive is not None else False
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class RangerPolicyItemCondition:
+ def __init__(self, type=None, values=None):
+ self.type = type
+ self.values = values if values is not None else []
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class RangerPolicyItem:
+ def __init__(self, accesses=None, users=None, groups=None, roles=None,
conditions=None, delegateAdmin=None):
+ self.accesses = accesses if accesses is not None else []
+ self.users = users if users is not None else []
+ self.groups = groups if groups is not None else []
+ self.roles = roles if roles is not None else []
+ self.conditions = conditions if conditions is not None else []
+ self.delegateAdmin = delegateAdmin if delegateAdmin is not None else
False
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class RangerPolicyItemAccess:
+ def __init__(self, type=None, isAllowed=None):
+ self.type = type
+ self.isAllowed = isAllowed if isAllowed is not None else True
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class RangerPolicyItemDataMaskInfo:
+ def __init__(self, dataMaskType=None, conditionExpr=None, valueExpr=None):
+ self.dataMaskType = dataMaskType
+ self.conditionExpr = conditionExpr
+ self.valueExpr = valueExpr
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class RangerDataMaskPolicyItem(RangerPolicyItem):
+ def __init__(self, dataMaskInfo=None, accesses=None, users=None,
groups=None, roles=None, conditions=None, delegateAdmin=None):
+ super().__init__(accesses, users, groups, roles, conditions,
delegateAdmin)
+
+ self.dataMaskInfo = dataMaskInfo if dataMaskInfo is not None else
RangerPolicyItemDataMaskInfo()
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class RangerRowFilterPolicyItem(RangerPolicyItem):
+ def __init__(self, rowFilterInfo=None, accesses=None, users=None,
groups=None, roles=None, conditions=None, delegateAdmin=None):
+ super().__init__(accesses, users, groups, roles, conditions,
delegateAdmin)
+
+ self.rowFilterInfo = rowFilterInfo
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class RangerPolicy(RangerBase):
+ def __init__(self, id=None, guid=None, createdBy=None, updatedBy=None,
createTime=None, updateTime=None,
+ service=None, name=None, description=None, isEnabled=True,
isAuditEnabled=None, resources=None,
+ policyItems=None, dataMaskPolicyItems=None,
rowFilterPolicyItems=None, serviceType=None, options=None,
+ policyLabels=None, zoneName=None, isDenyAllElse=None,
validitySchedules=None, version=None,
+ denyPolicyItems=None, denyExceptions=None,
allowExceptions=None, resourceSignature=None,
+ policyType=None, policyPriority=None, conditions=None):
+ super().__init__(id, guid, createdBy, updatedBy, createTime,
updateTime, version, isEnabled)
+
+ self.service = service
+ self.name = name
+ self.policyType = policyType
+ self.policyPriority = policyPriority if policyPriority is not
None else 0
+ self.description = description
+ self.resourceSignature = resourceSignature
+ self.isAuditEnabled = isAuditEnabled if isAuditEnabled is not
None else True
+ self.resources = resources if resources is not None else {}
+ self.policyItems = policyItems if policyItems is not None
else []
+ self.denyPolicyItems = denyPolicyItems if denyPolicyItems is not
None else []
+ self.allowExceptions = allowExceptions if allowExceptions is not
None else []
+ self.denyExceptions = denyExceptions if denyExceptions is not
None else []
+ self.dataMaskPolicyItems = dataMaskPolicyItems if dataMaskPolicyItems
is not None else []
+ self.rowFilterPolicyItems = rowFilterPolicyItems if
rowFilterPolicyItems is not None else []
+ self.serviceType = serviceType
+ self.options = options if options is not None else {}
+ self.validitySchedules = validitySchedules if validitySchedules is
not None else []
+ self.policyLabels = policyLabels if policyLabels is not None
else []
+ self.zoneName = zoneName
+ self.conditions = conditions
+ self.isDenyAllElse = isDenyAllElse if isDenyAllElse is not None
else False
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
diff --git a/intg/src/main/python/ranger/model/ranger_role.py
b/intg/src/main/python/ranger/model/ranger_role.py
new file mode 100644
index 0000000..aa05216
--- /dev/null
+++ b/intg/src/main/python/ranger/model/ranger_role.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+
+#
+# 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.
+
+import json
+
+from ranger.model.ranger_base import RangerBase
+
+
+class RoleMember:
+ def __init__(self, name=None, isAdmin=None):
+ self.name = name
+ self.isAdmin = isAdmin if isAdmin is not None else False
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class RangerRole(RangerBase):
+ def __init__(self, id=None, guid=None, createdBy=None, updatedBy=None,
createTime=None, updateTime=None,
+ version=None, isEnabled=None, name=None, description=None,
options=None, users=None, groups=None,
+ roles=None, createdByUser=None):
+ super().__init__(id, guid, createdBy, updatedBy, createTime,
updateTime, version, isEnabled)
+
+ self.name = name
+ self.description = description
+ self.options = options if options is not None else {}
+ self.users = users if users is not None else []
+ self.groups = groups if groups is not None else []
+ self.roles = roles if roles is not None else []
+ self.createdByUser = createdByUser
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
diff --git a/intg/src/main/python/ranger/model/ranger_security_zone.py
b/intg/src/main/python/ranger/model/ranger_security_zone.py
new file mode 100644
index 0000000..40a5209
--- /dev/null
+++ b/intg/src/main/python/ranger/model/ranger_security_zone.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+
+#
+# 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.
+
+import json
+
+from ranger.model.ranger_base import RangerBase
+
+
+class RangerSecurityZoneService:
+ def __init__(self, resources=None):
+ self.resources = resources if resources is not None else []
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class RangerSecurityZone(RangerBase):
+ def __init__(self, id=None, guid=None, createdBy=None, updatedBy=None,
createTime=None, updateTime=None,
+ version=None, isEnabled=None, name=None, services=None,
tagServices=None, adminUsers=None,
+ adminUserGroups=None, auditUsers=None, auditUserGroups=None,
description=None):
+ super().__init__(id, guid, createdBy, updatedBy, createTime,
updateTime, version, isEnabled)
+ self.name = name
+ self.services = services if services is not None else {}
+ self.tagServices = tagServices if tagServices is not None else []
+ self.adminUsers = adminUsers if adminUsers is not None else []
+ self.adminUserGroups = adminUserGroups if adminUserGroups is not None
else []
+ self.auditUsers = auditUsers if auditUsers is not None else []
+ self.auditUserGroups = auditUserGroups if auditUserGroups is not None
else []
+ self.description = description
+ return
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
diff --git a/intg/src/main/python/ranger/model/ranger_service.py
b/intg/src/main/python/ranger/model/ranger_service.py
new file mode 100644
index 0000000..637c38f
--- /dev/null
+++ b/intg/src/main/python/ranger/model/ranger_service.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+#
+# 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.
+
+import json
+
+from ranger.model.ranger_base import RangerBase
+
+
+class RangerService(RangerBase):
+ def __init__(self, id=None, guid=None, createdBy=None, updatedBy=None,
createTime=None, updateTime=None,
+ version=None, isEnabled=None, type=None, name=None,
displayName=None, description=None,
+ tagService=None, configs=None, policyVersion=None,
policyUpdateTime=None, tagVersion=None,
+ tagUpdateTime=None):
+ super().__init__(id, guid, createdBy, updatedBy, createTime,
updateTime, version, isEnabled)
+
+ self.type = type
+ self.name = name
+ self.displayName = displayName
+ self.description = description
+ self.tagService = tagService
+ self.configs = configs if configs is not None else {}
+ self.policyVersion = policyVersion
+ self.policyUpdateTime = policyUpdateTime
+ self.tagVersion = tagVersion
+ self.tagUpdateTime = tagUpdateTime
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
diff --git a/intg/src/main/python/ranger/model/ranger_service_def.py
b/intg/src/main/python/ranger/model/ranger_service_def.py
new file mode 100644
index 0000000..8cd4757
--- /dev/null
+++ b/intg/src/main/python/ranger/model/ranger_service_def.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+
+#
+# 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.
+
+import json
+
+from ranger.model.ranger_base import RangerBase
+
+
+class RangerDataMaskDef:
+ def __init__(self, maskTypes=None, accessTypes=None, resources=None):
+ self.maskTypes = maskTypes if maskTypes is not None else []
+ self.accessTypes = accessTypes if accessTypes is not None else []
+ self.resources = resources if resources is not None else []
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class RangerRowFilterDef:
+ def __init__(self, accessTypes=None, resources=None):
+ self.accessTypes = accessTypes if accessTypes is not None else []
+ self.resources = resources if resources is not None else []
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class RangerServiceConfigDef:
+ def __init__(self, itemId=None, name=None, type=None, subType=None,
mandatory=None, defaultValue=None,
+ validationRegEx=None, validationMessage=None, uiHint=None,
label=None, description=None,
+ rbKeyLabel=None, rbKeyDescription=None,
rbKeyValidationMessage=None):
+ self.itemId = itemId
+ self.name = name
+ self.type = type
+ self.subType = subType
+ self.mandatory = mandatory if mandatory is not None else
False
+ self.defaultValue = defaultValue
+ self.validationRegEx = validationRegEx
+ self.validationMessage = validationMessage
+ self.uiHint = uiHint
+ self.label = label
+ self.description = description
+ self.rbKeyLabel = rbKeyLabel
+ self.rbKeyDescription = rbKeyDescription
+ self.rbKeyValidationMessage = rbKeyValidationMessage
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class RangerAccessTypeDef:
+ def __init__(self, itemId=None, name=None, label=None, rbKeyLabel=None,
impliedGrants=None):
+ self.itemId = itemId
+ self.name = name
+ self.label = label
+ self.rbKeyLabel = rbKeyLabel
+ self.impliedGrants = impliedGrants if impliedGrants is not None else []
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class RangerResourceDef:
+ def __init__(self, itemId=None, name=None, type=None, level=None,
parent=None, mandatory=None, lookupSupported=None,
+ recursiveSupported=None, excludesSupported=None,
matcher=None, matcherOptions=None,
+ validationRegEx=None, validationMessage=None, uiHint=None,
label=None, description=None,
+ rbKeyLabel=None, rbKeyDescription=None,
rbKeyValidationMessage=None, accessTypeRestrictions=None,
+ isValidLeaf=None):
+ self.itemId = itemId
+ self.name = name
+ self.type = type
+ self.level = level if level is not None else 1
+ self.parent = parent
+ self.mandatory = mandatory if mandatory is not None else
False
+ self.lookupSupported = lookupSupported if lookupSupported is
not None else False
+ self.recursiveSupported = recursiveSupported if recursiveSupported
is not None else False
+ self.excludesSupported = excludesSupported if excludesSupported
is not None else False
+ self.matcher = matcher
+ self.matcherOptions = matcherOptions if matcherOptions is not
None else {}
+ self.validationRegEx = validationRegEx
+ self.validationMessage = validationMessage
+ self.uiHint = uiHint
+ self.label = label
+ self.description = description
+ self.rbKeyLabel = rbKeyLabel
+ self.rbKeyDescription = rbKeyDescription
+ self.rbKeyValidationMessage = rbKeyValidationMessage
+ self.accessTypeRestrictions = accessTypeRestrictions if
accessTypeRestrictions is not None else []
+ self.isValidLeaf = isValidLeaf
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
+
+
+class RangerServiceDef(RangerBase):
+ def __init__(self, id=None, guid=None, createdBy=None, updatedBy=None,
createTime=None, updateTime=None,
+ version=None, isEnabled=None, name=None, displayName=None,
implClass=None, label=None,
+ description=None, rbKeyLabel=None, rbKeyDescription=None,
options=None, configs=None, resources=None,
+ accessTypes=None, policyConditions=None,
contextEnrichers=None, enums=None, dataMaskDef=None,
+ rowFilterDef=None):
+ super().__init__(id, guid, createdBy, updatedBy, createTime,
updateTime, version, isEnabled)
+
+ self.name = name
+ self.displayName = displayName
+ self.implClass = implClass
+ self.label = label
+ self.description = description
+ self.rbKeyLabel = rbKeyLabel
+ self.rbKeyDescription = rbKeyDescription
+ self.options = options if options is not None else {}
+ self.configs = configs if configs is not None else []
+ self.resources = resources if resources is not None else []
+ self.accessTypes = accessTypes if accessTypes is not None else []
+ self.policyConditions = policyConditions if policyConditions is not
None else []
+ self.contextEnrichers = contextEnrichers if contextEnrichers is not
None else []
+ self.enums = enums if enums is not None else []
+ self.dataMaskDef = dataMaskDef if dataMaskDef is not None else
RangerDataMaskDef()
+ self.rowFilterDef = rowFilterDef if rowFilterDef is not None else
RangerRowFilterDef()
+
+ def __repr__(self):
+ return json.dumps(self, default=lambda x: x.__dict__, sort_keys=True,
indent=4)
diff --git a/intg/src/main/resources/logging.conf
b/intg/src/main/resources/logging.conf
new file mode 100644
index 0000000..c168775
--- /dev/null
+++ b/intg/src/main/resources/logging.conf
@@ -0,0 +1,40 @@
+#
+# 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.
+#
+
+[loggers]
+keys=root
+
+[handlers]
+keys=consoleHandler
+
+[formatters]
+keys=simpleFormatter
+
+[logger_root]
+level=INFO
+handlers=consoleHandler
+
+[handler_consoleHandler]
+class=StreamHandler
+level=INFO
+formatter=simpleFormatter
+args=(sys.stdout,)
+
+[formatter_simpleFormatter]
+format=%(asctime)s.%(msecs)03d %(levelname)s:%(name)s:%(message)s
+datefmt=%Y-%m-%d %H:%M:%S
diff --git a/intg/src/test/python/test_ranger_client.py
b/intg/src/test/python/test_ranger_client.py
new file mode 100644
index 0000000..5efda23
--- /dev/null
+++ b/intg/src/test/python/test_ranger_client.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+
+#
+# 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.
+
+import unittest
+from unittest.mock import patch
+from ranger.model.ranger_service import RangerService
+from ranger.client.ranger_client import API, HttpMethod, HTTPStatus,
RangerClient
+
+
+class MockResponse:
+ def __init__(self, status_code, response=None, content=None):
+ self.status_code = status_code
+ self.response = response
+ self.content = content
+ return
+
+ def json(self):
+ return [self.response.__repr__()]
+
+ def text(self):
+ return str(self.content)
+
+
+class TestRangerClient(unittest.TestCase):
+ URL = "url"
+ USERNAME = "user"
+ PASSWORD = "password"
+
+ @patch('ranger.client.ranger_client.Session')
+ def test_get_service_unavailable(self, mock_session):
+ mock_session.return_value.get.return_value =
MockResponse(HTTPStatus.SERVICE_UNAVAILABLE)
+ result =
RangerClient(TestRangerClient.URL, TestRangerClient.USERNAME,
TestRangerClient.PASSWORD).find_services({})
+
+ self.assertTrue(result is None)
+
+
+ @patch('ranger.client.ranger_client.Session')
+ def test_get_success(self, mock_session):
+ response = RangerService()
+ mock_session.return_value.get.return_value =
MockResponse(HTTPStatus.OK, response=response, content='Success')
+ result =
RangerClient(TestRangerClient.URL, TestRangerClient.USERNAME,
TestRangerClient.PASSWORD).find_services({})
+
+ self.assertTrue(response.__repr__() in result)
+
+
+ @patch('ranger.client.ranger_client.Session')
+ @patch('ranger.client.ranger_client.Response')
+ def test_get_unexpected_status_code(self, mock_response, mock_session):
+ content = 'Internal Server Error'
+ mock_response.text = content
+ mock_response.content = content
+ mock_response.status_code =
HTTPStatus.INTERNAL_SERVER_ERROR
+ mock_session.return_value.get.return_value = mock_response
+
+ try:
+ RangerClient(TestRangerClient.URL, TestRangerClient.USERNAME,
TestRangerClient.PASSWORD).find_services({})
+ except Exception as e:
+ self.assertTrue(content in repr(e))
+
+
+ @patch('ranger.client.ranger_client.RangerClient.FIND_SERVICES')
+ def test_unexpected_http_method(self, mock_api):
+ mock_api.method.return_value = "PATCH"
+
+ try:
+ RangerClient(TestRangerClient.URL, TestRangerClient.USERNAME,
TestRangerClient.PASSWORD).find_services({})
+ except Exception as e:
+ self.assertTrue('Unsupported HTTP Method' in repr(e))
+
+
+ def test_url_missing_format(self):
+ params = {'arg1': 1, 'arg2': 2}
+
+ try:
+ API("{arg1}test{arg2}path{arg3}", HttpMethod.GET,
HTTPStatus.OK).apply_url_params(params)
+
+ self.fail("Supposed to fail")
+ except KeyError as e:
+ self.assertTrue('KeyError' in repr(e))
+
+
+ def test_url_invalid_format(self):
+ params = {'1', '2'}
+
+ try:
+ API("{}test{}path{}", HttpMethod.GET,
HTTPStatus.OK).apply_url_params(params)
+
+ self.fail("Supposed to fail")
+ except TypeError as e:
+ self.assertTrue('TypeError' in repr(e))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/pom.xml b/pom.xml
index 4e7c922..b8d6141 100644
--- a/pom.xml
+++ b/pom.xml
@@ -158,6 +158,8 @@
<libpam4j.version>1.10</libpam4j.version>
<local.lib.dir>${project.basedir}/../lib/local</local.lib.dir>
<log4j.version>1.2.17</log4j.version>
+ <maven.exec.plugin.version>1.6.0</maven.exec.plugin.version>
+ <maven.pmd.plugin.version>3.7</maven.pmd.plugin.version>
<metrics.core.version>3.0.2</metrics.core.version>
<mockito.version>3.0.0</mockito.version>
<mockito.all.version>1.10.19</mockito.all.version>
@@ -948,7 +950,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
- <version>3.7</version>
+ <version>${maven.pmd.plugin.version}</version>
<executions>
<execution>
<phase>verify</phase>
@@ -1030,6 +1032,7 @@
<exclude>**/package.json</exclude>
<exclude>**/package-lock.json</exclude>
<exclude>**/ranger_es_schema.json</exclude>
+ <exclude>**/venv/**</exclude>
</excludes>
</configuration>
</plugin>
diff --git a/ranger-examples/distro/src/main/assembly/sample-client.xml
b/ranger-examples/distro/src/main/assembly/sample-client.xml
index cde39f1..e3fa1bb 100644
--- a/ranger-examples/distro/src/main/assembly/sample-client.xml
+++ b/ranger-examples/distro/src/main/assembly/sample-client.xml
@@ -78,10 +78,10 @@
<directory>${project.parent.basedir}/sample-client/scripts</directory>
<includes>
<include>*.sh</include>
+ <include>*.py</include>
</includes>
<fileMode>755</fileMode>
</fileSet>
-<!-- only for testing -->
<fileSet>
<outputDirectory></outputDirectory>
<directory>${project.parent.basedir}/sample-client/conf</directory>
@@ -99,5 +99,24 @@
<include>*.properties</include>
</includes>
</fileSet>
+ <fileSet>
+ <outputDirectory>venv/pylib</outputDirectory>
+ <directory>../../intg/src/main/python</directory>
+ </fileSet>
+ <fileSet>
+ <outputDirectory>venv/resources</outputDirectory>
+ <directory>../../intg/src/main/resources</directory>
+ </fileSet>
+ <fileSet>
+ <outputDirectory>venv/pylib</outputDirectory>
+ <directory>../sample-client/src/main/python</directory>
+ <includes>
+ <include>sample_client.py</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <outputDirectory>venv</outputDirectory>
+ <directory>../sample-client/venv</directory>
+ </fileSet>
</fileSets>
</assembly>
diff --git a/ranger-examples/sample-client/.gitignore
b/ranger-examples/sample-client/.gitignore
index a6f89c2..897593c 100644
--- a/ranger-examples/sample-client/.gitignore
+++ b/ranger-examples/sample-client/.gitignore
@@ -1 +1,2 @@
-/target/
\ No newline at end of file
+/target/
+/venv/
\ No newline at end of file
diff --git a/ranger-examples/sample-client/conf/config.properties
b/ranger-examples/sample-client/conf/config.properties
new file mode 100644
index 0000000..5be772a
--- /dev/null
+++ b/ranger-examples/sample-client/conf/config.properties
@@ -0,0 +1,21 @@
+# 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.
+#
+ranger.client.url=https://localhost:6182
+# Authentication properties
+ranger.client.authentication.type=kerberos
+ranger.client.kerberos.principal=
+ranger.client.kerberos.keytab=
+ranger.client.ssl.config.filename=
\ No newline at end of file
diff --git a/ranger-examples/sample-client/pom.xml
b/ranger-examples/sample-client/pom.xml
index b102e38..d112c6f 100644
--- a/ranger-examples/sample-client/pom.xml
+++ b/ranger-examples/sample-client/pom.xml
@@ -28,6 +28,44 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>sample-client</artifactId>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-clean-plugin</artifactId>
+ <version>2.6.1</version>
+ <configuration>
+ <filesets>
+ <fileset>
+ <directory>venv</directory>
+ </fileset>
+ </filesets>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>${maven.exec.plugin.version}</version>
+ <executions>
+ <execution>
+ <configuration>
+ <executable>python3</executable>
+ <arguments>
+ <argument>-m</argument>
+ <argument>venv</argument>
+ <argument>venv</argument>
+ </arguments>
+ </configuration>
+ <id>create-venv</id>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
<dependencies>
<dependency>
<groupId>org.apache.ranger</groupId>
diff --git a/ranger-examples/sample-client/scripts/run-pyclient.sh
b/ranger-examples/sample-client/scripts/run-pyclient.sh
new file mode 100644
index 0000000..8649f0b
--- /dev/null
+++ b/ranger-examples/sample-client/scripts/run-pyclient.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# 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.
+
+usage() {
+ echo "usage: run-pyclient.sh
+ -n <arg> url to connect to
+ -h show help."
+ exit 1
+}
+
+cd venv || exit
+source bin/activate
+INSTALL="python3 ./../setup.py install"
+$INSTALL >> build.log
+PYTHON_CMD="python3 build/lib/pylib/sample_client.py"
+while getopts "n:h" opt; do
+ case $opt in
+ n) URL=$OPTARG
+ PYTHON_CMD="$PYTHON_CMD --url $URL"
+ ;;
+ h) usage
+ ;;
+ \?) echo -e \\n"Option -$OPTARG not allowed."
+ usage
+ ;;
+ esac
+done
+
+prompt="Sample Authentication User Name:"
+read -p "$prompt" userName
+prompt="Sample Authentication User Password:"
+read -p "$prompt" -s password
+printf "\n"
+PYTHON_CMD="$PYTHON_CMD --username $userName --password $password"
+printf "Python command : %s\n" "$PYTHON_CMD"
+$PYTHON_CMD
\ No newline at end of file
diff --git a/ranger-examples/sample-client/scripts/setup.py
b/ranger-examples/sample-client/scripts/setup.py
new file mode 100644
index 0000000..c4e7407
--- /dev/null
+++ b/ranger-examples/sample-client/scripts/setup.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+
+#
+# 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.
+
+from setuptools import setup, find_packages
+
+# External dependencies
+requirements = ['requests>=2.24']
+
+setup(
+ name='sample_pyclient',
+ packages=['pylib', 'pylib/ranger/client', 'pylib/ranger/model'],
+ data_files=[('../../lib/resources', ['resources/logging.conf'])],
+ install_requires=requirements,
+ zip_safe=False
+)
diff --git a/ranger-examples/sample-client/src/main/python/sample_client.py
b/ranger-examples/sample-client/src/main/python/sample_client.py
new file mode 100644
index 0000000..7ff6e70
--- /dev/null
+++ b/ranger-examples/sample-client/src/main/python/sample_client.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+
+#
+# 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.
+
+import argparse
+import logging.config
+
+from ranger.model.ranger_service import RangerService
+from ranger.model.ranger_role import RangerRole, RoleMember
+from ranger.client.ranger_client import RangerClient, LOG_CONFIG
+from ranger.model.ranger_policy import RangerPolicy, RangerPolicyResource
+from ranger.model.ranger_service_def import RangerServiceConfigDef,
RangerAccessTypeDef, RangerResourceDef, RangerServiceDef
+
+logging.config.fileConfig(LOG_CONFIG, disable_existing_loggers=False)
+log = logging.getLogger(__name__)
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description='Parameters required for
connection to Ranger Admin!')
+ parser.add_argument('--url', default='http://localhost:6080')
+ parser.add_argument('--username')
+ parser.add_argument('--password')
+ args = parser.parse_args()
+
+ service_def_name = "sampleServiceDef"
+ service_name = "sampleService"
+ policy_name = "samplePolicy"
+ role_name = "sampleRole"
+
+ ranger_client = RangerClient(args.url, args.username, args.password)
+
+ # create a new service definition
+ config = RangerServiceConfigDef(itemId=1, name='sampleConfig',
type='string')
+ access_type = RangerAccessTypeDef(itemId=1, name='sampleAccess')
+ resource_def = RangerResourceDef(itemId=1, name='root', type='string')
+ service_def = RangerServiceDef(name=service_def_name, configs=[config],
accessTypes=[access_type],
+ resources=[resource_def])
+
+ created_service_def = ranger_client.create_service_def(service_def)
+ log.info('New Service Definition created successfully
{}'.format(created_service_def))
+
+ # create a new service
+ service = RangerService(name=service_name, type=service_def_name)
+
+ created_service = ranger_client.create_service(service)
+ log.info('New Service created successfully {}'.format(created_service))
+
+ # create a new policy
+ resource = RangerPolicyResource(['/path/to/sample/resource'], False, False)
+ policy = RangerPolicy(service=service_name, name=policy_name,
resources={'root': resource})
+ created_policy = ranger_client.create_policy(policy)
+ log.info('New Ranger Policy created successfully
{}'.format(created_policy))
+
+ # update an existing policy
+ policy = RangerPolicy(service=service_name, name=policy_name,
description='Policy Updated!')
+ updated_policy = ranger_client.update_policy(service_name, policy_name,
policy)
+ log.info('Ranger Policy updated successfully {}'.format(updated_policy))
+
+ # get a policy by name
+ fetched_policy = ranger_client.get_policy(service_name, policy_name)
+ log.info('Policy {} fetched {}'.format(policy_name, fetched_policy))
+
+ # get all policies
+ all_policies = ranger_client.find_policies({})
+ for id, policy in enumerate(all_policies):
+ log.info('Policy #{}: {}'.format(id, RangerPolicy(**policy)))
+
+ # delete a policy by name
+ ranger_client.delete_policy(service_name, policy_name)
+ log.info('Policy {} successfully deleted'.format(policy_name))
+
+ # create a role in ranger
+ sample_role = RangerRole(name=role_name, description='Sample Role',
users=[RoleMember(isAdmin=True)])
+ created_role = ranger_client.create_role(service_name, sample_role)
+ log.info('New Role successfully created {}'.format(created_role))
+
+ # update a role in ranger
+ sample_role = RangerRole(name=role_name, description='Updated Sample
Role!')
+ updated_role = ranger_client.update_role(created_role.id, sample_role)
+ log.info('Role {} successfully updated {}'.format(role_name, updated_role))
+
+ # get all roles in ranger
+ all_roles = ranger_client.find_roles({})
+ log.info('List of Roles {}'.format(all_roles)) if all_roles is not None
else log.info('No roles found!')
+
+ # delete a role in ranger
+ ranger_client.delete_role(role_name, ranger_client.username, service_name)
+ log.info('Role {} successfully deleted'.format(role_name))
+
+ # delete a service
+ ranger_client.delete_service(service_name)
+ log.info('Service {} successfully deleted'.format(service_name))
+
+ # delete a service definition
+ ranger_client.delete_service_def(service_def_name)
+ log.info('Service Definition {} successfully
deleted'.format(service_def_name))
+