Browse Source

[raz] MVP clients examples for ADLS

Adding as-is as for now
Romain Rigaux 4 năm trước cách đây
mục cha
commit
8f814d6c67

+ 59 - 0
desktop/core/src/desktop/lib/raz/clients/ranger_raz_adls.py

@@ -0,0 +1,59 @@
+#!/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 logging
+
+from desktop.lib.raz.clients.model.ranger_raz         import RangerRazRequest, RangerRazResult, ResourceAccess
+from desktop.lib.raz.clients.client.ranger_raz_client import RangerRazClient
+from apache_ranger.utils                    import *
+
+LOG = logging.getLogger(__name__)
+
+class RangerRazAdls:
+    def __init__(self, url, auth):
+        self.razClient = RangerRazClient(url, auth)
+
+    def get_raz_client(self):
+        return self.razClient
+
+    def get_dsas_token(self, storage_account, container, relative_path, action):
+        req = RangerRazRequest()
+
+        req.serviceType = 'adls'
+        req.operation   = ResourceAccess({'resource': {'storageaccount': storage_account, 'container': container, 'relativepath': relative_path}, 'action': action})
+
+        res = self.razClient.check_privilege(req)
+
+        return res.operResult.additionalInfo["ADLS_DSAS"]
+
+'''
+#
+# Sample usage
+#
+from apache_ranger.client.ranger_raz_adls import RangerRazAdls
+from requests_kerberos                    import HTTPKerberosAuth
+
+razAdls = RangerRazAdls('https://<raz_server_host>:<raz_port>', HTTPKerberosAuth())
+
+# disable HTTPS certificate validation; not recommended for production use
+razAdls.razClient.session.verify = False
+
+dsas = razAdls.get_dsas_token('<storage_account>', '<container>>', '<relative_path>', 'read')
+
+print(dsas)
+'''

+ 199 - 0
desktop/core/src/desktop/lib/raz/clients/ranger_raz_client.py

@@ -0,0 +1,199 @@
+#!/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
+import logging
+import os
+
+from requests                       import Session, Response
+from desktop.lib.raz.clients.model.ranger_raz import RangerRazResult
+from apache_ranger.utils            import *
+
+
+LOG = logging.getLogger(__name__)
+
+class RangerRazClient:
+    def __init__(self, url, auth):
+        self.url          = url
+        self.session      = Session()
+        self.session.auth = auth
+
+        logging.getLogger("requests").setLevel(logging.WARNING)
+
+    def get_delegation_token(self, renewer, dtServiceName=None, doAsUser=None):
+        resp = None
+
+        if self.__is_kerberos_authenticated():
+            resp = self.__call_api(RangerRazClient.GET_DELEGATION_TOKEN, self.__get_query_params({ RangerRazClient.PARAM_OP: RangerRazClient.GET_DELEGATION_TOKEN,
+                                                                                                   RangerRazClient.PARAM_RENEWER: renewer,
+                                                                                                   RangerRazClient.PARAM_DT_SERVICENAME: dtServiceName }, doAsUser))
+        else:
+            LOG.error("Kerberos Authentication is required to get RAZ delegation token")
+
+        return resp
+
+    def renew_delegation_token(self, delegation_token, doAsUser=None):
+        resp = None
+
+        if self.__is_kerberos_authenticated():
+            resp = self.__call_api(RangerRazClient.RENEW_DELEGATION_TOKEN, self.__get_query_params({ RangerRazClient.PARAM_OP: RangerRazClient.RENEW_DELEGATION_TOKEN,
+                                                                                                     RangerRazClient.PARAM_TOKEN: delegation_token }, doAsUser))
+        else:
+            LOG.error("Kerberos Authentication is required to renew RAZ delegation token")
+
+        return resp
+
+    def cancel_delegation_token(self, delegation_token, doAsUser=None):
+        resp = None
+
+        if self.__is_kerberos_authenticated():
+            resp = self.__call_api(RangerRazClient.CANCEL_DELEGATION_TOKEN, self.__get_query_params({ RangerRazClient.PARAM_OP: RangerRazClient.CANCEL_DELEGATION_TOKEN,
+                                                                                                      RangerRazClient.PARAM_TOKEN: delegation_token }, doAsUser))
+        else:
+            LOG.error("Kerberos Authentication is required to cancel RAZ delegation token")
+
+        return resp
+
+    def check_privilege(self, raz_request, doAsUser=None):
+        resp = self.__call_api(RangerRazClient.CHECK_PRIVILEGE.format_path({ 'serviceType': raz_request.serviceType }), query_params=self.__get_query_params(None, doAsUser), request_data=raz_request)
+
+        return type_coerce(resp, RangerRazResult)
+
+    def check_privileges(self, raz_requests, doAsUser=None):
+        resp = self.__call_api(RangerRazClient.CHECK_PRIVILEGES.format_path({ 'serviceType': raz_request.serviceType }), query_params=self.__get_query_params(None, doAsUser), request_data=raz_requests)
+
+        return type_coerce_list(resp, RangerRazResult)
+
+    def __is_kerberos_authenticated(self):
+        from requests_kerberos import HTTPKerberosAuth
+
+        return isinstance(self.session.auth, HTTPKerberosAuth)
+
+    def __get_query_params(self, query_params, doAsUser=None):
+        if doAsUser is not None:
+            query_params = query_params or {}
+
+            query_params[RangerRazClient.PARAM_DOAS] = doAsUser
+
+        return query_params
+
+    def __call_api(self, api, query_params=None, request_data=None):
+        ret    = None
+        params = { 'headers': { 'Accept': api.consumes, 'Content-type': api.produces } }
+
+        if query_params:
+            params['params'] = query_params
+
+        if request_data:
+            params['data'] = json.dumps(request_data)
+
+        path = os.path.join(self.url, api.path)
+
+        if LOG.isEnabledFor(logging.DEBUG):
+            LOG.debug("------------------------------------------------------")
+            LOG.debug("Call         : %s %s", api.method, path)
+            LOG.debug("Content-type : %s", api.consumes)
+            LOG.debug("Accept       : %s", api.produces)
+
+        response = None
+
+        if api.method == HttpMethod.GET:
+            response = self.session.get(path, **params)
+        elif api.method == HttpMethod.POST:
+            response = self.session.post(path, **params)
+        elif api.method == HttpMethod.PUT:
+            response = self.session.put(path, **params)
+        elif api.method == HttpMethod.DELETE:
+            response = self.session.delete(path, **params)
+
+        if LOG.isEnabledFor(logging.DEBUG):
+            LOG.debug("HTTP Status: %s", response.status_code if response else "None")
+
+        if response is None:
+            ret = None
+        elif response.status_code == api.expected_status:
+            try:
+                if response.content is not None:
+                    if LOG.isEnabledFor(logging.DEBUG):
+                        LOG.debug("<== __call_api(%s, %s, %s), result=%s", vars(api), params, request_data, response)
+
+                        LOG.debug(response.json())
+
+                    ret = response.json()
+                else:
+                    ret = None
+            except Exception as e:
+                print(e)
+
+                LOG.exception("Exception occurred while parsing response with msg: %s", e)
+
+                raise RangerRazException(api, response)
+        elif response.status_code == HTTPStatus.SERVICE_UNAVAILABLE:
+            LOG.error("Ranger Raz server unavailable. HTTP Status: %s", HTTPStatus.SERVICE_UNAVAILABLE)
+
+            ret = None
+        else:
+            raise RangerRazException(api, response)
+
+        return ret
+
+    # URIs
+    PARAM_OP                 = "op"
+    PARAM_RENEWER            = "renewer"
+    PARAM_TOKEN              = "token"
+    PARAM_DELEGATION         = "delegation"
+    PARAM_DOAS               = "doAs"
+    PARAM_DT_SERVICENAME     = "service"
+    OP_GETDELEGATIONTOKEN    = "GETDELEGATIONTOKEN"
+    OP_RENEWDELEGATIONTOKEN  = "RENEWDELEGATIONTOKEN"
+    OP_CANCELDELEGATIONTOKEN = "CANCELDELEGATIONTOKEN"
+    URI_DELEGATION_TOKEN     = ""
+    URI_CHECK_PRIVILEGE      = "api/authz/{serviceType}/access"
+    URI_CHECK_PRIVILEGES     = "api/authz/{serviceType}/accesses"
+
+    # APIs
+    GET_DELEGATION_TOKEN    = API(URI_DELEGATION_TOKEN, HttpMethod.GET, HTTPStatus.OK)
+    RENEW_DELEGATION_TOKEN  = API(URI_DELEGATION_TOKEN, HttpMethod.PUT, HTTPStatus.OK)
+    CANCEL_DELEGATION_TOKEN = API(URI_DELEGATION_TOKEN, HttpMethod.PUT, HTTPStatus.OK)
+    CHECK_PRIVILEGE         = API(URI_CHECK_PRIVILEGE, HttpMethod.POST, HTTPStatus.OK)
+    CHECK_PRIVILEGES        = API(URI_CHECK_PRIVILEGES, HttpMethod.POST, HTTPStatus.OK)
+
+class RangerRazException(Exception):
+    """Exception raised for errors in API calls.
+
+    Attributes:
+        api      -- api endpoint which caused the error
+        response -- response from the server
+    """
+
+    def __init__(self, api, response):
+        self.method          = api.method.name
+        self.path            = api.path
+        self.expected_status = api.expected_status
+        self.statusCode      = -1
+        self.msgDesc         = None
+        self.messageList     = None
+
+        print(response)
+
+        if api is not None and response is not None:
+            self.statusCode  = response.status_code
+            self.message     = response.content
+
+        Exception.__init__(self, "{} {} failed: expected_status={}, status={}, message={}".format(self.method, self.path, self.expected_status, self.statusCode, self.message))

+ 154 - 0
desktop/core/src/desktop/lib/raz/model/ranger_raz.py

@@ -0,0 +1,154 @@
+#!/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 desktop.lib.raz.clients.model.ranger_base import *
+from apache_ranger.utils             import *
+
+class RangerRazRequestBase(RangerBase):
+    def __init__(self, attrs=None):
+        attrs = attrs or {}
+
+        RangerBase.__init__(self, attrs)
+
+        self.requestId       = attrs.get('requestId')
+        self.serviceType     = attrs.get('serviceType')
+        self.serviceName     = attrs.get('serviceName')
+        self.user            = attrs.get('user')
+        self.userGroups      = attrs.get('userGroups')
+        self.accessTime      = attrs.get('accessTime')
+        self.clientIpAddress = attrs.get('clientIpAddress')
+        self.clientType      = attrs.get('clientType')
+        self.clusterName     = attrs.get('clusterName')
+        self.clusterType     = attrs.get('clusterType')
+        self.sessionId       = attrs.get('sessionId')
+        self.context         = attrs.get('context')
+
+    def type_coerce_attrs(self):
+        super(RangerRazRequestBase, self).type_coerce_attrs()
+
+class ResourceAccess(RangerBase):
+    def __init__(self, attrs=None):
+        attrs = attrs or {}
+
+        RangerBase.__init__(self, attrs)
+
+        self.resource      = attrs.get('resource')
+        self.resourceOwner = attrs.get('resourceOwner')
+        self.action        = attrs.get('action')
+        self.accessTypes   = attrs.get('accessTypes')
+
+    def type_coerce_attrs(self):
+        super(ResourceAccess, self).type_coerce_attrs()
+
+class RangerRazRequest(RangerRazRequestBase):
+    def __init__(self, attrs=None):
+        attrs = attrs or {}
+
+        RangerRazRequestBase.__init__(self, attrs)
+
+        self.operation = attrs.get('operation')
+
+    def type_coerce_attrs(self):
+        super(RangerRazRequest, self).type_coerce_attrs()
+
+        self.operation = type_coerce(self.operation, ResourceAccess)
+
+class RangerRazMultiOperationRequest(RangerRazRequestBase):
+    def __init__(self, attrs=None):
+        attrs = attrs or {}
+
+        RangerRazRequestBase.__init__(self, attrs)
+
+        self.operations = attrs.get('operations')
+
+    def type_coerce_attrs(self):
+        super(RangerRazMultiOperationRequest, self).type_coerce_attrs()
+
+        self.operation = type_coerce_list(self.operation, ResourceAccess)
+
+class RangerRazResultBase(RangerBase):
+    ALLOWED        = 0
+    DENIED         = 1
+    NOT_DETERMINED = 2
+
+    def __init__(self, attrs=None):
+        attrs = attrs or {}
+
+        RangerBase.__init__(self, attrs)
+
+        self.requestId = attrs.get('requestId')
+
+    def type_coerce_attrs(self):
+        super(RangerRazResultBase, self).type_coerce_attrs()
+
+class AuditInfo(RangerBase):
+    def __init__(self, attrs=None):
+        attrs = attrs or {}
+
+        RangerBase.__init__(self, attrs)
+
+        self.auditId       = attrs.get('auditId')
+        self.accessType    = attrs.get('accessType')
+        self.result        = attrs.get('result')
+        self.policyId      = attrs.get('policyId')
+        self.policyVersion = attrs.get('policyVersion')
+
+    def type_coerce_attrs(self):
+        super(AuditInfo, self).type_coerce_attrs()
+
+class ResourceAccessResult(RangerBase):
+    def __init__(self, attrs=None):
+        attrs = attrs or {}
+
+        RangerBase.__init__(self, attrs)
+
+        self.result         = attrs.get('result')
+        self.isAudited      = attrs.get('isAudited')
+        self.auditLogs      = attrs.get('auditLogs')
+        self.additionalInfo = attrs.get('additionalInfo')
+
+    def type_coerce_attrs(self):
+        super(ResourceAccessResult, self).type_coerce_attrs()
+
+        self.auditLogs = type_coerce_list(self.operation, AuditInfo)
+
+class RangerRazResult(RangerRazResultBase):
+    def __init__(self, attrs=None):
+        attrs = attrs or {}
+
+        RangerRazResultBase.__init__(self, attrs)
+
+        self.operResult = attrs.get('operResult')
+
+    def type_coerce_attrs(self):
+        super(RangerRazResult, self).type_coerce_attrs()
+
+        self.operResult = type_coerce(self.operResult, ResourceAccessResult)
+
+class RangerRazMultiOperationResult(RangerRazResultBase):
+    def __init__(self, attrs=None):
+        attrs = attrs or {}
+
+        RangerRazRequestBase.__init__(self, attrs)
+
+        self.operResults = attrs.get('operResults')
+
+    def type_coerce_attrs(self):
+        super(RangerRazMultiOperationResult, self).type_coerce_attrs()
+
+        self.operResults = type_coerce_list(self.operResults, ResourceAccessResult)