Skip to content

Commit 1da17c4

Browse files
authored
[raz] Raz Client for ADLS to submit proper requests to getback SAS token (#2362)
- Update existing S3 client to support ADLS - Updated UTs - Currently, it sends the read request to get ADLS SAS Token, we need more info for mapping with request methods to read/write/delete ops for the token
1 parent 048b8b8 commit 1da17c4

File tree

2 files changed

+145
-52
lines changed

2 files changed

+145
-52
lines changed

desktop/core/src/desktop/lib/raz/raz_client.py

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def __init__(self, raz_url, raz_token, username, service='s3', service_name='cm_
7676
self.raz_token = raz_token
7777
self.username = username
7878
self.service = service
79+
7980
if self.service == 'adls':
8081
self.service_params = {
8182
'endpoint_prefix': 'adls',
@@ -88,6 +89,7 @@ def __init__(self, raz_url, raz_token, username, service='s3', service_name='cm_
8889
'service_name': 's3',
8990
'serviceType': 's3'
9091
}
92+
9193
self.service_name = service_name
9294
self.cluster_name = cluster_name
9395
self.requestid = str(uuid.uuid4())
@@ -100,51 +102,34 @@ def check_access(self, method, url, params=None, headers=None):
100102
params = params if params is not None else {}
101103
headers = headers if headers is not None else {}
102104

103-
allparams = [raz_signer.StringListStringMapProto(key=key, value=[val]) for key, val in url_params.items()]
104-
allparams.extend([raz_signer.StringListStringMapProto(key=key, value=[val]) for key, val in params.items()])
105-
headers = [raz_signer.StringStringMapProto(key=key, value=val) for key, val in headers.items()]
106105
endpoint = "%s://%s" % (path.scheme, path.netloc)
107106
resource_path = path.path.lstrip("/")
108107

109-
LOG.debug(
110-
"Preparing sign request with http_method: {%s}, headers: {%s}, parameters: {%s}, endpoint: {%s}, resource_path: {%s}" %
111-
(method, headers, allparams, endpoint, resource_path)
112-
)
113-
raz_req = raz_signer.SignRequestProto(
114-
endpoint_prefix=self.service_params['endpoint_prefix'],
115-
service_name=self.service_params['service_name'],
116-
endpoint=endpoint,
117-
http_method=method,
118-
headers=headers,
119-
parameters=allparams,
120-
resource_path=resource_path,
121-
time_offset=0
122-
)
123-
raz_req_serialized = raz_req.SerializeToString()
124-
signed_request = base64.b64encode(raz_req_serialized)
125-
126108
request_data = {
127109
"requestId": self.requestid,
128110
"serviceType": self.service_params['serviceType'],
129111
"serviceName": self.service_name,
130112
"user": self.username,
131113
"userGroups": [],
132-
"accessTime": "",
133114
"clientIpAddress": "",
134115
"clientType": "",
135116
"clusterName": self.cluster_name,
136117
"clusterType": "",
137118
"sessionId": "",
138-
"context": {
139-
"S3_SIGN_REQUEST": signed_request
140-
}
119+
"accessTime": "",
120+
"context": {}
141121
}
142-
headers = {"Content-Type":"application/json", "Accept-Encoding":"gzip,deflate"}
143-
raz_url = "%s/api/authz/s3/access?delegation=%s" % (self.raz_url, self.raz_token)
144-
LOG.debug('Raz url: %s' % raz_url)
122+
request_headers = {"Content-Type": "application/json"}
123+
raz_url = "%s/api/authz/%s/access?delegation=%s" % (self.raz_url, self.service, self.raz_token)
145124

146-
LOG.debug("Sending access check headers: {%s} request_data: {%s}" % (headers, request_data))
147-
raz_req = requests.post(raz_url, headers=headers, json=request_data, verify=False)
125+
if self.service == 'adls':
126+
self._make_adls_request(request_data, path, resource_path)
127+
elif self.service == 's3':
128+
self._make_s3_request(request_data, request_headers, method, params, headers, url_params, endpoint, resource_path)
129+
130+
LOG.debug('Raz url: %s' % raz_url)
131+
LOG.debug("Sending access check headers: {%s} request_data: {%s}" % (request_headers, request_data))
132+
raz_req = requests.post(raz_url, headers=request_headers, json=request_data, verify=False)
148133

149134
signed_response_result = None
150135
signed_response = None
@@ -164,21 +149,67 @@ def check_access(self, method, url, params=None, headers=None):
164149
if result == "ALLOWED":
165150
LOG.debug('Received allowed response %s' % raz_req.json())
166151
signed_response_data = raz_req.json()["operResult"]["additionalInfo"]
152+
167153
if self.service == 'adls':
168154
LOG.debug("Received SAS %s" % signed_response_data["ADLS_DSAS"])
169155
return {'token': signed_response_data["ADLS_DSAS"]}
170156
else:
171157
signed_response_result = signed_response_data["S3_SIGN_RESPONSE"]
172158

173-
if signed_response_result:
159+
if signed_response_result is not None:
174160
raz_response_proto = raz_signer.SignResponseProto()
175161
signed_response = raz_response_proto.FromString(base64.b64decode(signed_response_result))
176162
LOG.debug("Received signed Response %s" % signed_response)
177163

178164
# Signed headers "only"
179-
if signed_response:
165+
if signed_response is not None:
180166
return dict([(i.key, i.value) for i in signed_response.signer_generated_headers])
181167

168+
def _make_adls_request(self, request_data, path, resource_path):
169+
storage_account = path.netloc.split('.')[0]
170+
container, relative_path = resource_path.split('/', 1)
171+
172+
request_data.update({
173+
"clientType": "adls",
174+
"operation": {
175+
"resource": {
176+
"storageaccount": storage_account,
177+
"container": container,
178+
"relativepath": relative_path,
179+
},
180+
"resourceOwner": "",
181+
"action": "read",
182+
"accessTypes":["read"]
183+
}
184+
})
185+
186+
def _make_s3_request(self, request_data, request_headers, method, params, headers, url_params, endpoint, resource_path):
187+
188+
allparams = [raz_signer.StringListStringMapProto(key=key, value=[val]) for key, val in url_params.items()]
189+
allparams.extend([raz_signer.StringListStringMapProto(key=key, value=[val]) for key, val in params.items()])
190+
headers = [raz_signer.StringStringMapProto(key=key, value=val) for key, val in headers.items()]
191+
192+
LOG.debug(
193+
"Preparing sign request with http_method: {%s}, headers: {%s}, parameters: {%s}, endpoint: {%s}, resource_path: {%s}" %
194+
(method, headers, allparams, endpoint, resource_path)
195+
)
196+
raz_req = raz_signer.SignRequestProto(
197+
endpoint_prefix=self.service_params['endpoint_prefix'],
198+
service_name=self.service_params['service_name'],
199+
endpoint=endpoint,
200+
http_method=method,
201+
headers=headers,
202+
parameters=allparams,
203+
resource_path=resource_path,
204+
time_offset=0
205+
)
206+
raz_req_serialized = raz_req.SerializeToString()
207+
signed_request = base64.b64encode(raz_req_serialized)
208+
209+
request_headers["Accept-Encoding"] = {"gzip,deflate"}
210+
request_data["context"] = {
211+
"S3_SIGN_REQUEST": signed_request
212+
}
182213

183214
def get_raz_client(raz_url, username, auth='kerberos', service='s3', service_name='cm_s3', cluster_name='myCluster'):
184215
if not username:

desktop/core/src/desktop/lib/raz/raz_client_test.py

Lines changed: 83 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,9 @@ def test_renew_delegation_token(self):
8181

8282
t = token.renew_delegation_token(user=self.username)
8383

84-
assert_equal(
84+
assert_equal(t,
8585
'https://gethue-test.s3.amazonaws.com/gethue/data/customer.csv?AWSAccessKeyId=AKIA23E77ZX2HVY76YGL&'
86-
'Signature=3lhK%2BwtQ9Q2u5VDIqb4MEpoY3X4%3D&Expires=1617207304',
87-
t
86+
'Signature=3lhK%2BwtQ9Q2u5VDIqb4MEpoY3X4%3D&Expires=1617207304'
8887
)
8988

9089
with patch('desktop.lib.raz.raz_client.requests.put') as requests_put:
@@ -100,36 +99,103 @@ class RazClientTest(unittest.TestCase):
10099
def setUp(self):
101100
self.username = 'gethue'
102101
self.raz_url = 'https://raz.gethue.com:8080'
103-
self.resource_url = 'https://gethue-test.s3.amazonaws.com/gethue/data/customer.csv'
104102

105-
def test_get_raz_client(self):
103+
self.s3_path = 'https://gethue-test.s3.amazonaws.com/gethue/data/customer.csv'
104+
self.adls_path = 'https://gethuestorageaccount.blob.core.windows.net/demo-gethue-container/demo-dir1/customer.csv'
106105

106+
def test_get_raz_client_adls(self):
107107
with patch('desktop.lib.raz.raz_client.RazToken') as RazToken:
108108
with patch('desktop.lib.raz.raz_client.requests_kerberos.HTTPKerberosAuth') as HTTPKerberosAuth:
109109
client = get_raz_client(
110110
raz_url=self.raz_url,
111111
username=self.username,
112112
auth='kerberos',
113-
service='s3',
114-
service_name='gethue_s3',
113+
service='adls',
114+
service_name='gethue_adls',
115115
cluster_name='gethueCluster'
116116
)
117117

118118
assert_true(isinstance(client, RazClient))
119119

120120
HTTPKerberosAuth.assert_called()
121-
assert_equal(
122-
client.raz_url, self.raz_url
121+
assert_equal(client.raz_url, self.raz_url)
122+
assert_equal(client.service_name, 'gethue_adls')
123+
assert_equal(client.cluster_name, 'gethueCluster')
124+
125+
def test_check_access_adls(self):
126+
with patch('desktop.lib.raz.raz_client.requests.post') as requests_post:
127+
with patch('desktop.lib.raz.raz_client.uuid.uuid4') as uuid:
128+
raz_token = "mock_RAZ_token"
129+
130+
requests_post.return_value = Mock(
131+
json=Mock(return_value=
132+
{
133+
'operResult': {
134+
'result': 'ALLOWED',
135+
'additionalInfo': {
136+
"ADLS_DSAS": "nulltenantIdnullnullbnullALLOWEDnullnull1.05nSlN7t/QiPJ1OFlCruTEPLibFbAhEYYj5wbJuaeQqs="
137+
}
138+
}
139+
}
140+
)
123141
)
124-
assert_equal(
125-
client.service_name, 'gethue_s3'
142+
uuid.return_value = 'mock_request_id'
143+
144+
client = RazClient(self.raz_url, raz_token, username=self.username, service="adls", service_name="adls", cluster_name="cl1")
145+
146+
resp = client.check_access(method='GET', url=self.adls_path)
147+
148+
requests_post.assert_called_with(
149+
"https://raz.gethue.com:8080/api/authz/adls/access?delegation=" + raz_token,
150+
headers={"Content-Type": "application/json"},
151+
json={
152+
'requestId': 'mock_request_id',
153+
'serviceType': 'adls',
154+
'serviceName': 'adls',
155+
'user': 'gethue',
156+
'userGroups': [],
157+
'clientIpAddress': '',
158+
'clientType': 'adls',
159+
'clusterName': 'cl1',
160+
'clusterType': '',
161+
'sessionId': '',
162+
'accessTime': '',
163+
'context': {},
164+
'operation': {
165+
'resource': {
166+
'storageaccount': 'gethuestorageaccount',
167+
'container': 'demo-gethue-container',
168+
'relativepath': 'demo-dir1/customer.csv'
169+
},
170+
'resourceOwner': '',
171+
'action': 'read',
172+
'accessTypes': ['read']
173+
}
174+
},
175+
verify=False
126176
)
127-
assert_equal(
128-
client.cluster_name, 'gethueCluster'
177+
assert_equal(resp['token'], "nulltenantIdnullnullbnullALLOWEDnullnull1.05nSlN7t/QiPJ1OFlCruTEPLibFbAhEYYj5wbJuaeQqs=")
178+
179+
def test_get_raz_client_s3(self):
180+
with patch('desktop.lib.raz.raz_client.RazToken') as RazToken:
181+
with patch('desktop.lib.raz.raz_client.requests_kerberos.HTTPKerberosAuth') as HTTPKerberosAuth:
182+
client = get_raz_client(
183+
raz_url=self.raz_url,
184+
username=self.username,
185+
auth='kerberos',
186+
service='s3',
187+
service_name='gethue_s3',
188+
cluster_name='gethueCluster'
129189
)
130190

191+
assert_true(isinstance(client, RazClient))
192+
193+
HTTPKerberosAuth.assert_called()
194+
assert_equal(client.raz_url, self.raz_url)
195+
assert_equal(client.service_name, 'gethue_s3')
196+
assert_equal(client.cluster_name, 'gethueCluster')
131197

132-
def test_check_access(self):
198+
def test_check_access_s3(self):
133199
raz_token = Mock()
134200

135201
client = RazClient(self.raz_url, raz_token, username=self.username)
@@ -162,11 +228,7 @@ def test_check_access(self):
162228
)
163229
)
164230

165-
resp = client.check_access(method='GET', url=self.resource_url)
231+
resp = client.check_access(method='GET', url=self.s3_path)
166232

167-
assert_true(
168-
resp
169-
)
170-
assert_equal(
171-
resp['AWSAccessKeyId'], 'AKIA23E77ZX2HVY76YGL'
172-
)
233+
assert_true(resp)
234+
assert_equal(resp['AWSAccessKeyId'], 'AKIA23E77ZX2HVY76YGL')

0 commit comments

Comments
 (0)