oscar-python 1.2.1__tar.gz → 1.3.1__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.
Files changed (34) hide show
  1. {oscar_python-1.2.1 → oscar_python-1.3.1}/PKG-INFO +44 -3
  2. {oscar_python-1.2.1 → oscar_python-1.3.1}/README.md +40 -2
  3. oscar_python-1.3.1/oscar_python/_oidc.py +96 -0
  4. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python/_providers/_minio.py +4 -3
  5. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python/_providers/_onedata.py +16 -13
  6. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python/_providers/_providers_base.py +4 -3
  7. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python/_providers/_s3.py +15 -13
  8. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python/_providers/_webdav.py +7 -7
  9. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python/_utils.py +27 -21
  10. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python/client.py +55 -34
  11. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python/client_anon.py +3 -2
  12. oscar_python-1.3.1/oscar_python/default_client.py +39 -0
  13. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python/local_test.py +1 -1
  14. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python/storage.py +34 -18
  15. oscar_python-1.3.1/oscar_python/test_v2.py +206 -0
  16. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python.egg-info/PKG-INFO +44 -3
  17. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python.egg-info/SOURCES.txt +11 -1
  18. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python.egg-info/requires.txt +5 -4
  19. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python.egg-info/top_level.txt +3 -0
  20. {oscar_python-1.2.1 → oscar_python-1.3.1}/setup.py +3 -2
  21. oscar_python-1.3.1/tests/test_client.py +138 -0
  22. oscar_python-1.3.1/tests/test_default_client.py +61 -0
  23. oscar_python-1.3.1/tests/test_oidc.py +38 -0
  24. oscar_python-1.3.1/tests/test_onedata.py +66 -0
  25. oscar_python-1.3.1/tests/test_s3.py +48 -0
  26. oscar_python-1.3.1/tests/test_storage.py +51 -0
  27. oscar_python-1.3.1/tests/test_utils.py +96 -0
  28. oscar_python-1.3.1/tests/test_webdav.py +50 -0
  29. oscar_python-1.2.1/oscar_python/default_client.py +0 -35
  30. {oscar_python-1.2.1 → oscar_python-1.3.1}/LICENSE +0 -0
  31. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python/__init__.py +0 -0
  32. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python.egg-info/dependency_links.txt +0 -0
  33. {oscar_python-1.2.1 → oscar_python-1.3.1}/oscar_python.egg-info/not-zip-safe +0 -0
  34. {oscar_python-1.2.1 → oscar_python-1.3.1}/setup.cfg +0 -0
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: oscar_python
3
- Version: 1.2.1
3
+ Version: 1.3.1
4
4
  Summary: OSCAR API for python
5
5
  Home-page: https://github.com/grycap/oscar_python
6
6
  Author: GRyCAP - Universitat Politecnica de Valencia
7
7
  Author-email: calarcon@i3m.upv.es
8
8
  License: Apache 2.0
9
+ Platform: UNKNOWN
9
10
  Classifier: Programming Language :: Python :: 3
10
11
  Classifier: License :: OSI Approved :: Apache Software License
11
12
  Description-Content-Type: text/markdown
@@ -13,7 +14,7 @@ License-File: LICENSE
13
14
 
14
15
  ## Python OSCAR client
15
16
 
16
- [![Build](https://github.com/grycap/oscar_python/actions/workflows/main.yaml/badge.svg)](https://github.com/grycap/oscar_python/actions/workflows/main.yaml)
17
+ [![Build](https://github.com/grycap/oscar_python/actions/workflows/release.yaml/badge.svg)](https://github.com/grycap/oscar_python/actions/workflows/main.yaml)
17
18
  ![PyPI](https://img.shields.io/pypi/v/oscar_python)
18
19
 
19
20
  This package provides a client to interact with OSCAR (https://oscar.grycap.net) clusters and services. It is available on Pypi with the name [oscar-python](https://pypi.org/project/oscar-python/).
@@ -68,6 +69,33 @@ If you already have a valid token, you can use the parameter `oidc_token` instea
68
69
  ```
69
70
  An example of using a generated token is if you want to use EGI Notebooks. Since you can't use oidc-agent on the Notebook, you can make use of the generated token that EGI provides on path `/var/run/secrets/egi.eu/access_token`.
70
71
 
72
+ If you have a valid refresh token (long live token), you can use the parameter `refresh_token` instead.
73
+
74
+ ``` python
75
+ options_oidc_auth = {'cluster_id':'cluster-id',
76
+ 'endpoint':'https://cluster-endpoint',
77
+ 'refresh_token':'token',
78
+ 'ssl':'True'}
79
+
80
+ client = Client(options = options_oidc_auth)
81
+ ```
82
+
83
+ You can get a refresh token from EGI Check-In using the [Token Portal](https://aai.egi.eu/token).
84
+
85
+ In case of using other OIDC provider you must provide two additional parameters `token_endpoint`
86
+ and `scopes`:
87
+
88
+ ``` python
89
+ options_oidc_auth = {'cluster_id':'cluster-id',
90
+ 'endpoint':'https://cluster-endpoint',
91
+ 'refresh_token':'token',
92
+ 'scopes': ["openid", "profile", "email"],
93
+ 'token_endpoint': "http://issuer.com/token",
94
+ 'ssl':'True'}
95
+
96
+ client = Client(options = options_oidc_auth)
97
+ ```
98
+
71
99
  ### Sample usage
72
100
 
73
101
  - Sample code that creates a client and gets information about the cluster
@@ -170,6 +198,9 @@ response = client.remove_service("service_name") # returns an http response
170
198
  ``` python
171
199
  # make a synchronous execution
172
200
  response = client.run_service("service_name", input="input", output="out.png", timeout=100) # returns an http response
201
+
202
+ # make an asynchronous execution
203
+ response = client.run_service("service_name", input="input", async_call=True) # returns an http response
173
204
  ```
174
205
 
175
206
  #### Logs methods
@@ -200,11 +231,19 @@ response = client.remove_all_jobs("service_name") # returns an http response
200
231
 
201
232
  #### Storage usage
202
233
 
203
- You can create a storage object to operate over the different storage providers defined on a service with the method `create_storage_client` as follows:
234
+ You can create a storage object to operate over the different storage providers defined on a service with the method `create_storage_client`. This constructor returns a storage object with methos to interact with the storage providers.
235
+
236
+ The default constructor, seen as follows, will create a provider to interact with the default MinIO instance through the user's credentials.
237
+
238
+ ``` python
239
+ storage_service = client.create_storage_client() # returns a storage object
240
+ ```
241
+ Additionally, if you need to interact with specific storage providers defined on a service, the constructor accepts a `svc` parameter where you can state the service name from which to search for additional credentials.
204
242
 
205
243
  ``` python
206
244
  storage_service = client.create_storage_client("service_name") # returns a storage object
207
245
  ```
246
+
208
247
  > _Note_ : The `storage_provider` parameter on the storage methods follows the format: `["storage_provider_type"].["storage_provider_name"]` where `storage_provider_type` is one of the suported storage providers (minIO, S3, Onedata or webdav) and `storage_provider_name` is the identifier _(ex: minio.default)_
209
248
 
210
249
  **list_files_from_path**
@@ -227,3 +266,5 @@ response = storage_service.upload_file("storage_provider", "local_path", "remote
227
266
  # download a file from a remote path to a local path
228
267
  response = storage_service.download_file("storage_provider", "local_path", "remote_path")
229
268
  ```
269
+
270
+
@@ -1,6 +1,6 @@
1
1
  ## Python OSCAR client
2
2
 
3
- [![Build](https://github.com/grycap/oscar_python/actions/workflows/main.yaml/badge.svg)](https://github.com/grycap/oscar_python/actions/workflows/main.yaml)
3
+ [![Build](https://github.com/grycap/oscar_python/actions/workflows/release.yaml/badge.svg)](https://github.com/grycap/oscar_python/actions/workflows/main.yaml)
4
4
  ![PyPI](https://img.shields.io/pypi/v/oscar_python)
5
5
 
6
6
  This package provides a client to interact with OSCAR (https://oscar.grycap.net) clusters and services. It is available on Pypi with the name [oscar-python](https://pypi.org/project/oscar-python/).
@@ -55,6 +55,33 @@ If you already have a valid token, you can use the parameter `oidc_token` instea
55
55
  ```
56
56
  An example of using a generated token is if you want to use EGI Notebooks. Since you can't use oidc-agent on the Notebook, you can make use of the generated token that EGI provides on path `/var/run/secrets/egi.eu/access_token`.
57
57
 
58
+ If you have a valid refresh token (long live token), you can use the parameter `refresh_token` instead.
59
+
60
+ ``` python
61
+ options_oidc_auth = {'cluster_id':'cluster-id',
62
+ 'endpoint':'https://cluster-endpoint',
63
+ 'refresh_token':'token',
64
+ 'ssl':'True'}
65
+
66
+ client = Client(options = options_oidc_auth)
67
+ ```
68
+
69
+ You can get a refresh token from EGI Check-In using the [Token Portal](https://aai.egi.eu/token).
70
+
71
+ In case of using other OIDC provider you must provide two additional parameters `token_endpoint`
72
+ and `scopes`:
73
+
74
+ ``` python
75
+ options_oidc_auth = {'cluster_id':'cluster-id',
76
+ 'endpoint':'https://cluster-endpoint',
77
+ 'refresh_token':'token',
78
+ 'scopes': ["openid", "profile", "email"],
79
+ 'token_endpoint': "http://issuer.com/token",
80
+ 'ssl':'True'}
81
+
82
+ client = Client(options = options_oidc_auth)
83
+ ```
84
+
58
85
  ### Sample usage
59
86
 
60
87
  - Sample code that creates a client and gets information about the cluster
@@ -157,6 +184,9 @@ response = client.remove_service("service_name") # returns an http response
157
184
  ``` python
158
185
  # make a synchronous execution
159
186
  response = client.run_service("service_name", input="input", output="out.png", timeout=100) # returns an http response
187
+
188
+ # make an asynchronous execution
189
+ response = client.run_service("service_name", input="input", async_call=True) # returns an http response
160
190
  ```
161
191
 
162
192
  #### Logs methods
@@ -187,11 +217,19 @@ response = client.remove_all_jobs("service_name") # returns an http response
187
217
 
188
218
  #### Storage usage
189
219
 
190
- You can create a storage object to operate over the different storage providers defined on a service with the method `create_storage_client` as follows:
220
+ You can create a storage object to operate over the different storage providers defined on a service with the method `create_storage_client`. This constructor returns a storage object with methos to interact with the storage providers.
221
+
222
+ The default constructor, seen as follows, will create a provider to interact with the default MinIO instance through the user's credentials.
223
+
224
+ ``` python
225
+ storage_service = client.create_storage_client() # returns a storage object
226
+ ```
227
+ Additionally, if you need to interact with specific storage providers defined on a service, the constructor accepts a `svc` parameter where you can state the service name from which to search for additional credentials.
191
228
 
192
229
  ``` python
193
230
  storage_service = client.create_storage_client("service_name") # returns a storage object
194
231
  ```
232
+
195
233
  > _Note_ : The `storage_provider` parameter on the storage methods follows the format: `["storage_provider_type"].["storage_provider_name"]` where `storage_provider_type` is one of the suported storage providers (minIO, S3, Onedata or webdav) and `storage_provider_name` is the identifier _(ex: minio.default)_
196
234
 
197
235
  **list_files_from_path**
@@ -0,0 +1,96 @@
1
+ """
2
+ Class to manage OIDC JWT tokens
3
+ """
4
+ import json
5
+ import base64
6
+ import re
7
+ import time
8
+ import requests
9
+
10
+
11
+ class OIDC(object):
12
+
13
+ @staticmethod
14
+ def _b64d(b):
15
+ """Decode some base64-encoded bytes.
16
+
17
+ Raises Exception if the string contains invalid characters or padding.
18
+
19
+ :param b: bytes
20
+ """
21
+
22
+ cb = b.rstrip(b"=") # shouldn't but there you are
23
+
24
+ # Python's base64 functions ignore invalid characters, so we need to
25
+ # check for them explicitly.
26
+ b64_re = re.compile(b"^[A-Za-z0-9_-]*$")
27
+ if not b64_re.match(cb):
28
+ raise Exception(cb, "base64-encoded data contains illegal characters")
29
+
30
+ if cb == b:
31
+ b = OIDC._add_padding(b)
32
+
33
+ return base64.urlsafe_b64decode(b)
34
+
35
+ @staticmethod
36
+ def _add_padding(b):
37
+ # add padding chars
38
+ m = len(b) % 4
39
+ if m == 1:
40
+ # NOTE: for some reason b64decode raises *TypeError* if the
41
+ # padding is incorrect.
42
+ raise Exception(b, "incorrect padding")
43
+ elif m == 2:
44
+ b += b"=="
45
+ elif m == 3:
46
+ b += b"="
47
+ return b
48
+
49
+ @staticmethod
50
+ def get_info(token):
51
+ """
52
+ Unpacks a JWT into its parts and base64 decodes the parts
53
+ individually, returning the part 1 json decoded, where the
54
+ token info is stored.
55
+
56
+ :param token: The JWT token
57
+ """
58
+ part = tuple(token.encode("utf-8").split(b"."))
59
+ part = [OIDC._b64d(p) for p in part]
60
+ return json.loads(part[1].decode("utf-8"))
61
+
62
+ @staticmethod
63
+ def is_access_token_expired(token):
64
+ """
65
+ Check if the current access token is expired
66
+ """
67
+ try:
68
+ decoded_token = OIDC.get_info(token)
69
+ now = int(time.time())
70
+ expires = int(decoded_token['exp'])
71
+ validity = expires - now
72
+ if validity < 0:
73
+ return True
74
+ else:
75
+ return False
76
+ except Exception:
77
+ return True
78
+
79
+ @staticmethod
80
+ def refresh_access_token(refresh_token, scopes, token_endpoint):
81
+ """
82
+ Refresh the access token using the refresh token
83
+ """
84
+ data = {
85
+ 'grant_type': 'refresh_token',
86
+ 'refresh_token': refresh_token,
87
+ 'client_id': 'token-portal',
88
+ 'scope': ' '.join(scopes)
89
+ }
90
+
91
+ response = requests.post(token_endpoint, data=data)
92
+
93
+ if response.status_code == 200:
94
+ return response.json()['access_token']
95
+ else:
96
+ return None
@@ -10,15 +10,16 @@
10
10
  # distributed under the License is distributed on an "AS IS" BASIS,
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
- # limitations under the License.
13
+ # limitations under the License.
14
14
 
15
15
  import boto3
16
16
  from oscar_python._providers._s3 import S3
17
17
 
18
18
  _DEFAULT_MINIO_ENDPOINT = 'http://minio-service.minio:9000'
19
19
 
20
+
20
21
  class Minio(S3):
21
-
22
+
22
23
  def _get_client(self, c):
23
24
  """Return Minio client with user configuration."""
24
25
  if c["endpoint"] == '':
@@ -30,4 +31,4 @@ class Minio(S3):
30
31
  region_name=c["region"],
31
32
  verify=c["verify"],
32
33
  aws_access_key_id=c["access_key"],
33
- aws_secret_access_key=c["secret_key"])
34
+ aws_secret_access_key=c["secret_key"])
@@ -10,29 +10,30 @@
10
10
  # distributed under the License is distributed on an "AS IS" BASIS,
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
- # limitations under the License.
13
+ # limitations under the License.
14
14
 
15
15
  from oscar_python._providers._providers_base import StorageProvider
16
16
  import requests
17
17
  import json
18
18
 
19
- #TODO fix error returns
19
+
20
+ # TODO fix error returns
20
21
  class Onedata(StorageProvider):
21
-
22
+
22
23
  _CDMI_PATH = '/cdmi'
23
24
  _CDMI_VERSION_HEADER = {'X-CDMI-Specification-Version': '1.1.1'}
24
25
 
25
26
  def __init__(self, credentials) -> None:
26
27
  super().__init__()
27
28
  self._get_client(credentials)
28
-
29
+
29
30
  def _get_client(self, c):
30
31
  self.oneprovider_space = c["space"]
31
32
  self.oneprovider_host = c["oneprovider_host"]
32
33
  self.headers = {'X-Auth-Token': c["token"]}
33
34
  self.url = (f'https://{self.oneprovider_host}{self._CDMI_PATH}/'
34
35
  f'{self.oneprovider_space}/')
35
-
36
+
36
37
  def upload_file(self, local_path, remote_path):
37
38
  file_name = local_path.split('/')[-1]
38
39
  if not self._folder_exists(remote_path):
@@ -57,12 +58,13 @@ class Onedata(StorageProvider):
57
58
  file_name = remote_path.split('/')[-1]
58
59
  url = self.url+remote_path
59
60
  res = requests.get(url=url, headers=self.headers)
60
- if res.status_code == 200: print("Saving file to {0}/{1}".format(local_path, file_name))
61
+ if res.status_code == 200:
62
+ print("Saving file to {0}/{1}".format(local_path, file_name))
61
63
  self._save_file(local_path+"/"+file_name, res.content, mode='wb')
62
-
64
+
63
65
  def list_files_from_path(self, path):
64
66
  headers = {**self._CDMI_VERSION_HEADER, **self.headers}
65
- return requests.get(url = self.url+path+"/", headers=headers)
67
+ return requests.get(url=self.url+path+"/", headers=headers)
66
68
 
67
69
  def _save_file(self, path, content, mode='w'):
68
70
  with open(path, mode) as fwc:
@@ -72,12 +74,13 @@ class Onedata(StorageProvider):
72
74
 
73
75
  def _folder_exists(self, folder_name):
74
76
  headers = {**self._CDMI_VERSION_HEADER, **self.headers}
75
- response = requests.get(url = self.url+folder_name+"/", headers=headers)
77
+ response = requests.get(url=self.url+folder_name+"/", headers=headers)
76
78
  if response.status_code == 200:
77
79
  return True
78
80
  return False
79
-
81
+
80
82
  def _create_folder(self, folder_name):
81
- response = requests.put(url = self.url+folder_name+"/", headers=self.headers)
82
- if response.status_code != 201:
83
- return False
83
+ response = requests.put(url=self.url+folder_name+"/", headers=self.headers)
84
+ if response.status_code == 201:
85
+ return True
86
+ return False
@@ -10,10 +10,11 @@
10
10
  # distributed under the License is distributed on an "AS IS" BASIS,
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
- # limitations under the License.
13
+ # limitations under the License.
14
14
 
15
15
  import abc
16
16
 
17
+
17
18
  class StorageProvider(abc.ABC):
18
19
  @abc.abstractmethod
19
20
  def download_file(self, local_path, remote_path):
@@ -22,11 +23,11 @@ class StorageProvider(abc.ABC):
22
23
  @abc.abstractmethod
23
24
  def upload_file(self, local_path, remote_path):
24
25
  """Generic method to be implemented by all the storage providers."""
25
-
26
+
26
27
  @abc.abstractmethod
27
28
  def list_files_from_path(self, path):
28
29
  """Generic method to be implemented by all the storage providers."""
29
30
 
30
31
  @abc.abstractmethod
31
32
  def _get_client(self):
32
- """Generic method to be implemented by all the storage providers."""
33
+ """Generic method to be implemented by all the storage providers."""
@@ -10,19 +10,19 @@
10
10
  # distributed under the License is distributed on an "AS IS" BASIS,
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
- # limitations under the License.
13
+ # limitations under the License.
14
14
 
15
15
  from aiohttp import ClientError
16
- import logging
17
16
  import boto3
18
17
  from oscar_python._providers._providers_base import StorageProvider
19
18
 
19
+
20
20
  class S3(StorageProvider):
21
21
  def __init__(self, credentials) -> None:
22
22
  super().__init__()
23
23
  self.client = self._get_client(credentials)
24
24
  self.resource = self._get_resource(credentials)
25
-
25
+
26
26
  def _get_client(self, c):
27
27
  """Returns S3 client with default configuration."""
28
28
  if c is None:
@@ -42,39 +42,41 @@ class S3(StorageProvider):
42
42
  if region == '':
43
43
  region = None
44
44
  return boto3.resource('s3',
45
- region_name=region,
46
- aws_access_key_id=c["access_key"],
47
- aws_secret_access_key=c["secret_key"])
45
+ region_name=region,
46
+ aws_access_key_id=c["access_key"],
47
+ aws_secret_access_key=c["secret_key"])
48
48
 
49
49
  def list_files_from_path(self, path):
50
50
  prefix = ""
51
51
  path_split = path.split('/')
52
52
  bucket = path_split[0]
53
53
  if len(path_split) > 1:
54
- prefix = path.split('/',2)[1]
55
- print("Reading content from path:",path)
54
+ prefix = path.split('/', 2)[1]
55
+ print("Reading content from path:", path)
56
56
  return self.client.list_objects(Bucket=bucket, Prefix=prefix)
57
-
57
+
58
58
  def upload_file(self, local_path, remote_path):
59
59
  bucket_name = remote_path.split('/')[0]
60
- file_key = remote_path.split('/',1)[1]
60
+ file_key = remote_path.split('/', 1)[1]
61
61
  file_name = local_path.split('/')[-1]
62
62
  print("Uploading to bucket '{0}' with key '{1}'".format(bucket_name,file_key))
63
63
  with open(local_path, 'rb') as data:
64
64
  try:
65
- self.client.upload_fileobj(data, bucket_name, file_key+"/"+file_name)
65
+ self.client.upload_fileobj(data, bucket_name, file_key + "/" + file_name)
66
+ return True
66
67
  except ClientError as err:
67
68
  print("Error uploading file: ", err)
68
69
  return False
69
70
 
70
71
  def download_file(self, local_path, remote_path):
71
72
  bucket_name = remote_path.split('/')[0]
72
- file_key = remote_path.split('/',1)[1]
73
+ file_key = remote_path.split('/', 1)[1]
73
74
  file_path = local_path+"/"+remote_path.split('/')[-1]
74
75
  print("Downloading from bucket '{0}' to path '{1}' with key '{2}'".format(bucket_name, file_path, file_key))
75
76
  with open(file_path, 'wb') as data:
76
77
  try:
77
78
  self.client.download_fileobj(bucket_name, file_key, data)
79
+ return True
78
80
  except ClientError as err:
79
81
  print("Error downloading file: ", err)
80
- return False
82
+ return False
@@ -10,12 +10,13 @@
10
10
  # distributed under the License is distributed on an "AS IS" BASIS,
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
- # limitations under the License.
13
+ # limitations under the License.
14
14
 
15
15
  from oscar_python._providers._providers_base import StorageProvider
16
16
  from webdav3.client import Client
17
17
  from webdav3.exceptions import WebDavException
18
18
 
19
+
19
20
  class WebDav(StorageProvider):
20
21
  def __init__(self, credentials) -> None:
21
22
  self.client = self._get_client(credentials)
@@ -26,9 +27,9 @@ class WebDav(StorageProvider):
26
27
  if 'https://' not in hostname:
27
28
  hostname = 'https://'+hostname
28
29
  options = {
29
- 'webdav_hostname': hostname,
30
- 'webdav_login': c["login"],
31
- 'webdav_password': c["password"]
30
+ 'webdav_hostname': hostname,
31
+ 'webdav_login': c["login"],
32
+ 'webdav_password': c["password"]
32
33
  }
33
34
  return Client(options=options)
34
35
 
@@ -45,13 +46,12 @@ class WebDav(StorageProvider):
45
46
  except WebDavException as exception:
46
47
  print("error uploading file to path '{0}': {1}".format(remote_path+"/"+file_name, exception))
47
48
 
48
-
49
49
  def download_file(self, local_path, remote_path):
50
50
  file_name = remote_path.split('/')[-1]
51
51
  try:
52
52
  self.client.download_sync(remote_path, local_path+"/"+file_name)
53
53
  except WebDavException as exception:
54
54
  print("error downloading file from path '{0}': {1}".format(remote_path, exception))
55
-
55
+
56
56
  def list_files_from_path(self, path):
57
- return self.client.list(path)
57
+ return self.client.list(path)
@@ -10,7 +10,7 @@
10
10
  # distributed under the License is distributed on an "AS IS" BASIS,
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
- # limitations under the License.
13
+ # limitations under the License.
14
14
 
15
15
  import base64
16
16
  import os
@@ -18,18 +18,19 @@ import requests
18
18
  import liboidcagent as agent
19
19
  _DEFAULT_TIMEOUT = 60
20
20
 
21
- """ Generic http request """
22
- def make_request(c , path, method, **kwargs):
23
21
 
24
- headers = get_headers(c)
22
+ def make_request(c, path, method, **kwargs):
23
+ """ Generic http request """
25
24
 
26
- if "timeout" in kwargs.keys() and kwargs["timeout"]:
25
+ headers = get_headers(c)
26
+
27
+ if "timeout" in kwargs.keys() and kwargs["timeout"]:
27
28
  timeout = kwargs["timeout"]
28
- else:
29
+ else:
29
30
  timeout = _DEFAULT_TIMEOUT
30
31
 
31
32
  url = c.endpoint+path
32
-
33
+
33
34
  if method in ["post", "put"]:
34
35
  if "token" in kwargs.keys() and kwargs["token"]:
35
36
  headers = get_headers_with_token(kwargs["token"])
@@ -40,37 +41,42 @@ def make_request(c , path, method, **kwargs):
40
41
 
41
42
  if "handle" in kwargs.keys() and kwargs["handle"] == False:
42
43
  return result
43
-
44
+
44
45
  result.raise_for_status()
45
46
  return result
46
47
 
47
- """ Function to generate headers with basic authentication or OIDC """
48
+
48
49
  def get_headers(c):
50
+ """ Function to generate headers with basic authentication or OIDC """
49
51
  if c._AUTH_TYPE == "basicauth":
50
- usr_pass_as_bytes = bytes(c.user+":"+c.password,"utf-8")
52
+ usr_pass_as_bytes = bytes(c.user + ":" + c.password, "utf-8")
51
53
  usr_pass_base_64 = base64.b64encode(usr_pass_as_bytes).decode("utf-8")
52
- return {"Authorization": "Basic "+ usr_pass_base_64}
54
+ return {"Authorization": "Basic " + usr_pass_base_64}
53
55
  if c._AUTH_TYPE == "oidc-agent":
54
56
  token = agent.get_access_token(c.shortname)
55
57
  return get_headers_with_token(token)
56
58
  if c._AUTH_TYPE == "oidc":
57
- return get_headers_with_token(c.oidc_token)
59
+ return get_headers_with_token(c.get_access_token())
60
+
58
61
 
59
- """ Function to generate headers with token auth """
60
62
  def get_headers_with_token(token):
61
- return {"Authorization": "Bearer "+ str(token)}
63
+ """ Function to generate headers with token auth """
64
+ return {"Authorization": "Bearer " + str(token)}
65
+
62
66
 
63
67
  def write_text_file(content, file_path):
64
68
  with open(file_path, 'w') as f:
65
69
  f.write(content)
66
70
 
71
+
67
72
  def isBase64(st):
68
73
  try:
69
74
  base64.b64decode(st)
70
75
  return True
71
- except:
76
+ except Exception:
72
77
  return False
73
78
 
79
+
74
80
  def decode_b64(b64_str, file_out):
75
81
  file_extension = os.path.splitext(file_out)[1]
76
82
  try:
@@ -80,7 +86,7 @@ def decode_b64(b64_str, file_out):
80
86
  decode = 'w'
81
87
  decoded_data = decoded_data.decode("utf-8")
82
88
  else:
83
- decode = 'wb'
89
+ decode = 'wb'
84
90
 
85
91
  with open(file_out, decode) as f:
86
92
  f.write(decoded_data)
@@ -90,6 +96,7 @@ def decode_b64(b64_str, file_out):
90
96
  except OSError:
91
97
  print('Error decoding output: Failed to write decoded data to file.')
92
98
 
99
+
93
100
  def encode_input(data):
94
101
  if os.path.isfile(data):
95
102
  try:
@@ -103,12 +110,11 @@ def encode_input(data):
103
110
  message_bytes = data.encode('ascii')
104
111
  return base64.b64encode(message_bytes)
105
112
 
113
+
106
114
  def decode_output(output, file_path):
107
- if(isBase64(output)):
115
+ if isBase64(output):
108
116
  decode_b64(output, file_path)
109
117
  return
110
- if(isinstance(output,str)):
111
- write_text_file(output,file_path)
118
+ if isinstance(output, str):
119
+ write_text_file(output, file_path)
112
120
  return
113
-
114
-