esdk-obs-python 3.25.3__tar.gz → 3.26.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/PKG-INFO +10 -11
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/esdk_obs_python.egg-info/PKG-INFO +10 -11
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/esdk_obs_python.egg-info/not-zip-safe +1 -1
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/__init__.py +3 -2
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/auth.py +278 -278
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/bulktasks.py +218 -218
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/cache.py +44 -44
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/client.py +47 -31
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/const.py +8 -5
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/convertor.py +27 -9
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/extension.py +152 -152
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/http2.py +169 -169
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/ilog.py +147 -147
- esdk-obs-python-3.26.2/obs/loadtoken.py +341 -0
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/locks.py +28 -28
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/model.py +37 -8
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/obs_cipher_suite.py +358 -358
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/posix_transfer.py +83 -83
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/progress.py +83 -83
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/scheduler.py +127 -127
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/searchmethod.py +41 -41
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/transfer.py +832 -830
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/workflow.py +424 -424
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/setup.cfg +4 -4
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/setup.py +1 -1
- esdk-obs-python-3.25.3/obs/loadtoken.py +0 -184
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/esdk_obs_python.egg-info/SOURCES.txt +0 -0
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/esdk_obs_python.egg-info/dependency_links.txt +0 -0
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/esdk_obs_python.egg-info/requires.txt +0 -0
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/esdk_obs_python.egg-info/top_level.txt +0 -0
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/bucket.py +0 -0
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/crc64mod.py +0 -0
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/crypto_client.py +0 -0
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/http.py +0 -0
- {esdk-obs-python-3.25.3 → esdk-obs-python-3.26.2}/obs/util.py +0 -0
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: esdk-obs-python
|
|
3
|
-
Version: 3.
|
|
4
|
-
Summary: OBS Python SDK
|
|
5
|
-
Home-page:
|
|
6
|
-
License: Apache-2.0
|
|
7
|
-
Keywords: obs,python
|
|
8
|
-
Platform: Independant
|
|
9
|
-
|
|
10
|
-
OBS Python SDK
|
|
11
|
-
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: esdk-obs-python
|
|
3
|
+
Version: 3.26.2
|
|
4
|
+
Summary: OBS Python SDK
|
|
5
|
+
Home-page:
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Keywords: obs,python
|
|
8
|
+
Platform: Independant
|
|
9
|
+
|
|
10
|
+
OBS Python SDK
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: esdk-obs-python
|
|
3
|
-
Version: 3.
|
|
4
|
-
Summary: OBS Python SDK
|
|
5
|
-
Home-page:
|
|
6
|
-
License: Apache-2.0
|
|
7
|
-
Keywords: obs,python
|
|
8
|
-
Platform: Independant
|
|
9
|
-
|
|
10
|
-
OBS Python SDK
|
|
11
|
-
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: esdk-obs-python
|
|
3
|
+
Version: 3.26.2
|
|
4
|
+
Summary: OBS Python SDK
|
|
5
|
+
Home-page:
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Keywords: obs,python
|
|
8
|
+
Platform: Independant
|
|
9
|
+
|
|
10
|
+
OBS Python SDK
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
@@ -23,7 +23,7 @@ from obs.model import TopicConfiguration, FunctionGraphConfiguration, FilterRule
|
|
|
23
23
|
from obs.model import Options, PutObjectHeader, AppendObjectHeader, AppendObjectContent, RedirectAllRequestTo
|
|
24
24
|
from obs.model import Redirect, RoutingRule, Tag, TagInfo, Transition, NoncurrentVersionTransition, Rule, Versions, AbortIncompleteMultipartUpload
|
|
25
25
|
from obs.model import Object, WebsiteConfiguration, Logging, CompleteMultipartUploadRequest, DeleteObjectsRequest,CustomDomainConfiguration
|
|
26
|
-
from obs.model import ListMultipartUploadsRequest, GetObjectRequest, UploadFileHeader, Payer
|
|
26
|
+
from obs.model import ListMultipartUploadsRequest, GetObjectRequest, UploadFileHeader, Payer, ClientVerify
|
|
27
27
|
from obs.model import ExtensionHeader, FetchStatus, BucketAliasModel, ListBucketAliasModel
|
|
28
28
|
from obs.workflow import WorkflowClient
|
|
29
29
|
from obs.crypto_client import CryptoObsClient
|
|
@@ -97,5 +97,6 @@ __all__ = [
|
|
|
97
97
|
'CtrRSACipherGenerator',
|
|
98
98
|
'BucketAliasModel',
|
|
99
99
|
'ListBucketAliasModel',
|
|
100
|
-
'CustomDomainConfiguration'
|
|
100
|
+
'CustomDomainConfiguration',
|
|
101
|
+
'ClientVerify'
|
|
101
102
|
]
|
|
@@ -1,278 +1,278 @@
|
|
|
1
|
-
#!/usr/bin/python
|
|
2
|
-
# -*- coding:utf-8 -*-
|
|
3
|
-
# Copyright 2019 Huawei Technologies Co.,Ltd.
|
|
4
|
-
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
5
|
-
# this file except in compliance with the License. You may obtain a copy of the
|
|
6
|
-
# License at
|
|
7
|
-
|
|
8
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
|
|
10
|
-
# Unless required by applicable law or agreed to in writing, software distributed
|
|
11
|
-
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
12
|
-
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
13
|
-
# specific language governing permissions and limitations under the License.
|
|
14
|
-
|
|
15
|
-
import hashlib
|
|
16
|
-
import hmac
|
|
17
|
-
import binascii
|
|
18
|
-
from obs import util
|
|
19
|
-
from obs import const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class Authentication(object):
|
|
23
|
-
|
|
24
|
-
def __init__(self, ak, sk, path_style, ha, server, is_cname):
|
|
25
|
-
self.ak = ak
|
|
26
|
-
self.sk = sk
|
|
27
|
-
self.path_style = path_style
|
|
28
|
-
self.ha = ha
|
|
29
|
-
self.server = server
|
|
30
|
-
self.is_cname = is_cname
|
|
31
|
-
|
|
32
|
-
def doAuth(self, method, bucket, key, path_args, headers, expires=None):
|
|
33
|
-
ret = self.getSignature(method, bucket, key, path_args, headers, expires)
|
|
34
|
-
return {
|
|
35
|
-
const.AUTHORIZATION_HEADER: '%s %s:%s' % (self.ha.auth_prefix(), self.ak, ret['Signature']),
|
|
36
|
-
const.CANONICAL_STRING: ret[const.CANONICAL_STRING]
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
def getSignature(self, method, bucket, key, path_args, headers, expires=None):
|
|
40
|
-
canonical_string = self.__make_canonical_string(method, bucket, key, path_args, headers, expires)
|
|
41
|
-
return {
|
|
42
|
-
'Signature': self.hmacSha128(canonical_string),
|
|
43
|
-
const.CANONICAL_STRING: canonical_string
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
def hmacSha128(self, canonical_string):
|
|
47
|
-
if const.IS_PYTHON2:
|
|
48
|
-
hashed = hmac.new(self.sk, canonical_string, hashlib.sha1)
|
|
49
|
-
encode_canonical = binascii.b2a_base64(hashed.digest())[:-1]
|
|
50
|
-
else:
|
|
51
|
-
hashed = hmac.new(self.sk.encode('UTF-8'), canonical_string.encode('UTF-8'), hashlib.sha1)
|
|
52
|
-
encode_canonical = binascii.b2a_base64(hashed.digest())[:-1].decode('UTF-8')
|
|
53
|
-
|
|
54
|
-
return encode_canonical
|
|
55
|
-
|
|
56
|
-
def __make_canonical_string(self, method, bucket_name, key, path_args, headers, expires=None):
|
|
57
|
-
interesting_headers = self.__make_canonicalstring_interesting_headers(headers, expires)
|
|
58
|
-
key_list = sorted(interesting_headers.keys())
|
|
59
|
-
str_list = self.__make_canonicalstring_str_list(key_list, method, interesting_headers)
|
|
60
|
-
URI = ''
|
|
61
|
-
_bucket_name = self.server if self.is_cname else bucket_name
|
|
62
|
-
if _bucket_name:
|
|
63
|
-
URI += '/'
|
|
64
|
-
URI += _bucket_name
|
|
65
|
-
if not self.path_style or self.is_cname:
|
|
66
|
-
URI += '/'
|
|
67
|
-
|
|
68
|
-
if key:
|
|
69
|
-
if not URI.endswith('/'):
|
|
70
|
-
URI += '/'
|
|
71
|
-
URI += util.encode_object_key(key)
|
|
72
|
-
|
|
73
|
-
if URI:
|
|
74
|
-
str_list.append(URI)
|
|
75
|
-
else:
|
|
76
|
-
str_list.append('/')
|
|
77
|
-
|
|
78
|
-
if path_args:
|
|
79
|
-
e = '?'
|
|
80
|
-
cannoList = sorted(path_args.items(), key=lambda d: d[0])
|
|
81
|
-
for path_key, path_value in cannoList:
|
|
82
|
-
if path_key.lower() in const.ALLOWED_RESOURCE_PARAMTER_NAMES or path_key.lower().startswith(
|
|
83
|
-
self.ha._get_header_prefix()):
|
|
84
|
-
path_key = util.encode_item(path_key, '/')
|
|
85
|
-
if path_value is None:
|
|
86
|
-
e += path_key + '&'
|
|
87
|
-
continue
|
|
88
|
-
e += path_key + '=' + util.to_string(path_value) + '&'
|
|
89
|
-
|
|
90
|
-
e = e[:-1]
|
|
91
|
-
str_list.append(e)
|
|
92
|
-
return ''.join(str_list)
|
|
93
|
-
|
|
94
|
-
def __make_canonicalstring_interesting_headers(self, headers, expires):
|
|
95
|
-
interesting_headers = {}
|
|
96
|
-
if isinstance(headers, dict):
|
|
97
|
-
for hash_key in headers.keys():
|
|
98
|
-
lk = hash_key.lower()
|
|
99
|
-
if lk in const.CONTENT_LIST or lk.startswith(self.ha._get_header_prefix()):
|
|
100
|
-
s = headers.get(hash_key)
|
|
101
|
-
interesting_headers[lk] = ''.join(s)
|
|
102
|
-
|
|
103
|
-
key_list = interesting_headers.keys()
|
|
104
|
-
|
|
105
|
-
if self.ha.date_header() in key_list:
|
|
106
|
-
interesting_headers[const.DATE_HEADER.lower()] = ''
|
|
107
|
-
|
|
108
|
-
if expires:
|
|
109
|
-
interesting_headers[const.DATE_HEADER.lower()] = expires
|
|
110
|
-
|
|
111
|
-
if const.CONTENT_TYPE_HEADER.lower() not in key_list:
|
|
112
|
-
interesting_headers[const.CONTENT_TYPE_HEADER.lower()] = ''
|
|
113
|
-
|
|
114
|
-
if const.CONTENT_MD5_HEADER.lower() not in key_list:
|
|
115
|
-
interesting_headers[const.CONTENT_MD5_HEADER.lower()] = ''
|
|
116
|
-
|
|
117
|
-
return interesting_headers
|
|
118
|
-
|
|
119
|
-
def __make_canonicalstring_str_list(self, keylist, method, interesting_headers):
|
|
120
|
-
str_list = [method + '\n']
|
|
121
|
-
for k in keylist:
|
|
122
|
-
header_key = util.to_string(k)
|
|
123
|
-
val = '' if interesting_headers[header_key] is None else interesting_headers[header_key]
|
|
124
|
-
if header_key.startswith(self.ha._get_meta_header_prefix()):
|
|
125
|
-
str_list.append(header_key + ':' + util.to_string(val).strip())
|
|
126
|
-
elif header_key.startswith(self.ha._get_header_prefix()):
|
|
127
|
-
str_list.append(header_key + ':' + val)
|
|
128
|
-
else:
|
|
129
|
-
str_list.append(val)
|
|
130
|
-
str_list.append('\n')
|
|
131
|
-
return str_list
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
class V4Authentication(object):
|
|
135
|
-
CONTENT_SHA256 = 'UNSIGNED-PAYLOAD'
|
|
136
|
-
|
|
137
|
-
def __init__(self, ak, sk, region, shortDate, longDate, path_style, ha):
|
|
138
|
-
self.ak = ak
|
|
139
|
-
self.sk = sk
|
|
140
|
-
self.region = region
|
|
141
|
-
self.shortDate = shortDate
|
|
142
|
-
self.longDate = longDate
|
|
143
|
-
self.path_style = path_style
|
|
144
|
-
self.ha = ha
|
|
145
|
-
|
|
146
|
-
def doAuth(self, method, bucket, key, args_path, headers):
|
|
147
|
-
args_path = args_path if isinstance(args_path, dict) else {}
|
|
148
|
-
headers = headers if isinstance(headers, dict) else {}
|
|
149
|
-
headers[self.ha.content_sha256_header()] = self.CONTENT_SHA256
|
|
150
|
-
|
|
151
|
-
credential = self.getCredential()
|
|
152
|
-
headMap = self.setMapKeyLower(headers)
|
|
153
|
-
signedHeaders = self.getSignedHeaders(headMap)
|
|
154
|
-
ret = self.getSignature(method, bucket, key, args_path, headMap, signedHeaders)
|
|
155
|
-
auth = 'AWS4-HMAC-SHA256 Credential=%s,SignedHeaders=%s,Signature=%s' % (
|
|
156
|
-
credential, signedHeaders, ret['Signature'])
|
|
157
|
-
return {
|
|
158
|
-
const.AUTHORIZATION_HEADER: auth,
|
|
159
|
-
const.CANONICAL_REQUEST: ret[const.CANONICAL_REQUEST]
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
def getCredential(self):
|
|
163
|
-
return '%s/%s/%s/s3/aws4_request' % (self.ak, self.shortDate, self.region)
|
|
164
|
-
|
|
165
|
-
def getScope(self):
|
|
166
|
-
return '%s/%s/s3/aws4_request' % (self.shortDate, self.region)
|
|
167
|
-
|
|
168
|
-
@staticmethod
|
|
169
|
-
def getSignedHeaders(headMap):
|
|
170
|
-
headList = sorted(headMap.items(), key=lambda d: d[0])
|
|
171
|
-
signedHeaders = ''
|
|
172
|
-
i = 0
|
|
173
|
-
for val in headList:
|
|
174
|
-
if i != 0:
|
|
175
|
-
signedHeaders += ';'
|
|
176
|
-
signedHeaders += val[0]
|
|
177
|
-
i = 1
|
|
178
|
-
return signedHeaders
|
|
179
|
-
|
|
180
|
-
def getSignature(self, method, bucket, key, args_path, headMap, signedHeaders, payload=None):
|
|
181
|
-
outPut = 'AWS4-HMAC-SHA256' + '\n'
|
|
182
|
-
outPut += self.longDate + '\n'
|
|
183
|
-
outPut += self.getScope() + '\n'
|
|
184
|
-
cannonicalRequest = self.getCanonicalRequest(method, bucket, key, args_path, headMap, signedHeaders, payload)
|
|
185
|
-
|
|
186
|
-
if const.IS_PYTHON2:
|
|
187
|
-
stringToSign = outPut + self.__shaCannonicalRequest_python2(cannonicalRequest)
|
|
188
|
-
signingKey = self.getSigningKey_python2()
|
|
189
|
-
else:
|
|
190
|
-
stringToSign = outPut + self.__shaCannonicalRequest_python3(cannonicalRequest)
|
|
191
|
-
stringToSign = stringToSign.encode('UTF-8')
|
|
192
|
-
signingKey = self.getSigningKey_python3()
|
|
193
|
-
return {
|
|
194
|
-
'Signature': self.hmacSha256(signingKey, stringToSign),
|
|
195
|
-
const.CANONICAL_REQUEST: cannonicalRequest
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
@staticmethod
|
|
199
|
-
def hmacSha256(signingKey, stringToSign):
|
|
200
|
-
return hmac.new(signingKey, stringToSign, hashlib.sha256).hexdigest()
|
|
201
|
-
|
|
202
|
-
def getSigningKey_python2(self):
|
|
203
|
-
key = 'AWS4' + self.sk
|
|
204
|
-
dateKey = hmac.new(key, self.shortDate, hashlib.sha256).digest()
|
|
205
|
-
dateRegionKey = hmac.new(dateKey, self.region, hashlib.sha256).digest()
|
|
206
|
-
dateRegionServiceKey = hmac.new(dateRegionKey, 's3', hashlib.sha256).digest()
|
|
207
|
-
signingKey = hmac.new(dateRegionServiceKey, 'aws4_request', hashlib.sha256).digest()
|
|
208
|
-
return signingKey
|
|
209
|
-
|
|
210
|
-
def getSigningKey_python3(self):
|
|
211
|
-
key = 'AWS4' + self.sk
|
|
212
|
-
dateKey = hmac.new(key.encode('UTF-8'), self.shortDate.encode('UTF-8'), hashlib.sha256).digest()
|
|
213
|
-
dateRegionKey = hmac.new(dateKey, self.region.encode('UTF-8'), hashlib.sha256).digest()
|
|
214
|
-
dateRegionServiceKey = hmac.new(dateRegionKey, 's3'.encode('UTF-8'), hashlib.sha256).digest()
|
|
215
|
-
signingKey = hmac.new(dateRegionServiceKey, 'aws4_request'.encode('UTF-8'), hashlib.sha256).digest()
|
|
216
|
-
return signingKey
|
|
217
|
-
|
|
218
|
-
def getCanonicalRequest(self, method, bucket, key, args_path, headMap, signedHeaders, payload=None):
|
|
219
|
-
output = [method]
|
|
220
|
-
output.append(self.getCanonicalURI(bucket, key))
|
|
221
|
-
output.append(self.getCanonicalQueryString(args_path))
|
|
222
|
-
output.append(self.getCanonicalHeaders(headMap))
|
|
223
|
-
output.append(signedHeaders)
|
|
224
|
-
output.append(self.CONTENT_SHA256 if payload is None else payload)
|
|
225
|
-
return '\n'.join(output)
|
|
226
|
-
|
|
227
|
-
@staticmethod
|
|
228
|
-
def __shaCannonicalRequest_python2(cannonicalRequest):
|
|
229
|
-
return hashlib.sha256(cannonicalRequest).hexdigest()
|
|
230
|
-
|
|
231
|
-
@staticmethod
|
|
232
|
-
def __shaCannonicalRequest_python3(cannonicalRequest):
|
|
233
|
-
return hashlib.sha256(cannonicalRequest.encode('UTF-8')).hexdigest()
|
|
234
|
-
|
|
235
|
-
def getCanonicalURI(self, bucket=None, key=None):
|
|
236
|
-
URI = ''
|
|
237
|
-
if self.path_style and bucket:
|
|
238
|
-
URI += '/' + bucket
|
|
239
|
-
if key:
|
|
240
|
-
URI += '/' + key
|
|
241
|
-
if not URI:
|
|
242
|
-
URI = '/'
|
|
243
|
-
return util.encode_object_key(URI)
|
|
244
|
-
|
|
245
|
-
@staticmethod
|
|
246
|
-
def getCanonicalQueryString(args_path):
|
|
247
|
-
canonMap = {}
|
|
248
|
-
for key, value in args_path.items():
|
|
249
|
-
canonMap[key] = value
|
|
250
|
-
cannoList = sorted(canonMap.items(), key=lambda d: d[0])
|
|
251
|
-
queryStr = ''
|
|
252
|
-
i = 0
|
|
253
|
-
for val in cannoList:
|
|
254
|
-
if i != 0:
|
|
255
|
-
queryStr += '&'
|
|
256
|
-
queryStr += '%s=%s' % (util.encode_item(val[0], '/'), util.encode_item(val[1], ''))
|
|
257
|
-
i = 1
|
|
258
|
-
return queryStr
|
|
259
|
-
|
|
260
|
-
@staticmethod
|
|
261
|
-
def getCanonicalHeaders(headMap):
|
|
262
|
-
headList = sorted(headMap.items(), key=lambda d: d[0])
|
|
263
|
-
canonicalHeaderStr = ''
|
|
264
|
-
for val in headList:
|
|
265
|
-
if isinstance(val[1], list):
|
|
266
|
-
tlist = sorted(val[1])
|
|
267
|
-
for v in tlist:
|
|
268
|
-
canonicalHeaderStr += val[0] + ':' + v + '\n'
|
|
269
|
-
else:
|
|
270
|
-
canonicalHeaderStr += val[0] + ':' + str(val[1]) + '\n'
|
|
271
|
-
return canonicalHeaderStr
|
|
272
|
-
|
|
273
|
-
@staticmethod
|
|
274
|
-
def setMapKeyLower(inputMap):
|
|
275
|
-
outputMap = {}
|
|
276
|
-
for key in inputMap.keys():
|
|
277
|
-
outputMap[key.lower()] = inputMap[key]
|
|
278
|
-
return outputMap
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding:utf-8 -*-
|
|
3
|
+
# Copyright 2019 Huawei Technologies Co.,Ltd.
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
5
|
+
# this file except in compliance with the License. You may obtain a copy of the
|
|
6
|
+
# License at
|
|
7
|
+
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software distributed
|
|
11
|
+
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
12
|
+
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
13
|
+
# specific language governing permissions and limitations under the License.
|
|
14
|
+
|
|
15
|
+
import hashlib
|
|
16
|
+
import hmac
|
|
17
|
+
import binascii
|
|
18
|
+
from obs import util
|
|
19
|
+
from obs import const
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Authentication(object):
|
|
23
|
+
|
|
24
|
+
def __init__(self, ak, sk, path_style, ha, server, is_cname):
|
|
25
|
+
self.ak = ak
|
|
26
|
+
self.sk = sk
|
|
27
|
+
self.path_style = path_style
|
|
28
|
+
self.ha = ha
|
|
29
|
+
self.server = server
|
|
30
|
+
self.is_cname = is_cname
|
|
31
|
+
|
|
32
|
+
def doAuth(self, method, bucket, key, path_args, headers, expires=None):
|
|
33
|
+
ret = self.getSignature(method, bucket, key, path_args, headers, expires)
|
|
34
|
+
return {
|
|
35
|
+
const.AUTHORIZATION_HEADER: '%s %s:%s' % (self.ha.auth_prefix(), self.ak, ret['Signature']),
|
|
36
|
+
const.CANONICAL_STRING: ret[const.CANONICAL_STRING]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
def getSignature(self, method, bucket, key, path_args, headers, expires=None):
|
|
40
|
+
canonical_string = self.__make_canonical_string(method, bucket, key, path_args, headers, expires)
|
|
41
|
+
return {
|
|
42
|
+
'Signature': self.hmacSha128(canonical_string),
|
|
43
|
+
const.CANONICAL_STRING: canonical_string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
def hmacSha128(self, canonical_string):
|
|
47
|
+
if const.IS_PYTHON2:
|
|
48
|
+
hashed = hmac.new(self.sk, canonical_string, hashlib.sha1)
|
|
49
|
+
encode_canonical = binascii.b2a_base64(hashed.digest())[:-1]
|
|
50
|
+
else:
|
|
51
|
+
hashed = hmac.new(self.sk.encode('UTF-8'), canonical_string.encode('UTF-8'), hashlib.sha1)
|
|
52
|
+
encode_canonical = binascii.b2a_base64(hashed.digest())[:-1].decode('UTF-8')
|
|
53
|
+
|
|
54
|
+
return encode_canonical
|
|
55
|
+
|
|
56
|
+
def __make_canonical_string(self, method, bucket_name, key, path_args, headers, expires=None):
|
|
57
|
+
interesting_headers = self.__make_canonicalstring_interesting_headers(headers, expires)
|
|
58
|
+
key_list = sorted(interesting_headers.keys())
|
|
59
|
+
str_list = self.__make_canonicalstring_str_list(key_list, method, interesting_headers)
|
|
60
|
+
URI = ''
|
|
61
|
+
_bucket_name = self.server if self.is_cname else bucket_name
|
|
62
|
+
if _bucket_name:
|
|
63
|
+
URI += '/'
|
|
64
|
+
URI += _bucket_name
|
|
65
|
+
if not self.path_style or self.is_cname:
|
|
66
|
+
URI += '/'
|
|
67
|
+
|
|
68
|
+
if key:
|
|
69
|
+
if not URI.endswith('/'):
|
|
70
|
+
URI += '/'
|
|
71
|
+
URI += util.encode_object_key(key)
|
|
72
|
+
|
|
73
|
+
if URI:
|
|
74
|
+
str_list.append(URI)
|
|
75
|
+
else:
|
|
76
|
+
str_list.append('/')
|
|
77
|
+
|
|
78
|
+
if path_args:
|
|
79
|
+
e = '?'
|
|
80
|
+
cannoList = sorted(path_args.items(), key=lambda d: d[0])
|
|
81
|
+
for path_key, path_value in cannoList:
|
|
82
|
+
if path_key.lower() in const.ALLOWED_RESOURCE_PARAMTER_NAMES or path_key.lower().startswith(
|
|
83
|
+
self.ha._get_header_prefix()):
|
|
84
|
+
path_key = util.encode_item(path_key, '/')
|
|
85
|
+
if path_value is None:
|
|
86
|
+
e += path_key + '&'
|
|
87
|
+
continue
|
|
88
|
+
e += path_key + '=' + util.to_string(path_value) + '&'
|
|
89
|
+
|
|
90
|
+
e = e[:-1]
|
|
91
|
+
str_list.append(e)
|
|
92
|
+
return ''.join(str_list)
|
|
93
|
+
|
|
94
|
+
def __make_canonicalstring_interesting_headers(self, headers, expires):
|
|
95
|
+
interesting_headers = {}
|
|
96
|
+
if isinstance(headers, dict):
|
|
97
|
+
for hash_key in headers.keys():
|
|
98
|
+
lk = hash_key.lower()
|
|
99
|
+
if lk in const.CONTENT_LIST or lk.startswith(self.ha._get_header_prefix()):
|
|
100
|
+
s = headers.get(hash_key)
|
|
101
|
+
interesting_headers[lk] = ''.join(s)
|
|
102
|
+
|
|
103
|
+
key_list = interesting_headers.keys()
|
|
104
|
+
|
|
105
|
+
if self.ha.date_header() in key_list:
|
|
106
|
+
interesting_headers[const.DATE_HEADER.lower()] = ''
|
|
107
|
+
|
|
108
|
+
if expires:
|
|
109
|
+
interesting_headers[const.DATE_HEADER.lower()] = expires
|
|
110
|
+
|
|
111
|
+
if const.CONTENT_TYPE_HEADER.lower() not in key_list:
|
|
112
|
+
interesting_headers[const.CONTENT_TYPE_HEADER.lower()] = ''
|
|
113
|
+
|
|
114
|
+
if const.CONTENT_MD5_HEADER.lower() not in key_list:
|
|
115
|
+
interesting_headers[const.CONTENT_MD5_HEADER.lower()] = ''
|
|
116
|
+
|
|
117
|
+
return interesting_headers
|
|
118
|
+
|
|
119
|
+
def __make_canonicalstring_str_list(self, keylist, method, interesting_headers):
|
|
120
|
+
str_list = [method + '\n']
|
|
121
|
+
for k in keylist:
|
|
122
|
+
header_key = util.to_string(k)
|
|
123
|
+
val = '' if interesting_headers[header_key] is None else interesting_headers[header_key]
|
|
124
|
+
if header_key.startswith(self.ha._get_meta_header_prefix()):
|
|
125
|
+
str_list.append(header_key + ':' + util.to_string(val).strip())
|
|
126
|
+
elif header_key.startswith(self.ha._get_header_prefix()):
|
|
127
|
+
str_list.append(header_key + ':' + val)
|
|
128
|
+
else:
|
|
129
|
+
str_list.append(val)
|
|
130
|
+
str_list.append('\n')
|
|
131
|
+
return str_list
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class V4Authentication(object):
|
|
135
|
+
CONTENT_SHA256 = 'UNSIGNED-PAYLOAD'
|
|
136
|
+
|
|
137
|
+
def __init__(self, ak, sk, region, shortDate, longDate, path_style, ha):
|
|
138
|
+
self.ak = ak
|
|
139
|
+
self.sk = sk
|
|
140
|
+
self.region = region
|
|
141
|
+
self.shortDate = shortDate
|
|
142
|
+
self.longDate = longDate
|
|
143
|
+
self.path_style = path_style
|
|
144
|
+
self.ha = ha
|
|
145
|
+
|
|
146
|
+
def doAuth(self, method, bucket, key, args_path, headers):
|
|
147
|
+
args_path = args_path if isinstance(args_path, dict) else {}
|
|
148
|
+
headers = headers if isinstance(headers, dict) else {}
|
|
149
|
+
headers[self.ha.content_sha256_header()] = self.CONTENT_SHA256
|
|
150
|
+
|
|
151
|
+
credential = self.getCredential()
|
|
152
|
+
headMap = self.setMapKeyLower(headers)
|
|
153
|
+
signedHeaders = self.getSignedHeaders(headMap)
|
|
154
|
+
ret = self.getSignature(method, bucket, key, args_path, headMap, signedHeaders)
|
|
155
|
+
auth = 'AWS4-HMAC-SHA256 Credential=%s,SignedHeaders=%s,Signature=%s' % (
|
|
156
|
+
credential, signedHeaders, ret['Signature'])
|
|
157
|
+
return {
|
|
158
|
+
const.AUTHORIZATION_HEADER: auth,
|
|
159
|
+
const.CANONICAL_REQUEST: ret[const.CANONICAL_REQUEST]
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
def getCredential(self):
|
|
163
|
+
return '%s/%s/%s/s3/aws4_request' % (self.ak, self.shortDate, self.region)
|
|
164
|
+
|
|
165
|
+
def getScope(self):
|
|
166
|
+
return '%s/%s/s3/aws4_request' % (self.shortDate, self.region)
|
|
167
|
+
|
|
168
|
+
@staticmethod
|
|
169
|
+
def getSignedHeaders(headMap):
|
|
170
|
+
headList = sorted(headMap.items(), key=lambda d: d[0])
|
|
171
|
+
signedHeaders = ''
|
|
172
|
+
i = 0
|
|
173
|
+
for val in headList:
|
|
174
|
+
if i != 0:
|
|
175
|
+
signedHeaders += ';'
|
|
176
|
+
signedHeaders += val[0]
|
|
177
|
+
i = 1
|
|
178
|
+
return signedHeaders
|
|
179
|
+
|
|
180
|
+
def getSignature(self, method, bucket, key, args_path, headMap, signedHeaders, payload=None):
|
|
181
|
+
outPut = 'AWS4-HMAC-SHA256' + '\n'
|
|
182
|
+
outPut += self.longDate + '\n'
|
|
183
|
+
outPut += self.getScope() + '\n'
|
|
184
|
+
cannonicalRequest = self.getCanonicalRequest(method, bucket, key, args_path, headMap, signedHeaders, payload)
|
|
185
|
+
|
|
186
|
+
if const.IS_PYTHON2:
|
|
187
|
+
stringToSign = outPut + self.__shaCannonicalRequest_python2(cannonicalRequest)
|
|
188
|
+
signingKey = self.getSigningKey_python2()
|
|
189
|
+
else:
|
|
190
|
+
stringToSign = outPut + self.__shaCannonicalRequest_python3(cannonicalRequest)
|
|
191
|
+
stringToSign = stringToSign.encode('UTF-8')
|
|
192
|
+
signingKey = self.getSigningKey_python3()
|
|
193
|
+
return {
|
|
194
|
+
'Signature': self.hmacSha256(signingKey, stringToSign),
|
|
195
|
+
const.CANONICAL_REQUEST: cannonicalRequest
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@staticmethod
|
|
199
|
+
def hmacSha256(signingKey, stringToSign):
|
|
200
|
+
return hmac.new(signingKey, stringToSign, hashlib.sha256).hexdigest()
|
|
201
|
+
|
|
202
|
+
def getSigningKey_python2(self):
|
|
203
|
+
key = 'AWS4' + self.sk
|
|
204
|
+
dateKey = hmac.new(key, self.shortDate, hashlib.sha256).digest()
|
|
205
|
+
dateRegionKey = hmac.new(dateKey, self.region, hashlib.sha256).digest()
|
|
206
|
+
dateRegionServiceKey = hmac.new(dateRegionKey, 's3', hashlib.sha256).digest()
|
|
207
|
+
signingKey = hmac.new(dateRegionServiceKey, 'aws4_request', hashlib.sha256).digest()
|
|
208
|
+
return signingKey
|
|
209
|
+
|
|
210
|
+
def getSigningKey_python3(self):
|
|
211
|
+
key = 'AWS4' + self.sk
|
|
212
|
+
dateKey = hmac.new(key.encode('UTF-8'), self.shortDate.encode('UTF-8'), hashlib.sha256).digest()
|
|
213
|
+
dateRegionKey = hmac.new(dateKey, self.region.encode('UTF-8'), hashlib.sha256).digest()
|
|
214
|
+
dateRegionServiceKey = hmac.new(dateRegionKey, 's3'.encode('UTF-8'), hashlib.sha256).digest()
|
|
215
|
+
signingKey = hmac.new(dateRegionServiceKey, 'aws4_request'.encode('UTF-8'), hashlib.sha256).digest()
|
|
216
|
+
return signingKey
|
|
217
|
+
|
|
218
|
+
def getCanonicalRequest(self, method, bucket, key, args_path, headMap, signedHeaders, payload=None):
|
|
219
|
+
output = [method]
|
|
220
|
+
output.append(self.getCanonicalURI(bucket, key))
|
|
221
|
+
output.append(self.getCanonicalQueryString(args_path))
|
|
222
|
+
output.append(self.getCanonicalHeaders(headMap))
|
|
223
|
+
output.append(signedHeaders)
|
|
224
|
+
output.append(self.CONTENT_SHA256 if payload is None else payload)
|
|
225
|
+
return '\n'.join(output)
|
|
226
|
+
|
|
227
|
+
@staticmethod
|
|
228
|
+
def __shaCannonicalRequest_python2(cannonicalRequest):
|
|
229
|
+
return hashlib.sha256(cannonicalRequest).hexdigest()
|
|
230
|
+
|
|
231
|
+
@staticmethod
|
|
232
|
+
def __shaCannonicalRequest_python3(cannonicalRequest):
|
|
233
|
+
return hashlib.sha256(cannonicalRequest.encode('UTF-8')).hexdigest()
|
|
234
|
+
|
|
235
|
+
def getCanonicalURI(self, bucket=None, key=None):
|
|
236
|
+
URI = ''
|
|
237
|
+
if self.path_style and bucket:
|
|
238
|
+
URI += '/' + bucket
|
|
239
|
+
if key:
|
|
240
|
+
URI += '/' + key
|
|
241
|
+
if not URI:
|
|
242
|
+
URI = '/'
|
|
243
|
+
return util.encode_object_key(URI)
|
|
244
|
+
|
|
245
|
+
@staticmethod
|
|
246
|
+
def getCanonicalQueryString(args_path):
|
|
247
|
+
canonMap = {}
|
|
248
|
+
for key, value in args_path.items():
|
|
249
|
+
canonMap[key] = value
|
|
250
|
+
cannoList = sorted(canonMap.items(), key=lambda d: d[0])
|
|
251
|
+
queryStr = ''
|
|
252
|
+
i = 0
|
|
253
|
+
for val in cannoList:
|
|
254
|
+
if i != 0:
|
|
255
|
+
queryStr += '&'
|
|
256
|
+
queryStr += '%s=%s' % (util.encode_item(val[0], '/'), util.encode_item(val[1], ''))
|
|
257
|
+
i = 1
|
|
258
|
+
return queryStr
|
|
259
|
+
|
|
260
|
+
@staticmethod
|
|
261
|
+
def getCanonicalHeaders(headMap):
|
|
262
|
+
headList = sorted(headMap.items(), key=lambda d: d[0])
|
|
263
|
+
canonicalHeaderStr = ''
|
|
264
|
+
for val in headList:
|
|
265
|
+
if isinstance(val[1], list):
|
|
266
|
+
tlist = sorted(val[1])
|
|
267
|
+
for v in tlist:
|
|
268
|
+
canonicalHeaderStr += val[0] + ':' + v + '\n'
|
|
269
|
+
else:
|
|
270
|
+
canonicalHeaderStr += val[0] + ':' + str(val[1]) + '\n'
|
|
271
|
+
return canonicalHeaderStr
|
|
272
|
+
|
|
273
|
+
@staticmethod
|
|
274
|
+
def setMapKeyLower(inputMap):
|
|
275
|
+
outputMap = {}
|
|
276
|
+
for key in inputMap.keys():
|
|
277
|
+
outputMap[key.lower()] = inputMap[key]
|
|
278
|
+
return outputMap
|