oscar-python 1.3.0__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.
- {oscar_python-1.3.0 → oscar_python-1.3.1}/PKG-INFO +42 -20
- {oscar_python-1.3.0 → oscar_python-1.3.1}/README.md +37 -2
- oscar_python-1.3.1/oscar_python/_oidc.py +96 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python/_utils.py +1 -1
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python/client.py +28 -13
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python/storage.py +21 -5
- oscar_python-1.3.1/oscar_python/test_v2.py +206 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python.egg-info/PKG-INFO +43 -21
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python.egg-info/SOURCES.txt +3 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python.egg-info/top_level.txt +2 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/tests/test_client.py +9 -0
- oscar_python-1.3.1/tests/test_oidc.py +38 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/tests/test_storage.py +2 -2
- {oscar_python-1.3.0 → oscar_python-1.3.1}/tests/test_utils.py +3 -1
- {oscar_python-1.3.0 → oscar_python-1.3.1}/LICENSE +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python/__init__.py +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python/_providers/_minio.py +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python/_providers/_onedata.py +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python/_providers/_providers_base.py +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python/_providers/_s3.py +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python/_providers/_webdav.py +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python/client_anon.py +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python/default_client.py +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python/local_test.py +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python.egg-info/dependency_links.txt +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python.egg-info/not-zip-safe +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/oscar_python.egg-info/requires.txt +5 -5
- {oscar_python-1.3.0 → oscar_python-1.3.1}/setup.cfg +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/setup.py +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/tests/test_default_client.py +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/tests/test_onedata.py +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/tests/test_s3.py +0 -0
- {oscar_python-1.3.0 → oscar_python-1.3.1}/tests/test_webdav.py +0 -0
|
@@ -1,35 +1,20 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: oscar_python
|
|
3
|
-
Version: 1.3.
|
|
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
|
|
12
13
|
License-File: LICENSE
|
|
13
|
-
Requires-Dist: webdavclient3==3.14.6
|
|
14
|
-
Requires-Dist: requests
|
|
15
|
-
Requires-Dist: boto3
|
|
16
|
-
Requires-Dist: setuptools>=40.8.0
|
|
17
|
-
Requires-Dist: pyyaml
|
|
18
|
-
Requires-Dist: aiohttp
|
|
19
|
-
Requires-Dist: liboidcagent
|
|
20
|
-
Dynamic: author
|
|
21
|
-
Dynamic: author-email
|
|
22
|
-
Dynamic: classifier
|
|
23
|
-
Dynamic: description
|
|
24
|
-
Dynamic: description-content-type
|
|
25
|
-
Dynamic: home-page
|
|
26
|
-
Dynamic: license
|
|
27
|
-
Dynamic: requires-dist
|
|
28
|
-
Dynamic: summary
|
|
29
14
|
|
|
30
15
|
## Python OSCAR client
|
|
31
16
|
|
|
32
|
-
[](https://github.com/grycap/oscar_python/actions/workflows/main.yaml)
|
|
33
18
|

|
|
34
19
|
|
|
35
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/).
|
|
@@ -84,6 +69,33 @@ If you already have a valid token, you can use the parameter `oidc_token` instea
|
|
|
84
69
|
```
|
|
85
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`.
|
|
86
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
|
+
|
|
87
99
|
### Sample usage
|
|
88
100
|
|
|
89
101
|
- Sample code that creates a client and gets information about the cluster
|
|
@@ -219,11 +231,19 @@ response = client.remove_all_jobs("service_name") # returns an http response
|
|
|
219
231
|
|
|
220
232
|
#### Storage usage
|
|
221
233
|
|
|
222
|
-
You can create a storage object to operate over the different storage providers defined on a service with the method `create_storage_client
|
|
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.
|
|
223
242
|
|
|
224
243
|
``` python
|
|
225
244
|
storage_service = client.create_storage_client("service_name") # returns a storage object
|
|
226
245
|
```
|
|
246
|
+
|
|
227
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)_
|
|
228
248
|
|
|
229
249
|
**list_files_from_path**
|
|
@@ -246,3 +266,5 @@ response = storage_service.upload_file("storage_provider", "local_path", "remote
|
|
|
246
266
|
# download a file from a remote path to a local path
|
|
247
267
|
response = storage_service.download_file("storage_provider", "local_path", "remote_path")
|
|
248
268
|
```
|
|
269
|
+
|
|
270
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## Python OSCAR client
|
|
2
2
|
|
|
3
|
-
[](https://github.com/grycap/oscar_python/actions/workflows/main.yaml)
|
|
4
4
|

|
|
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
|
|
@@ -190,11 +217,19 @@ response = client.remove_all_jobs("service_name") # returns an http response
|
|
|
190
217
|
|
|
191
218
|
#### Storage usage
|
|
192
219
|
|
|
193
|
-
You can create a storage object to operate over the different storage providers defined on a service with the method `create_storage_client
|
|
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.
|
|
194
228
|
|
|
195
229
|
``` python
|
|
196
230
|
storage_service = client.create_storage_client("service_name") # returns a storage object
|
|
197
231
|
```
|
|
232
|
+
|
|
198
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)_
|
|
199
234
|
|
|
200
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
|
|
@@ -56,7 +56,7 @@ def get_headers(c):
|
|
|
56
56
|
token = agent.get_access_token(c.shortname)
|
|
57
57
|
return get_headers_with_token(token)
|
|
58
58
|
if c._AUTH_TYPE == "oidc":
|
|
59
|
-
return get_headers_with_token(c.
|
|
59
|
+
return get_headers_with_token(c.get_access_token())
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
def get_headers_with_token(token):
|
|
@@ -18,6 +18,7 @@ import os
|
|
|
18
18
|
import yaml
|
|
19
19
|
import liboidcagent as agent
|
|
20
20
|
import oscar_python._utils as utils
|
|
21
|
+
from oscar_python._oidc import OIDC
|
|
21
22
|
from oscar_python.default_client import DefaultClient
|
|
22
23
|
from oscar_python.storage import Storage
|
|
23
24
|
|
|
@@ -37,10 +38,16 @@ _POST = "post"
|
|
|
37
38
|
_PUT = "put"
|
|
38
39
|
_DELETE = "delete"
|
|
39
40
|
|
|
41
|
+
# Default values for OIDC refresh token using EGI CheckIn
|
|
42
|
+
_DEFAULT_SCOPES = ['openid', 'email', 'profile', 'voperson_id', 'eduperson_entitlement']
|
|
43
|
+
_DEFAULT_TOKEN_ENDPOINT = 'https://aai.egi.eu/auth/realms/egi/protocol/openid-connect/token'
|
|
44
|
+
|
|
40
45
|
|
|
41
46
|
class Client(DefaultClient):
|
|
42
47
|
# Cluster info
|
|
43
48
|
def __init__(self, options) -> None:
|
|
49
|
+
self.id = options['cluster_id']
|
|
50
|
+
self.endpoint = options['endpoint']
|
|
44
51
|
self.set_auth_type(options)
|
|
45
52
|
if self._AUTH_TYPE == 'basicauth':
|
|
46
53
|
self.basic_auth_client(options)
|
|
@@ -50,22 +57,20 @@ class Client(DefaultClient):
|
|
|
50
57
|
self.oidc_client(options)
|
|
51
58
|
|
|
52
59
|
def basic_auth_client(self, options):
|
|
53
|
-
self.id = options['cluster_id']
|
|
54
|
-
self.endpoint = options['endpoint']
|
|
55
60
|
self.user = options['user']
|
|
56
61
|
self.password = options['password']
|
|
57
62
|
self.ssl = bool(options['ssl'])
|
|
58
63
|
|
|
59
64
|
def oidc_agent_client(self, options):
|
|
60
|
-
self.id = options['cluster_id']
|
|
61
|
-
self.endpoint = options['endpoint']
|
|
62
65
|
self.shortname = options['shortname']
|
|
63
66
|
self.ssl = bool(options['ssl'])
|
|
64
67
|
|
|
65
68
|
def oidc_client(self, options):
|
|
66
|
-
self.
|
|
67
|
-
self.
|
|
68
|
-
self.
|
|
69
|
+
self.oidc_token = options.get('oidc_token')
|
|
70
|
+
self.refresh_token = options.get('refresh_token')
|
|
71
|
+
self.scopes = options.get('scopes', _DEFAULT_SCOPES)
|
|
72
|
+
self.token_endpoint = options.get('token_endpoint',
|
|
73
|
+
_DEFAULT_TOKEN_ENDPOINT)
|
|
69
74
|
self.ssl = bool(options['ssl'])
|
|
70
75
|
|
|
71
76
|
def set_auth_type(self, options):
|
|
@@ -77,18 +82,28 @@ class Client(DefaultClient):
|
|
|
77
82
|
agent.get_access_token(options['shortname'])
|
|
78
83
|
except agent.OidcAgentError as e:
|
|
79
84
|
print("ERROR oidc-agent: {}".format(e))
|
|
80
|
-
elif 'oidc_token' in options:
|
|
85
|
+
elif 'oidc_token' in options or 'refresh_token' in options:
|
|
81
86
|
self._AUTH_TYPE = "oidc"
|
|
82
87
|
else:
|
|
83
88
|
raise ValueError("Unrecognized authentication credentials in options")
|
|
84
89
|
|
|
90
|
+
def get_access_token(self):
|
|
91
|
+
if self.refresh_token and OIDC.is_access_token_expired(self.oidc_token):
|
|
92
|
+
self.oidc_token = OIDC.refresh_access_token(self.refresh_token,
|
|
93
|
+
self.scopes,
|
|
94
|
+
self.token_endpoint)
|
|
95
|
+
return self.oidc_token
|
|
96
|
+
|
|
85
97
|
""" Creates a generic storage client to interact with the storage providers
|
|
86
98
|
defined on a specific service of the refered OSCAR cluster """
|
|
87
|
-
def create_storage_client(self, svc):
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
svc_name=svc)
|
|
91
|
-
|
|
99
|
+
def create_storage_client(self, svc=None):
|
|
100
|
+
if svc != None:
|
|
101
|
+
return Storage(
|
|
102
|
+
client_obj=self, svc_name=svc)
|
|
103
|
+
else:
|
|
104
|
+
return Storage(
|
|
105
|
+
client_obj=self)
|
|
106
|
+
|
|
92
107
|
""" Function to get cluster info """
|
|
93
108
|
def get_cluster_info(self):
|
|
94
109
|
return utils.make_request(self, _INFO_PATH, _GET)
|
|
@@ -23,21 +23,37 @@ _MINIO = "minio"
|
|
|
23
23
|
_S3 = "s3"
|
|
24
24
|
_ONE_DATA = "onedata"
|
|
25
25
|
_WEBDAV = "webdav"
|
|
26
|
+
|
|
27
|
+
_GET = "get"
|
|
28
|
+
_CONFIG_PATH = "/system/config"
|
|
26
29
|
_SVC_PATH = "/system/services"
|
|
27
30
|
|
|
28
31
|
|
|
29
32
|
# TODO check returns from functions
|
|
30
33
|
class Storage:
|
|
31
|
-
def __init__(self, client_obj, svc_name) -> None:
|
|
34
|
+
def __init__(self, client_obj, svc_name = None) -> None:
|
|
32
35
|
self.client_obj = client_obj
|
|
33
|
-
self.
|
|
34
|
-
|
|
36
|
+
self.storage_providers = {}
|
|
37
|
+
if svc_name != None:
|
|
38
|
+
self.svc_name = svc_name
|
|
39
|
+
self._store_provider_from_service()
|
|
40
|
+
self._store_default_minio_provider()
|
|
35
41
|
|
|
36
42
|
""" Function to store all the providers of the service """
|
|
37
|
-
def
|
|
38
|
-
svc = utils.make_request(self.client_obj, _SVC_PATH + "/" + self.svc_name,
|
|
43
|
+
def _store_provider_from_service(self):
|
|
44
|
+
svc = utils.make_request(self.client_obj, _SVC_PATH + "/" + self.svc_name, _GET)
|
|
39
45
|
self.storage_providers = json.loads(svc.text)["storage_providers"]
|
|
40
46
|
|
|
47
|
+
""" Function to store the user credentials for the default MinIO provider """
|
|
48
|
+
def _store_default_minio_provider(self):
|
|
49
|
+
config = utils.make_request(self.client_obj, _CONFIG_PATH + "/" , _GET)
|
|
50
|
+
if _MINIO in self.storage_providers:
|
|
51
|
+
self.storage_providers[_MINIO]["default"] = json.loads(config.text)["minio_provider"]
|
|
52
|
+
else:
|
|
53
|
+
default = {"default": json.loads(config.text)["minio_provider"]}
|
|
54
|
+
self.storage_providers[_MINIO] = default
|
|
55
|
+
|
|
56
|
+
|
|
41
57
|
""" Function to retreive credentials of a specific storage provider """
|
|
42
58
|
def _get_provider_creds(self, provider, provider_name):
|
|
43
59
|
return self.storage_providers[provider][provider_name]
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import base64
|
|
3
|
+
from client import Client
|
|
4
|
+
#from client import Client
|
|
5
|
+
from storage import Storage
|
|
6
|
+
from client_anon import AnonymousClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_svc_test(client):
|
|
10
|
+
response = client.get_service("cowsay")
|
|
11
|
+
print(response)
|
|
12
|
+
|
|
13
|
+
def cowsay_test_anon(client):
|
|
14
|
+
text = {"message": "blyat"}
|
|
15
|
+
tk = '38919156059e55805f66b81f74e8f6b59e0ba9e1f987900ed0a6eb0eb0aed82e'
|
|
16
|
+
response = client.run_service("cowsay", token=tk, input=json.dumps(text))
|
|
17
|
+
print(response.text)
|
|
18
|
+
|
|
19
|
+
def cowsay_test_text(client):
|
|
20
|
+
text = "blyat"
|
|
21
|
+
response = client.run_service("cowsay", input=text)
|
|
22
|
+
print(response.text)
|
|
23
|
+
|
|
24
|
+
def plants_test_anon(client):
|
|
25
|
+
tk = "201d9c9c252a47d70e61dacbf02353074921ddd4a39aaef16440949b604c2e26"
|
|
26
|
+
image_in = "/home/calarcon/Pictures/plant2.jpg"
|
|
27
|
+
response = client.run_service("ai4eosc-service", token= tk, input=image_in, timeout=400)
|
|
28
|
+
print(response)
|
|
29
|
+
|
|
30
|
+
def text_to_speech_test(client):
|
|
31
|
+
file_in = "/home/caterina/Documentos/text-to-speech-coqui/text.txt"
|
|
32
|
+
file_out = "/home/caterina/Documentos/text-to-speech-coqui/test-python-run-out.mp3"
|
|
33
|
+
response = client.run_service("text-to-speech-coqui", input="si vose volve a desir uwu", output="test-tes-test.mp3", timeout=100)
|
|
34
|
+
#print(response.text)
|
|
35
|
+
|
|
36
|
+
def plants_sync_test(client):
|
|
37
|
+
image_in = "/home/caterina/Documentos/egi_conference_2023/plants_example/images/cerezos.jpg"
|
|
38
|
+
response = client.run_service("plant-classification", input=image_in, timeout=400)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def grayify_test(client):
|
|
42
|
+
response = client.run_service("grayify", input="/home/caterina/Documentos/imagemagick_example/images")
|
|
43
|
+
|
|
44
|
+
def create_service_test(client):
|
|
45
|
+
path = "/home/caterina/Documentos/imagemagick_example/imagemagick.yaml"
|
|
46
|
+
return client.create_service(path)
|
|
47
|
+
|
|
48
|
+
def json_definition_example():
|
|
49
|
+
return {
|
|
50
|
+
"name": "grayify",
|
|
51
|
+
"memory": "1Gi",
|
|
52
|
+
"cpu": "1.0",
|
|
53
|
+
"total_memory": "1Gi",
|
|
54
|
+
"total_cpu": "1.0",
|
|
55
|
+
"log_level": "CRITICAL",
|
|
56
|
+
"image": "ghcr.io/grycap/imagemagick",
|
|
57
|
+
"alpine": True,
|
|
58
|
+
"script": "#!/bin/bash \necho \'SCRIPT: Invoked Image Grayifier. File available in $INPUT_FILE_PATH\' \nFILE_NAME=`basename \'$INPUT_FILE_PATH\'` \nOUTPUT_FILE=\'$TMP_OUTPUT_DIR/$FILE_NAME\' \necho \'SCRIPT: Converting input image file $INPUT_FILE_PATH to grayscale to output file $OUTPUT_FILE\' \nconvert \'$INPUT_FILE_PATH\' -type Grayscale \'$OUTPUT_FILE\'",
|
|
59
|
+
"image_pull_secrets": [
|
|
60
|
+
],
|
|
61
|
+
"input": [
|
|
62
|
+
{
|
|
63
|
+
"storage_provider": "minio.default",
|
|
64
|
+
"path": "grayify/input",
|
|
65
|
+
"suffix": [
|
|
66
|
+
""
|
|
67
|
+
],
|
|
68
|
+
"prefix": [
|
|
69
|
+
""
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"output": [
|
|
74
|
+
{
|
|
75
|
+
"storage_provider": "minio.default",
|
|
76
|
+
"path": "grayify/output",
|
|
77
|
+
"suffix": [
|
|
78
|
+
""
|
|
79
|
+
],
|
|
80
|
+
"prefix": [
|
|
81
|
+
""
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"storage_provider": "webdav.dcache",
|
|
86
|
+
"path": "/Users/calarcon/grayify",
|
|
87
|
+
"suffix": [
|
|
88
|
+
""
|
|
89
|
+
],
|
|
90
|
+
"prefix": [
|
|
91
|
+
""
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
],
|
|
95
|
+
"storage_providers": {
|
|
96
|
+
"minio": {
|
|
97
|
+
"default": {
|
|
98
|
+
"endpoint": "http://console.minio.wizardly-lamport0.im.grycap.net",
|
|
99
|
+
"region": "us-west-1",
|
|
100
|
+
"secret_key": "a1b2c3d4",
|
|
101
|
+
"access_key": "minio",
|
|
102
|
+
"verify": True
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"webdav": {
|
|
106
|
+
"dcache": {
|
|
107
|
+
"hostname": "prometheus.desy.de",
|
|
108
|
+
"login": "calarcon",
|
|
109
|
+
"password": "v5vsYhd2"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
def ai4eosc_test(client):
|
|
116
|
+
service = {
|
|
117
|
+
"log_level": "CRITICAL",
|
|
118
|
+
"memory": "1Gi",
|
|
119
|
+
"cluster_id": "oscar-cluster",
|
|
120
|
+
"name": "test2",
|
|
121
|
+
"cpu": "1.0",
|
|
122
|
+
"vo": "vo.ai4eosc.eu",
|
|
123
|
+
"image": "deephdc/deep-oc-plants-classification-tf",
|
|
124
|
+
"alpine": False,
|
|
125
|
+
"script": "#!/bin/bash",
|
|
126
|
+
"allowed_users" : []
|
|
127
|
+
}
|
|
128
|
+
res = client.create_service(service)
|
|
129
|
+
print(res.text)
|
|
130
|
+
|
|
131
|
+
def get_service_base_definition():
|
|
132
|
+
"""
|
|
133
|
+
Base parameters of an OSCAR service
|
|
134
|
+
|
|
135
|
+
"""
|
|
136
|
+
return {
|
|
137
|
+
|
|
138
|
+
"log_level": "CRITICAL",
|
|
139
|
+
"alpine": False,
|
|
140
|
+
"script": "#!/bin/bash \nFILE_NAME=`basename $INPUT_FILE_PATH` \
|
|
141
|
+
\nOUTPUT_FILE=\"$TMP_OUTPUT_DIR/$FILE_NAME\"\
|
|
142
|
+
\necho \"SCRIPT: Invoked deepaas-predict command. File available in $INPUT_FILE_PATH.\" \
|
|
143
|
+
\deepaas-predict -i $INPUT_FILE_PATH -o $OUTPUT_FILE"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
def main():
|
|
147
|
+
|
|
148
|
+
#client.remove_service(svc_name)
|
|
149
|
+
#local_client = Client("oscar-test","http://localhost", "oscar", "MTY5Yjc0", False)
|
|
150
|
+
|
|
151
|
+
# Options for basic auth login
|
|
152
|
+
options_basic_auth = {'cluster_id':'oscar-egi-cluster',
|
|
153
|
+
'endpoint':'https://funny-kalam8.im.grycap.net',
|
|
154
|
+
'user':'oscar',
|
|
155
|
+
'password':'oscar123',
|
|
156
|
+
'ssl':'True'}
|
|
157
|
+
|
|
158
|
+
options_egi = {'cluster_id':'oscar-cluster',
|
|
159
|
+
'endpoint':'https://oscar.test.fedcloud.eu/',
|
|
160
|
+
'shortname':'calarcon',
|
|
161
|
+
'ssl':'True'}
|
|
162
|
+
|
|
163
|
+
SERVICE_NAME = "grayify"
|
|
164
|
+
token = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJQVVlPaXJBM1ktZF9kR3BkajRpSkRIdzR6SGE4SVktYmhaZGFFajByamJVIn0.eyJleHAiOjE3MTY4MDMwMDksImlhdCI6MTcxNjc5OTQwOSwiYXV0aF90aW1lIjoxNzE2Nzk1NDgzLCJqdGkiOiI0OTBmNDdlNy00NTc2LTRjODYtYWUyMy00ZjIxNDUzZDEzMjMiLCJpc3MiOiJodHRwczovL2FhaS5lZ2kuZXUvYXV0aC9yZWFsbXMvZWdpIiwic3ViIjoiNjJiYjExYjQwMzk4ZjczNzc4YjY2ZjM0NGQyODIyNDJkZWJiOGVlM2ViYjEwNjcxN2ExMjNjYTIxMzE2MjkyNkBlZ2kuZXUiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJpbS1kYXNoYm9hcmQiLCJzZXNzaW9uX3N0YXRlIjoiMjhiMjU5OGUtYmQ2YS00Y2UzLWI4MDUtNDgyNWRmNGQ2ZjM0Iiwic2NvcGUiOiJvcGVuaWQgb2ZmbGluZV9hY2Nlc3MgZWR1cGVyc29uX2VudGl0bGVtZW50IHByb2ZpbGUgZW1haWwiLCJzaWQiOiIyOGIyNTk4ZS1iZDZhLTRjZTMtYjgwNS00ODI1ZGY0ZDZmMzQiLCJhdXRoZW50aWNhdGluZ19hdXRob3JpdHkiOiJodHRwczovL3d3dy5yZWRpcmlzLmVzL3Npci91cHZpZHAifQ.aZib2Rypqy-XGjPn4ycmEA4u1nlNUb45zz4ry8yZevAdna3lNzEIvxsfwYpjgAvmiDEzrJMYRNAm2652EgvCPUK4bnn9qJuzPhEfSpRbam_jDxmG9MiX6XlKSIO-UqlJQLuDxAAAGuxhdvfqffaT9rZ10042w6-0qTvSiXtNOvFkusc4iVHp8eBcr469txMFvEcWoV8uPXtTL6bl8rbEQMEo2KT1p9twzVpL1WQNIa0XrVa4pfIS3V_IDCKjcDl8PmV3N7KKKkIiQEFg13FS6TSLEs2peszi8eqSAXX2Lq27eSxl0pPH3ZTcrA7vI5dyTKSi7qoK7lsKtHZJOvOCvw"
|
|
165
|
+
options_egi_oidc = {'cluster_id':'oscar-egi-oidc',
|
|
166
|
+
'endpoint':'https://inference.cloud.ai4eosc.eu',
|
|
167
|
+
'oidc_token': token,
|
|
168
|
+
'ssl':'True'}
|
|
169
|
+
|
|
170
|
+
#client = Client("oscar-cluster","https://sleepy-brown8.im.grycap.net", "oscar", "oscar123", True)
|
|
171
|
+
anon_client = AnonymousClient({
|
|
172
|
+
'cluster_id':'oscar-cluster',
|
|
173
|
+
'endpoint':'https://funny-kalam8.im.grycap.net',
|
|
174
|
+
'ssl':'true'
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
#get_svc_test(anon_client)
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
client = Client(options = options_egi)
|
|
181
|
+
print("Client created!!")
|
|
182
|
+
except:
|
|
183
|
+
print("Error creating OSCAR client!!!!11")
|
|
184
|
+
|
|
185
|
+
# res = client.run_service(SERVICE_NAME, input="/home/calarcon/Pictures/cat.jpg", output=".")
|
|
186
|
+
# print(res)
|
|
187
|
+
storage = client.create_storage_client()
|
|
188
|
+
print(storage.storage_providers)
|
|
189
|
+
print("------------------------")
|
|
190
|
+
storage = client.create_storage_client("grayify")
|
|
191
|
+
print(storage.storage_providers)
|
|
192
|
+
#cowsay_test_text(client)
|
|
193
|
+
#get_svc_test(client)
|
|
194
|
+
#grayify_storage = client.create_storage_client(svc_name)
|
|
195
|
+
#stablediff_storage = client.create_storage_client("stable-diffusion")
|
|
196
|
+
|
|
197
|
+
# Download files test
|
|
198
|
+
#res = stablediff_storage.download_file("minio.default", "/home/caterina/Documentos/oscar_python_package", "stable-diffusion/out/text1.zip")
|
|
199
|
+
#res = grayify_storage.download_file("minio.default", "/home/caterina/Documentos/oscar_python_package", "gray/output/image1.jpg")
|
|
200
|
+
|
|
201
|
+
# Upload files test
|
|
202
|
+
#res = stablediff_storage.upload_file("minio.default", "/home/caterina/Documentos/txt_to_img/text1.txt", "stable-diffusion/in")
|
|
203
|
+
#res = grayify_storage.upload_file("minio.default", "/home/caterina/Documentos/imagemagick_example/images/image2.jpeg", "gray/input")
|
|
204
|
+
|
|
205
|
+
if __name__ == "__main__":
|
|
206
|
+
main()
|
|
@@ -1,35 +1,20 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
2
|
-
Name:
|
|
3
|
-
Version: 1.3.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: oscar-python
|
|
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
|
|
12
13
|
License-File: LICENSE
|
|
13
|
-
Requires-Dist: webdavclient3==3.14.6
|
|
14
|
-
Requires-Dist: requests
|
|
15
|
-
Requires-Dist: boto3
|
|
16
|
-
Requires-Dist: setuptools>=40.8.0
|
|
17
|
-
Requires-Dist: pyyaml
|
|
18
|
-
Requires-Dist: aiohttp
|
|
19
|
-
Requires-Dist: liboidcagent
|
|
20
|
-
Dynamic: author
|
|
21
|
-
Dynamic: author-email
|
|
22
|
-
Dynamic: classifier
|
|
23
|
-
Dynamic: description
|
|
24
|
-
Dynamic: description-content-type
|
|
25
|
-
Dynamic: home-page
|
|
26
|
-
Dynamic: license
|
|
27
|
-
Dynamic: requires-dist
|
|
28
|
-
Dynamic: summary
|
|
29
14
|
|
|
30
15
|
## Python OSCAR client
|
|
31
16
|
|
|
32
|
-
[](https://github.com/grycap/oscar_python/actions/workflows/main.yaml)
|
|
33
18
|

|
|
34
19
|
|
|
35
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/).
|
|
@@ -84,6 +69,33 @@ If you already have a valid token, you can use the parameter `oidc_token` instea
|
|
|
84
69
|
```
|
|
85
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`.
|
|
86
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
|
+
|
|
87
99
|
### Sample usage
|
|
88
100
|
|
|
89
101
|
- Sample code that creates a client and gets information about the cluster
|
|
@@ -219,11 +231,19 @@ response = client.remove_all_jobs("service_name") # returns an http response
|
|
|
219
231
|
|
|
220
232
|
#### Storage usage
|
|
221
233
|
|
|
222
|
-
You can create a storage object to operate over the different storage providers defined on a service with the method `create_storage_client
|
|
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.
|
|
223
242
|
|
|
224
243
|
``` python
|
|
225
244
|
storage_service = client.create_storage_client("service_name") # returns a storage object
|
|
226
245
|
```
|
|
246
|
+
|
|
227
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)_
|
|
228
248
|
|
|
229
249
|
**list_files_from_path**
|
|
@@ -246,3 +266,5 @@ response = storage_service.upload_file("storage_provider", "local_path", "remote
|
|
|
246
266
|
# download a file from a remote path to a local path
|
|
247
267
|
response = storage_service.download_file("storage_provider", "local_path", "remote_path")
|
|
248
268
|
```
|
|
269
|
+
|
|
270
|
+
|
|
@@ -2,12 +2,14 @@ LICENSE
|
|
|
2
2
|
README.md
|
|
3
3
|
setup.py
|
|
4
4
|
oscar_python/__init__.py
|
|
5
|
+
oscar_python/_oidc.py
|
|
5
6
|
oscar_python/_utils.py
|
|
6
7
|
oscar_python/client.py
|
|
7
8
|
oscar_python/client_anon.py
|
|
8
9
|
oscar_python/default_client.py
|
|
9
10
|
oscar_python/local_test.py
|
|
10
11
|
oscar_python/storage.py
|
|
12
|
+
oscar_python/test_v2.py
|
|
11
13
|
oscar_python.egg-info/PKG-INFO
|
|
12
14
|
oscar_python.egg-info/SOURCES.txt
|
|
13
15
|
oscar_python.egg-info/dependency_links.txt
|
|
@@ -21,6 +23,7 @@ oscar_python/_providers/_s3.py
|
|
|
21
23
|
oscar_python/_providers/_webdav.py
|
|
22
24
|
tests/test_client.py
|
|
23
25
|
tests/test_default_client.py
|
|
26
|
+
tests/test_oidc.py
|
|
24
27
|
tests/test_onedata.py
|
|
25
28
|
tests/test_s3.py
|
|
26
29
|
tests/test_storage.py
|
|
@@ -44,6 +44,15 @@ def test_oidc_client(options):
|
|
|
44
44
|
assert client.oidc_token == options['oidc_token']
|
|
45
45
|
assert client.ssl == options['ssl']
|
|
46
46
|
|
|
47
|
+
del options['oidc_token']
|
|
48
|
+
options['refresh_token'] = 'test_refresh_token'
|
|
49
|
+
options['scopes'] = ['openid', 'profile', 'email']
|
|
50
|
+
options['token_endpoint'] = 'test_token_endpoint'
|
|
51
|
+
client = Client(options)
|
|
52
|
+
assert client.refresh_token == options['refresh_token']
|
|
53
|
+
assert client.scopes == ['openid', 'profile', 'email']
|
|
54
|
+
assert client.token_endpoint == options['token_endpoint']
|
|
55
|
+
|
|
47
56
|
|
|
48
57
|
def test_set_auth_type(options):
|
|
49
58
|
client = Client(options)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from unittest.mock import patch, MagicMock
|
|
2
|
+
from oscar_python._oidc import OIDC
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_is_access_token_expired():
|
|
6
|
+
token = ("eyJraWQiOiJyc2ExIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJkYzVkNWFiNy02ZGI5LTQwNzktOTg1Yy04MGFjMDUwMTcw"
|
|
7
|
+
"NjYiLCJpc3MiOiJodHRwczpcL1wvaWFtLXRlc3QuaW5kaWdvLWRhdGFjbG91ZC5ldVwvIiwiZXhwIjoxNDY2MDkzOTE3LCJ"
|
|
8
|
+
"pYXQiOjE0NjYwOTAzMTcsImp0aSI6IjE1OTU2N2U2LTdiYzItNDUzOC1hYzNhLWJjNGU5MmE1NjlhMCJ9.eINKxJa2J--xd"
|
|
9
|
+
"GAZWIOKtx9Wi0Vz3xHzaSJWWY-UHWy044TQ5xYtt0VTvmY5Af-ngwAMGfyaqAAvNn1VEP-_fMYQZdwMqcXLsND4KkDi1ygiC"
|
|
10
|
+
"IwQ3JBz9azBT1o_oAHE5BsPsE2BjfDoVRasZxxW5UoXCmBslonYd8HK2tUVjz0")
|
|
11
|
+
assert OIDC.is_access_token_expired(token)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_refresh_access_token():
|
|
15
|
+
mock_response = MagicMock()
|
|
16
|
+
mock_response.status_code = 200
|
|
17
|
+
mock_response.json.return_value = {
|
|
18
|
+
"access_token": "new_access_token",
|
|
19
|
+
"expires_in": 3600,
|
|
20
|
+
"refresh_token": "new_refresh_token"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
with patch("requests.post") as mock_post:
|
|
24
|
+
mock_post.return_value = mock_response
|
|
25
|
+
access_token = OIDC.refresh_access_token("old_refresh_token",
|
|
26
|
+
["openid", "profile", "email"],
|
|
27
|
+
"http://test.com/token")
|
|
28
|
+
|
|
29
|
+
assert access_token == "new_access_token"
|
|
30
|
+
mock_post.assert_called_once_with(
|
|
31
|
+
"http://test.com/token",
|
|
32
|
+
data={
|
|
33
|
+
"grant_type": "refresh_token",
|
|
34
|
+
"refresh_token": "old_refresh_token",
|
|
35
|
+
"client_id": "token-portal",
|
|
36
|
+
"scope": "openid profile email"
|
|
37
|
+
}
|
|
38
|
+
)
|
|
@@ -11,9 +11,9 @@ def mock_client_obj():
|
|
|
11
11
|
@pytest.fixture
|
|
12
12
|
def storage(mock_client_obj):
|
|
13
13
|
mock_response = MagicMock()
|
|
14
|
-
mock_response.text = '{"
|
|
14
|
+
mock_response.text = '{"minio_provider": {"access_key": "key","secret_key": "secret", "endpoint": "http://test.endpoint", "region": "us-east-1", "verify": false}}'
|
|
15
15
|
with patch('oscar_python._utils.make_request', return_value=mock_response):
|
|
16
|
-
return Storage(mock_client_obj
|
|
16
|
+
return Storage(mock_client_obj)
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def test_get_provider_creds(storage):
|
|
@@ -29,7 +29,9 @@ def test_get_headers_with_oidc_agent():
|
|
|
29
29
|
def test_get_headers_with_oidc():
|
|
30
30
|
class MockClient:
|
|
31
31
|
_AUTH_TYPE = "oidc"
|
|
32
|
-
|
|
32
|
+
|
|
33
|
+
def get_access_token(self):
|
|
34
|
+
return "test_oidc_token"
|
|
33
35
|
|
|
34
36
|
c = MockClient()
|
|
35
37
|
headers = utils.get_headers(c)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|