seqslab-cli 3.3.7__tar.gz → 3.3.10__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.
- {seqslab-cli-3.3.7/python/seqslab_cli.egg-info → seqslab-cli-3.3.10}/PKG-INFO +1 -1
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/__init__.py +1 -1
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/auth/commands.py +54 -15
- seqslab-cli-3.3.10/python/seqslab/auth/utils.py +46 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/api/base.py +2 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/commands.py +69 -19
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/internal/utils.py +35 -4
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/storage/azure.py +16 -4
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/utils/biomimetype.py +1 -1
- seqslab-cli-3.3.10/python/seqslab/organization/commands.py +119 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/user/commands.py +2 -3
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/user/resource/azure.py +1 -1
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/user/resource/base.py +3 -4
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/wes/resource/base.py +2 -2
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10/python/seqslab_cli.egg-info}/PKG-INFO +1 -1
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab_cli.egg-info/SOURCES.txt +1 -0
- seqslab-cli-3.3.7/python/seqslab/organization/commands.py +0 -77
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/LICENSE +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/MANIFEST.in +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/README.md +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/auth/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/auth/azuread.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/cli.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/color.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/context.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/api/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/api/azure.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/api/common.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/api/template.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/internal/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/internal/aiocopy.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/internal/common.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/storage/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/storage/base.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/utils/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/utils/atgxmetadata.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/drs/utils/progressbar.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/exceptions.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/organization/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/organization/resource/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/organization/resource/base.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/plugin.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/role/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/role/commands.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/role/internal/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/role/internal/common.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/role/resource/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/role/resource/azure.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/role/resource/base.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/runsheet/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/runsheet/runsheet.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/sample_sheet/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/sample_sheet/_version.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/sample_sheet/util.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/scr/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/scr/commands.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/scr/internal/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/scr/internal/common.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/scr/resource/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/scr/resource/azure.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/scr/resource/base.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/session_logger.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/settings.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/statusbar.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/commands.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/internal/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/internal/utils.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/register/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/register/azure.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/register/base.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/register/common.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/register/template.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/resource/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/resource/azure.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/resource/base.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/resource/common.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/template/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/template/base.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/trs/template/template.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/usage_logger.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/user/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/user/internal/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/user/internal/common.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/user/resource/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/wes/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/wes/commands.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/wes/internal/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/wes/internal/parameters.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/wes/resource/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/wes/resource/azure.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/wes/resource/common.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/wes/template/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/wes/template/base.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/wes/template/template.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/workspace/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/workspace/commands.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/workspace/internal/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/workspace/internal/common.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/workspace/resource/__init__.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/workspace/resource/azure.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab/workspace/resource/base.py +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab_cli.egg-info/dependency_links.txt +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab_cli.egg-info/entry_points.txt +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab_cli.egg-info/requires.txt +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab_cli.egg-info/top_level.txt +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/python/seqslab_cli.egg-info/zip-safe +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/requirements.txt +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/setup.cfg +0 -0
- {seqslab-cli-3.3.7 → seqslab-cli-3.3.10}/setup.py +0 -0
|
@@ -19,6 +19,7 @@ from requests import HTTPError, request
|
|
|
19
19
|
from seqslab import settings as api_settings
|
|
20
20
|
from seqslab.auth import __version__, aad_app, azuread
|
|
21
21
|
from seqslab.auth.azuread import API_HOSTNAME
|
|
22
|
+
from seqslab.auth.utils import get_org, request_wrap
|
|
22
23
|
from termcolor import cprint
|
|
23
24
|
|
|
24
25
|
"""
|
|
@@ -44,12 +45,10 @@ class BaseAuth:
|
|
|
44
45
|
AUTHORIZATION_URL = f"https://{API_HOSTNAME}/auth/{__version__}/signin/{{backend}}/"
|
|
45
46
|
ACCESS_TOKEN_URL = f"https://{API_HOSTNAME}/auth/{__version__}/token/{{backend}}/"
|
|
46
47
|
REFRESH_TOKEN_URL = f"https://{API_HOSTNAME}/auth/{__version__}/token/refresh/"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
response.raise_for_status()
|
|
52
|
-
return response
|
|
48
|
+
PROFILE_URL = f"https://{API_HOSTNAME}/auth/{__version__}/profile/"
|
|
49
|
+
ORGANIZATION_GET_URL = (
|
|
50
|
+
f"https://{API_HOSTNAME}/management/{__version__}/organizations/{{cus_id}}/"
|
|
51
|
+
)
|
|
53
52
|
|
|
54
53
|
def _signin_azure_silent(
|
|
55
54
|
self,
|
|
@@ -76,7 +75,7 @@ class BaseAuth:
|
|
|
76
75
|
)
|
|
77
76
|
|
|
78
77
|
result.update({"assertion": assertion, "scope": " ".join(scopes)})
|
|
79
|
-
return
|
|
78
|
+
return request_wrap(
|
|
80
79
|
self.ACCESS_TOKEN_URL.format(backend=backend),
|
|
81
80
|
method=self.ACCESS_TOKEN_METHOD,
|
|
82
81
|
params={"secure": True},
|
|
@@ -89,7 +88,12 @@ class BaseAuth:
|
|
|
89
88
|
).json()
|
|
90
89
|
|
|
91
90
|
def _signin_azure(
|
|
92
|
-
self,
|
|
91
|
+
self,
|
|
92
|
+
device_code: bool,
|
|
93
|
+
daemon: bool,
|
|
94
|
+
backend: str,
|
|
95
|
+
organization_id: str,
|
|
96
|
+
proxy: str = None,
|
|
93
97
|
) -> dict:
|
|
94
98
|
result = None
|
|
95
99
|
token_uri = self.ACCESS_TOKEN_URL.format(backend=backend)
|
|
@@ -121,7 +125,7 @@ class BaseAuth:
|
|
|
121
125
|
if not device_code:
|
|
122
126
|
# obtain authorization uri
|
|
123
127
|
auth_uri = (
|
|
124
|
-
|
|
128
|
+
request_wrap(
|
|
125
129
|
self.AUTHORIZATION_URL.format(backend=backend),
|
|
126
130
|
method=self.ACCESS_TOKEN_METHOD,
|
|
127
131
|
headers={
|
|
@@ -176,8 +180,10 @@ class BaseAuth:
|
|
|
176
180
|
raise PermissionError(
|
|
177
181
|
"{}: {}".format(result["error"], result["error_description"])
|
|
178
182
|
)
|
|
183
|
+
if organization_id:
|
|
184
|
+
result.update({"cus_id": organization_id})
|
|
179
185
|
|
|
180
|
-
return
|
|
186
|
+
return request_wrap(
|
|
181
187
|
token_uri,
|
|
182
188
|
method=self.ACCESS_TOKEN_METHOD,
|
|
183
189
|
params={"secure": True} if daemon else None,
|
|
@@ -230,7 +236,7 @@ class BaseAuth:
|
|
|
230
236
|
if arrow.utcnow() >= arrow.get(Auth.tokens["attrs"]["exp"]):
|
|
231
237
|
# expired, refresh token
|
|
232
238
|
proxy = context.get_context().args.proxy
|
|
233
|
-
with
|
|
239
|
+
with request_wrap(
|
|
234
240
|
Auth.REFRESH_TOKEN_URL,
|
|
235
241
|
method=Auth.ACCESS_TOKEN_METHOD,
|
|
236
242
|
json={"refresh": Auth.tokens["tokens"]["refresh"]},
|
|
@@ -270,13 +276,27 @@ class BaseAuth:
|
|
|
270
276
|
"daemon",
|
|
271
277
|
type=bool,
|
|
272
278
|
positional=False,
|
|
273
|
-
description="Sign in for a long-running, non-interactive daemon process (optional).",
|
|
279
|
+
description="Sign in for a long-running, non-interactive daemon process. (optional).",
|
|
274
280
|
aliases=["d"],
|
|
275
281
|
)
|
|
276
|
-
|
|
282
|
+
@argument(
|
|
283
|
+
"organization_id",
|
|
284
|
+
type=str,
|
|
285
|
+
positional=False,
|
|
286
|
+
description="The organization id to signin to (optional). This argument is valid only for device_code flow.",
|
|
287
|
+
aliases=["o"],
|
|
288
|
+
)
|
|
289
|
+
def signin(
|
|
290
|
+
self, device_code: bool = False, daemon: bool = False, organization_id=None
|
|
291
|
+
) -> int:
|
|
277
292
|
"""
|
|
278
293
|
Sign in to the configured authentication backend and obtain API access token.
|
|
279
294
|
"""
|
|
295
|
+
if organization_id and not device_code:
|
|
296
|
+
logging.warning("Organization_id is only valid for device code flow")
|
|
297
|
+
cprint("Organization_id is valid only for device cod flow.", "red")
|
|
298
|
+
return errno.EINVAL
|
|
299
|
+
|
|
280
300
|
ctx = context.get_context()
|
|
281
301
|
backend = ctx.args.backend
|
|
282
302
|
proxy = ctx.args.proxy
|
|
@@ -289,9 +309,10 @@ class BaseAuth:
|
|
|
289
309
|
|
|
290
310
|
try:
|
|
291
311
|
method = getattr(self, mname)
|
|
292
|
-
result = method(device_code, daemon, backend, proxy)
|
|
312
|
+
result = method(device_code, daemon, backend, organization_id, proxy)
|
|
293
313
|
# store in keyring secret service
|
|
294
314
|
user = getpass.getuser()
|
|
315
|
+
|
|
295
316
|
keyring.set_password(
|
|
296
317
|
"net.seqslab.api.tokens.refresh", user, result["tokens"]["refresh"]
|
|
297
318
|
)
|
|
@@ -313,7 +334,8 @@ class BaseAuth:
|
|
|
313
334
|
@command(
|
|
314
335
|
aliases=["daemon"],
|
|
315
336
|
help="Authenticate silently to the platform and obtain API access token. "
|
|
316
|
-
"This requires that the SeqsLab API app admin consent has been granted
|
|
337
|
+
"This requires that the SeqsLab API app admin consent has been granted and also the user must "
|
|
338
|
+
"daemon-signin beforehand.",
|
|
317
339
|
)
|
|
318
340
|
@argument(
|
|
319
341
|
"scope",
|
|
@@ -362,6 +384,15 @@ class BaseAuth:
|
|
|
362
384
|
keyring.set_password(
|
|
363
385
|
"net.seqslab.api.tokens.access", user, result["tokens"]["access"]
|
|
364
386
|
)
|
|
387
|
+
org_info = get_org(
|
|
388
|
+
access_token=result["tokens"]["access"],
|
|
389
|
+
cus_id=self._decode(result["tokens"]["access"])["cus_id"],
|
|
390
|
+
proxy=proxy,
|
|
391
|
+
)
|
|
392
|
+
cprint(
|
|
393
|
+
f"Daemon process signin to {org_info['name']}",
|
|
394
|
+
"yellow",
|
|
395
|
+
)
|
|
365
396
|
return 0
|
|
366
397
|
except ValueError:
|
|
367
398
|
return errno.EINVAL
|
|
@@ -381,11 +412,19 @@ class BaseAuth:
|
|
|
381
412
|
"""
|
|
382
413
|
Print platform access token.
|
|
383
414
|
"""
|
|
415
|
+
ctx = context.get_context()
|
|
416
|
+
proxy = ctx.args.proxy
|
|
384
417
|
token = self.get_token()
|
|
385
418
|
if not token:
|
|
386
419
|
cprint("Not signed in yet")
|
|
387
420
|
return errno.EPERM
|
|
388
421
|
|
|
422
|
+
org_info = get_org(
|
|
423
|
+
access_token=token["tokens"]["access"],
|
|
424
|
+
cus_id=self._decode(token["tokens"]["access"])["cus_id"],
|
|
425
|
+
proxy=proxy,
|
|
426
|
+
)
|
|
427
|
+
cprint(f"For Organization: {org_info['name']} ({org_info['cus_id']})", "yellow")
|
|
389
428
|
cprint(token["tokens"]["access"], "yellow")
|
|
390
429
|
return 0
|
|
391
430
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Standard Library
|
|
2
|
+
from functools import lru_cache
|
|
3
|
+
|
|
4
|
+
from requests import request
|
|
5
|
+
from seqslab.auth import __version__, aad_app, azuread
|
|
6
|
+
from seqslab.auth.azuread import API_HOSTNAME
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
Copyright (C) 2024, Atgenomix Incorporated.
|
|
10
|
+
|
|
11
|
+
All Rights Reserved.
|
|
12
|
+
|
|
13
|
+
This program is an unpublished copyrighted work which is proprietary to
|
|
14
|
+
Atgenomix Incorporated and contains confidential information that is not to
|
|
15
|
+
be reproduced or disclosed to any other person or entity without prior
|
|
16
|
+
written consent from Atgenomix, Inc. in each and every instance.
|
|
17
|
+
|
|
18
|
+
Unauthorized reproduction of this program as well as unauthorized
|
|
19
|
+
preparation of derivative works based upon the program or distribution of
|
|
20
|
+
copies by sale, rental, lease or lending are violations of federal copyright
|
|
21
|
+
laws and state trade secret laws, punishable by civil and criminal penalties.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
ORGANIZATION_GET_URL = (
|
|
25
|
+
f"https://{API_HOSTNAME}/management/{__version__}/organizations/{{cus_id}}/"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def request_wrap(url, method, *args, **kwargs):
|
|
30
|
+
response = request(method, url, **kwargs)
|
|
31
|
+
response.raise_for_status()
|
|
32
|
+
return response
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@lru_cache(maxsize=16)
|
|
36
|
+
def get_org(access_token, cus_id, proxy):
|
|
37
|
+
return request_wrap(
|
|
38
|
+
ORGANIZATION_GET_URL.format(cus_id=cus_id),
|
|
39
|
+
method="GET",
|
|
40
|
+
headers={
|
|
41
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
42
|
+
"Accept": "application/json",
|
|
43
|
+
"Authorization": f"Bearer {access_token}",
|
|
44
|
+
},
|
|
45
|
+
proxies=proxy,
|
|
46
|
+
).json()
|
|
@@ -156,6 +156,7 @@ class DRSregister:
|
|
|
156
156
|
checksum_type: str = None,
|
|
157
157
|
updated_time: str = None,
|
|
158
158
|
deleted_time: str = None,
|
|
159
|
+
access_methods: dict = {},
|
|
159
160
|
**kwargs,
|
|
160
161
|
) -> dict:
|
|
161
162
|
"""
|
|
@@ -175,6 +176,7 @@ class DRSregister:
|
|
|
175
176
|
checksums=checksums,
|
|
176
177
|
updated_time=updated_time,
|
|
177
178
|
deleted_time=deleted_time,
|
|
179
|
+
access_methods=access_methods,
|
|
178
180
|
)
|
|
179
181
|
return template
|
|
180
182
|
|
|
@@ -600,8 +600,8 @@ class BaseDatahub:
|
|
|
600
600
|
type=str,
|
|
601
601
|
description="Specify the access methods of the object that you want to register. "
|
|
602
602
|
"(optional in stdin mode, otherwise required)"
|
|
603
|
-
'For example, [{"type":"https","access_url":{"url":"https://storage.url","headers":{'
|
|
604
|
-
'"Authorization":authorization}},"access_tier":"hot","region":"westus3"}, ... ]',
|
|
603
|
+
'For example, \'[{"type":"https","access_url":{"url":"https://storage.url","headers":{'
|
|
604
|
+
'"Authorization":authorization}},"access_tier":"hot","region":"westus3"}, ... ]\'',
|
|
605
605
|
)
|
|
606
606
|
@argument(
|
|
607
607
|
"checksum",
|
|
@@ -939,36 +939,63 @@ class BaseDatahub:
|
|
|
939
939
|
|
|
940
940
|
@command
|
|
941
941
|
@argument(
|
|
942
|
-
"
|
|
942
|
+
"tags",
|
|
943
943
|
type=List[str],
|
|
944
944
|
positional=False,
|
|
945
945
|
description="Locate a DRS object using a list of names " "(optional).",
|
|
946
946
|
)
|
|
947
947
|
@argument(
|
|
948
|
-
"
|
|
948
|
+
"keyword",
|
|
949
|
+
type=str,
|
|
950
|
+
positional=False,
|
|
951
|
+
description="Locate a DRS object with keyword search " "(optional).",
|
|
952
|
+
)
|
|
953
|
+
@argument(
|
|
954
|
+
"page",
|
|
955
|
+
type=int,
|
|
956
|
+
positional=False,
|
|
957
|
+
description="Page index of the return value (optional, default = 1).",
|
|
958
|
+
)
|
|
959
|
+
@argument(
|
|
960
|
+
"page_size",
|
|
961
|
+
type=int,
|
|
962
|
+
positional=False,
|
|
963
|
+
description="Number of record in a page (optional, default = 25).",
|
|
964
|
+
)
|
|
965
|
+
@argument(
|
|
966
|
+
"file_types",
|
|
949
967
|
type=List[str],
|
|
950
968
|
positional=False,
|
|
951
|
-
description="
|
|
969
|
+
description="File Type attributes (optional, default = []]).",
|
|
970
|
+
)
|
|
971
|
+
@argument(
|
|
972
|
+
"owner",
|
|
973
|
+
type=bool,
|
|
974
|
+
positional=False,
|
|
975
|
+
description="Whether to return self-own object only (optional, default = False).",
|
|
952
976
|
)
|
|
953
977
|
def search(
|
|
954
978
|
self,
|
|
955
979
|
tags: List[str] = [],
|
|
956
|
-
|
|
980
|
+
keyword: str = "",
|
|
981
|
+
page: int = 1,
|
|
982
|
+
page_size: int = 25,
|
|
983
|
+
file_types: List[str] = [],
|
|
984
|
+
owner: bool = False,
|
|
957
985
|
) -> int:
|
|
958
986
|
"""
|
|
959
|
-
Locate DRS objects either by
|
|
987
|
+
Locate DRS objects either by keyword or by tags.
|
|
960
988
|
"""
|
|
961
|
-
if not
|
|
962
|
-
cprint("Either give
|
|
963
|
-
return errno.ENOENT
|
|
964
|
-
if names and tags:
|
|
965
|
-
cprint(
|
|
966
|
-
"Search condition names and tags cannot be given at the same time",
|
|
967
|
-
"red",
|
|
968
|
-
)
|
|
989
|
+
if not keyword and not tags:
|
|
990
|
+
cprint("Either give keyword or tags to do DRS query", "red")
|
|
969
991
|
return errno.ENOENT
|
|
970
|
-
|
|
971
|
-
|
|
992
|
+
extra_params = {
|
|
993
|
+
"page": page,
|
|
994
|
+
"page_size": page_size,
|
|
995
|
+
"file_types": file_types,
|
|
996
|
+
"owner": owner,
|
|
997
|
+
}
|
|
998
|
+
result = asyncio.run(utils.drs_keyword_search(keyword, tags, **extra_params))
|
|
972
999
|
cprint(json.dumps(result, indent=4))
|
|
973
1000
|
|
|
974
1001
|
return 0
|
|
@@ -1329,6 +1356,16 @@ class BaseDatahub:
|
|
|
1329
1356
|
description="Specify a date in the format YYYY-MM-DD to set the automatic deletion time for the DRS "
|
|
1330
1357
|
"object, for example, 2024-01-01 (optional).",
|
|
1331
1358
|
)
|
|
1359
|
+
@argument(
|
|
1360
|
+
"access_methods",
|
|
1361
|
+
type=str,
|
|
1362
|
+
positional=False,
|
|
1363
|
+
description="Specify the DRS object access_methods in the format of json string (optional)."
|
|
1364
|
+
'For example, \'[{"id":320230,"type":"abfss","region":"westus2","access_url":{"headers":{'
|
|
1365
|
+
'"Authorization":"st=2025-01-06T060628Z&se=2025-01-09T060628Z00xxx"},'
|
|
1366
|
+
'"url":"abfss://org-epa906yha51qwmj@cgmwus232b21storage.dfs.core.windows.net/drs/user_admin'
|
|
1367
|
+
"/ref/ffseq/pd4615-sup-0008-file1.rdata\"}}]' ",
|
|
1368
|
+
)
|
|
1332
1369
|
@argument(
|
|
1333
1370
|
"stdin",
|
|
1334
1371
|
type=bool,
|
|
@@ -1359,13 +1396,26 @@ class BaseDatahub:
|
|
|
1359
1396
|
kwargs = kwargs[0]
|
|
1360
1397
|
kwargs["workspace"] = workspace
|
|
1361
1398
|
|
|
1362
|
-
if kwargs.get("metadata"):
|
|
1399
|
+
if metadata := kwargs.get("metadata"):
|
|
1363
1400
|
try:
|
|
1364
|
-
kwargs["metadata"] = json.loads(
|
|
1401
|
+
kwargs["metadata"] = json.loads(metadata)
|
|
1365
1402
|
except Exception as e:
|
|
1366
1403
|
cprint(f"Provided metadata is not a valid json-string: {e}", "red")
|
|
1367
1404
|
return errno.EINVAL
|
|
1368
1405
|
|
|
1406
|
+
if access_methods := kwargs.get("access_methods"):
|
|
1407
|
+
try:
|
|
1408
|
+
kwargs["access_methods"] = (
|
|
1409
|
+
json.loads(access_methods)
|
|
1410
|
+
if isinstance(access_methods, str)
|
|
1411
|
+
else access_methods
|
|
1412
|
+
)
|
|
1413
|
+
except Exception as e:
|
|
1414
|
+
cprint(
|
|
1415
|
+
f"Provided access_methods is not a valid json-string: {e}", "red"
|
|
1416
|
+
)
|
|
1417
|
+
return errno.EINVAL
|
|
1418
|
+
|
|
1369
1419
|
def __log(results: dict):
|
|
1370
1420
|
self._stdout(results=[results], output="json")
|
|
1371
1421
|
msg = f"Register {results['id']} is complete."
|
|
@@ -34,7 +34,7 @@ DRS_OBJECT_URL = f"https://{API_HOSTNAME}/ga4gh/drs/{__version__}/objects/"
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
@async_exception_handler
|
|
37
|
-
async def
|
|
37
|
+
async def drs_exact_match(names: List[str], labels: List[str], **kwargs) -> dict:
|
|
38
38
|
if names and labels:
|
|
39
39
|
raise ValueError("one search condition, either name or label, is allowed")
|
|
40
40
|
|
|
@@ -66,6 +66,34 @@ async def drs_search(names: List[str], labels: List[str], **kwargs) -> dict:
|
|
|
66
66
|
return json.loads(resp)
|
|
67
67
|
|
|
68
68
|
|
|
69
|
+
@async_exception_handler
|
|
70
|
+
async def drs_keyword_search(keyword: str, labels: List[str], **kwargs) -> dict:
|
|
71
|
+
try:
|
|
72
|
+
token = BaseAuth.get_token().get("tokens").get("access")
|
|
73
|
+
except KeyError:
|
|
74
|
+
raise KeyError("No tokens, Please signin first!")
|
|
75
|
+
params = f'?page_size={kwargs["page_size"]}&page={kwargs["page"]}&'
|
|
76
|
+
if keyword:
|
|
77
|
+
params += f"search={keyword}&"
|
|
78
|
+
if labels:
|
|
79
|
+
for lb in labels:
|
|
80
|
+
params += f"label={lb}&"
|
|
81
|
+
if types := kwargs["file_types"]:
|
|
82
|
+
for t in types:
|
|
83
|
+
params += f"file_types={t}&"
|
|
84
|
+
if kwargs["owner"]:
|
|
85
|
+
params += "owner=true&"
|
|
86
|
+
url = f"{DRS_OBJECT_URL}{params}"
|
|
87
|
+
async with ClientSession(raise_for_status=False).request(
|
|
88
|
+
method="get",
|
|
89
|
+
url=url.rstrip("&"),
|
|
90
|
+
proxy=kwargs.get("proxy", None),
|
|
91
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
92
|
+
) as response:
|
|
93
|
+
resp = await response.content.read()
|
|
94
|
+
return json.loads(resp)
|
|
95
|
+
|
|
96
|
+
|
|
69
97
|
@async_exception_handler
|
|
70
98
|
async def drs_crud(drs_id: str, method: str, sem: asyncio.Semaphore, **kwargs) -> dict:
|
|
71
99
|
try:
|
|
@@ -107,9 +135,9 @@ async def get_by_ids(drs_ids: List[str], **kwargs):
|
|
|
107
135
|
@async_exception_handler
|
|
108
136
|
async def drs_existence(path: URL, label_check: bool = True) -> bool:
|
|
109
137
|
if label_check:
|
|
110
|
-
res = await
|
|
138
|
+
res = await drs_exact_match(names=[], labels=[str(path)])
|
|
111
139
|
else:
|
|
112
|
-
res = await
|
|
140
|
+
res = await drs_exact_match(names=[os.path.basename(str(path))], labels=[])
|
|
113
141
|
|
|
114
142
|
if not res.get("objects"):
|
|
115
143
|
return False
|
|
@@ -119,7 +147,10 @@ async def drs_existence(path: URL, label_check: bool = True) -> bool:
|
|
|
119
147
|
|
|
120
148
|
@async_exception_handler
|
|
121
149
|
async def drs_delete(ids: List[str], names: List[str], labels: List[str], **kwargs):
|
|
122
|
-
search = [
|
|
150
|
+
search = [
|
|
151
|
+
drs_exact_match(names=names, labels=[]),
|
|
152
|
+
drs_exact_match(names=[], labels=labels),
|
|
153
|
+
]
|
|
123
154
|
resps = await asyncio.gather(*search, return_exceptions=False)
|
|
124
155
|
drs_ids = list(
|
|
125
156
|
set([ob.get("id") for res in resps for ob in res.get("objects")] + ids)
|
|
@@ -4,6 +4,7 @@ import datetime
|
|
|
4
4
|
import hashlib
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
|
+
import time
|
|
7
8
|
from base64 import b64encode
|
|
8
9
|
from functools import lru_cache
|
|
9
10
|
from hashlib import md5
|
|
@@ -375,10 +376,21 @@ class BlobStorage(BaseBlob):
|
|
|
375
376
|
"x-ms-date": date,
|
|
376
377
|
}
|
|
377
378
|
try:
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
379
|
+
retry = 5
|
|
380
|
+
while retry:
|
|
381
|
+
retry -= 1
|
|
382
|
+
try:
|
|
383
|
+
get_block_list = requests.get(
|
|
384
|
+
url=str(url),
|
|
385
|
+
headers=headers,
|
|
386
|
+
)
|
|
387
|
+
break
|
|
388
|
+
except Exception as err:
|
|
389
|
+
if progress_bar := kwargs.get("progress_bar"):
|
|
390
|
+
progress_bar.print(
|
|
391
|
+
f"Get block list failed, trial ${retry}.(Connection failed with error ${err}.)"
|
|
392
|
+
)
|
|
393
|
+
time.sleep(float(1))
|
|
382
394
|
if get_block_list.status_code == 200:
|
|
383
395
|
if progress_bar := kwargs.get("progress_bar"):
|
|
384
396
|
progress_bar.print("Get block list successfully.")
|
|
@@ -21,7 +21,7 @@ class bioMimeTypes(MimeTypes):
|
|
|
21
21
|
table[f"{prefix}"] = p_mime_types[i]
|
|
22
22
|
for j, suffixs in enumerate(suffixs_list):
|
|
23
23
|
for _, suffix in enumerate(suffixs):
|
|
24
|
-
table[f"{'.'.join([prefix,suffix])}"] = s_mime_types[j]
|
|
24
|
+
table[f"{'.'.join([prefix, suffix])}"] = s_mime_types[j]
|
|
25
25
|
table[f"{suffix}"] = s_mime_types[j]
|
|
26
26
|
|
|
27
27
|
for ext, type in table.items():
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Standard Library
|
|
3
|
+
import errno
|
|
4
|
+
import getpass
|
|
5
|
+
from typing import List
|
|
6
|
+
|
|
7
|
+
import keyring
|
|
8
|
+
import requests
|
|
9
|
+
import yarl
|
|
10
|
+
from nubia import argument, command, context
|
|
11
|
+
from requests import HTTPError
|
|
12
|
+
from seqslab.auth.commands import BaseAuth as Auth
|
|
13
|
+
from seqslab.auth.utils import get_org
|
|
14
|
+
from seqslab.organization.resource.base import BaseResource
|
|
15
|
+
from termcolor import cprint
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
Copyright (C) 2023, Atgenomix Incorporated.
|
|
19
|
+
|
|
20
|
+
All Rights Reserved.
|
|
21
|
+
|
|
22
|
+
This program is an unpublished copyrighted work which is proprietary to
|
|
23
|
+
Atgenomix Incorporated and contains confidential information that is not to
|
|
24
|
+
be reproduced or disclosed to any other person or entity without prior
|
|
25
|
+
written consent from Atgenomix, Inc. in each and every instance.
|
|
26
|
+
|
|
27
|
+
Unauthorized reproduction of this program as well as unauthorized
|
|
28
|
+
preparation of derivative works based upon the program or distribution of
|
|
29
|
+
copies by sale, rental, lease or lending are violations of federal copyright
|
|
30
|
+
laws and state trade secret laws, punishable by civil and criminal penalties.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class BaseOrg:
|
|
35
|
+
@command
|
|
36
|
+
def list(self, **kwargs) -> int:
|
|
37
|
+
"""
|
|
38
|
+
List organizations to which the current user belongs
|
|
39
|
+
"""
|
|
40
|
+
ctx = context.get_context()
|
|
41
|
+
backend = ctx.args.backend
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
token = Auth.get_token().get("tokens").get("access")
|
|
45
|
+
organizations = (
|
|
46
|
+
BaseResource.request_wrapper(
|
|
47
|
+
callback=requests.get,
|
|
48
|
+
url=Auth.PROFILE_URL.format(backend=backend),
|
|
49
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
50
|
+
status=[requests.codes.ok],
|
|
51
|
+
)
|
|
52
|
+
.json()
|
|
53
|
+
.get("organizations")
|
|
54
|
+
)
|
|
55
|
+
for index, org_id in enumerate(organizations):
|
|
56
|
+
values = organizations[org_id]
|
|
57
|
+
cprint(
|
|
58
|
+
f"{values['name']} ({org_id})",
|
|
59
|
+
"yellow",
|
|
60
|
+
)
|
|
61
|
+
except HTTPError as e:
|
|
62
|
+
cprint(str(e), "red")
|
|
63
|
+
return errno.EPROTO
|
|
64
|
+
else:
|
|
65
|
+
return 0
|
|
66
|
+
|
|
67
|
+
@command
|
|
68
|
+
@argument(
|
|
69
|
+
"id",
|
|
70
|
+
type=str,
|
|
71
|
+
positional=False,
|
|
72
|
+
description="Specify an organization ID (required).",
|
|
73
|
+
)
|
|
74
|
+
def switch(self, id, **kwargs) -> int:
|
|
75
|
+
"""
|
|
76
|
+
Switch organizations
|
|
77
|
+
"""
|
|
78
|
+
ctx = context.get_context()
|
|
79
|
+
backend = ctx.args.backend
|
|
80
|
+
proxy = ctx.args.proxy
|
|
81
|
+
user = getpass.getuser()
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
token = Auth.get_token().get("tokens").get("access")
|
|
85
|
+
resp = BaseResource.request_wrapper(
|
|
86
|
+
callback=requests.get,
|
|
87
|
+
url=f"{Auth.AUTHORIZATION_URL.format(backend=backend)}{id}/",
|
|
88
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
89
|
+
status=[requests.codes.ok],
|
|
90
|
+
).json()
|
|
91
|
+
|
|
92
|
+
keyring.set_password(
|
|
93
|
+
"net.seqslab.api.tokens.refresh", user, resp["tokens"]["refresh"]
|
|
94
|
+
)
|
|
95
|
+
keyring.set_password(
|
|
96
|
+
"net.seqslab.api.tokens.access", user, resp["tokens"]["access"]
|
|
97
|
+
)
|
|
98
|
+
token = resp["tokens"]["access"]
|
|
99
|
+
|
|
100
|
+
org_info = get_org(
|
|
101
|
+
access_token=token, cus_id=Auth._decode(token)["cus_id"], proxy=proxy
|
|
102
|
+
)
|
|
103
|
+
cprint(
|
|
104
|
+
f"The organization you are currently signed into: {org_info['name']}.",
|
|
105
|
+
"yellow",
|
|
106
|
+
)
|
|
107
|
+
except HTTPError as e:
|
|
108
|
+
cprint(str(e), "red")
|
|
109
|
+
return errno.EPROTO
|
|
110
|
+
else:
|
|
111
|
+
return 0
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@command
|
|
115
|
+
class Org(BaseOrg):
|
|
116
|
+
"""Organization commands"""
|
|
117
|
+
|
|
118
|
+
def __init__(self):
|
|
119
|
+
super().__init__()
|
|
@@ -149,12 +149,11 @@ class BaseUser:
|
|
|
149
149
|
if email.find("@") == -1:
|
|
150
150
|
cprint("Please give a legitimate email", "red")
|
|
151
151
|
return errno.ENOENT
|
|
152
|
-
|
|
153
152
|
ret = backend.add_user(
|
|
154
153
|
email=email,
|
|
155
154
|
roles=roles,
|
|
156
155
|
active=not deactivate,
|
|
157
|
-
name=name if
|
|
156
|
+
name=name if name else email.split("@")[0],
|
|
158
157
|
)
|
|
159
158
|
if isinstance(ret, int):
|
|
160
159
|
return ret
|
|
@@ -214,7 +213,7 @@ class BaseUser:
|
|
|
214
213
|
if roles:
|
|
215
214
|
payload["roles"] = roles
|
|
216
215
|
if name:
|
|
217
|
-
payload["
|
|
216
|
+
payload["username"] = name
|
|
218
217
|
if activate and not deactivate:
|
|
219
218
|
payload["is_active"] = True
|
|
220
219
|
elif deactivate and not activate:
|
|
@@ -37,7 +37,7 @@ class AzureResource(BaseResource):
|
|
|
37
37
|
|
|
38
38
|
@exception_handler
|
|
39
39
|
def admin_consent(self):
|
|
40
|
-
if self.
|
|
40
|
+
if self.is_global_or_org_admin():
|
|
41
41
|
token = BaseAuth.get_token().get("attrs")
|
|
42
42
|
return AzureResource.CONSENT_URL.format(
|
|
43
43
|
tenant=token.get("tid"),
|
|
@@ -109,12 +109,11 @@ class BaseResource:
|
|
|
109
109
|
|
|
110
110
|
@exception_handler
|
|
111
111
|
@retry(stop=stop_after_attempt(3), wait=wait_fixed(2), reraise=True)
|
|
112
|
-
def
|
|
112
|
+
def is_global_or_org_admin(self, **kwargs) -> bool:
|
|
113
113
|
token = BaseAuth.get_token()
|
|
114
114
|
response = self.get_user(token.get("attrs").get("user_id"))
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
]:
|
|
115
|
+
roles = [item.get("name") for item in response.get("roles")]
|
|
116
|
+
if "Global administrator" in roles or "Organization administrator" in roles:
|
|
118
117
|
return True
|
|
119
118
|
|
|
120
119
|
return False
|
|
@@ -191,7 +191,7 @@ class BaseResource(ABC):
|
|
|
191
191
|
backend = ctx.args.backend
|
|
192
192
|
token = BaseAuth.get_token().get("tokens").get("access")
|
|
193
193
|
with requests.get(
|
|
194
|
-
url=f"{self.WES_RUNS_STATUS_URL.format(id=run_id,backend=backend)}",
|
|
194
|
+
url=f"{self.WES_RUNS_STATUS_URL.format(id=run_id, backend=backend)}",
|
|
195
195
|
headers={"Authorization": f"Bearer {token}"},
|
|
196
196
|
) as response:
|
|
197
197
|
if response.status_code not in [requests.codes.ok]:
|
|
@@ -270,7 +270,7 @@ class BaseResource(ABC):
|
|
|
270
270
|
backend = ctx.args.backend
|
|
271
271
|
token = BaseAuth.get_token().get("tokens").get("access")
|
|
272
272
|
with requests.get(
|
|
273
|
-
url=f"{self.WES_RUNS_FILE_URL.format(id=run_id,backend=backend)}",
|
|
273
|
+
url=f"{self.WES_RUNS_FILE_URL.format(id=run_id, backend=backend)}",
|
|
274
274
|
headers={"Authorization": f"Bearer {token}"},
|
|
275
275
|
) as response:
|
|
276
276
|
if response.status_code not in [requests.codes.ok]:
|
|
@@ -17,6 +17,7 @@ python/seqslab/usage_logger.py
|
|
|
17
17
|
python/seqslab/auth/__init__.py
|
|
18
18
|
python/seqslab/auth/azuread.py
|
|
19
19
|
python/seqslab/auth/commands.py
|
|
20
|
+
python/seqslab/auth/utils.py
|
|
20
21
|
python/seqslab/drs/__init__.py
|
|
21
22
|
python/seqslab/drs/commands.py
|
|
22
23
|
python/seqslab/drs/api/__init__.py
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# Standard Library
|
|
3
|
-
import errno
|
|
4
|
-
from typing import List
|
|
5
|
-
|
|
6
|
-
import requests
|
|
7
|
-
import yarl
|
|
8
|
-
from nubia import argument, command
|
|
9
|
-
from requests import HTTPError
|
|
10
|
-
from seqslab.auth.commands import BaseAuth as Auth
|
|
11
|
-
from seqslab.organization.resource.base import BaseResource
|
|
12
|
-
from termcolor import cprint
|
|
13
|
-
|
|
14
|
-
"""
|
|
15
|
-
Copyright (C) 2023, Atgenomix Incorporated.
|
|
16
|
-
|
|
17
|
-
All Rights Reserved.
|
|
18
|
-
|
|
19
|
-
This program is an unpublished copyrighted work which is proprietary to
|
|
20
|
-
Atgenomix Incorporated and contains confidential information that is not to
|
|
21
|
-
be reproduced or disclosed to any other person or entity without prior
|
|
22
|
-
written consent from Atgenomix, Inc. in each and every instance.
|
|
23
|
-
|
|
24
|
-
Unauthorized reproduction of this program as well as unauthorized
|
|
25
|
-
preparation of derivative works based upon the program or distribution of
|
|
26
|
-
copies by sale, rental, lease or lending are violations of federal copyright
|
|
27
|
-
laws and state trade secret laws, punishable by civil and criminal penalties.
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class BaseOrg:
|
|
32
|
-
@command
|
|
33
|
-
@argument(
|
|
34
|
-
"id",
|
|
35
|
-
type=str,
|
|
36
|
-
positional=False,
|
|
37
|
-
description="Specify an organization ID (required).",
|
|
38
|
-
)
|
|
39
|
-
@argument(
|
|
40
|
-
"uri",
|
|
41
|
-
type=List[str],
|
|
42
|
-
positional=False,
|
|
43
|
-
description="Specify the redirect URI for auth code flow (optional). If no URI is provided, "
|
|
44
|
-
"all existing registered URIs will be removed.",
|
|
45
|
-
)
|
|
46
|
-
def redirect_uri(self, id: str, uri: List[str] = [], **kwargs) -> int:
|
|
47
|
-
"""
|
|
48
|
-
Register redirect URI(s) for auth code flow.
|
|
49
|
-
"""
|
|
50
|
-
for u in uri:
|
|
51
|
-
o = yarl.URL(u)
|
|
52
|
-
if not o.scheme or not o.host:
|
|
53
|
-
cprint(f"Invalid URI '{u}'", "red")
|
|
54
|
-
return -1
|
|
55
|
-
|
|
56
|
-
try:
|
|
57
|
-
token = Auth.get_token().get("tokens").get("access")
|
|
58
|
-
BaseResource.request_wrapper(
|
|
59
|
-
callback=requests.post,
|
|
60
|
-
url=BaseResource.MGMT_ORG_URL.format(organization=id),
|
|
61
|
-
headers={"Authorization": f"Bearer {token}"},
|
|
62
|
-
data={"redirect_uris": uri},
|
|
63
|
-
status=[requests.codes.ok],
|
|
64
|
-
)
|
|
65
|
-
except HTTPError as e:
|
|
66
|
-
cprint(str(e), "red")
|
|
67
|
-
return errno.EPROTO
|
|
68
|
-
else:
|
|
69
|
-
return 0
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@command
|
|
73
|
-
class Org(BaseOrg):
|
|
74
|
-
"""Organization commands"""
|
|
75
|
-
|
|
76
|
-
def __init__(self):
|
|
77
|
-
super().__init__()
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|