seqslab-cli 3.3.1__tar.gz → 3.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/LICENSE +0 -3
  2. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/PKG-INFO +4 -4
  3. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/README.md +3 -3
  4. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/__init__.py +3 -7
  5. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/auth/azuread.py +50 -37
  6. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/auth/commands.py +138 -84
  7. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/cli.py +5 -6
  8. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/context.py +1 -3
  9. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/drs/__init__.py +1 -3
  10. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/drs/api/azure.py +185 -89
  11. seqslab-cli-3.3.2/python/seqslab/drs/api/base.py +453 -0
  12. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/drs/api/common.py +1 -1
  13. seqslab-cli-3.3.2/python/seqslab/drs/api/template.py +149 -0
  14. seqslab-cli-3.3.2/python/seqslab/drs/commands.py +1457 -0
  15. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/drs/internal/aiocopy.py +240 -158
  16. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/drs/internal/common.py +1 -1
  17. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/drs/internal/utils.py +40 -23
  18. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/drs/storage/azure.py +317 -187
  19. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/drs/storage/base.py +136 -124
  20. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/drs/utils/atgxmetadata.py +63 -58
  21. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/drs/utils/biomimetype.py +9 -9
  22. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/drs/utils/progressbar.py +40 -22
  23. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/exceptions.py +4 -2
  24. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/organization/__init__.py +1 -4
  25. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/organization/commands.py +19 -17
  26. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/organization/resource/base.py +10 -10
  27. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/plugin.py +29 -26
  28. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/role/__init__.py +1 -3
  29. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/role/commands.py +1 -2
  30. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/role/internal/common.py +1 -1
  31. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/role/resource/base.py +18 -13
  32. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/runsheet/runsheet.py +71 -47
  33. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/scr/__init__.py +1 -3
  34. seqslab-cli-3.3.2/python/seqslab/scr/commands.py +232 -0
  35. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/scr/internal/common.py +2 -2
  36. {seqslab-cli-3.3.1/python/seqslab/trs → seqslab-cli-3.3.2/python/seqslab/scr}/resource/azure.py +2 -1
  37. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/scr/resource/base.py +37 -30
  38. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/settings.py +2 -2
  39. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/statusbar.py +3 -5
  40. seqslab-cli-3.3.2/python/seqslab/trs/__init__.py +13 -0
  41. seqslab-cli-3.3.2/python/seqslab/trs/commands.py +705 -0
  42. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/trs/internal/utils.py +5 -5
  43. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/trs/register/azure.py +11 -7
  44. seqslab-cli-3.3.2/python/seqslab/trs/register/base.py +300 -0
  45. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/trs/register/common.py +2 -3
  46. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/trs/register/template.py +82 -45
  47. {seqslab-cli-3.3.1/python/seqslab/workspace → seqslab-cli-3.3.2/python/seqslab/trs}/resource/azure.py +2 -1
  48. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/trs/resource/base.py +69 -56
  49. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/trs/resource/common.py +2 -3
  50. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/trs/template/base.py +82 -44
  51. seqslab-cli-3.3.2/python/seqslab/trs/template/template.py +85 -0
  52. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/user/__init__.py +1 -3
  53. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/user/commands.py +113 -75
  54. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/user/internal/common.py +2 -2
  55. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/user/resource/azure.py +14 -8
  56. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/user/resource/base.py +34 -23
  57. seqslab-cli-3.3.2/python/seqslab/wes/__init__.py +13 -0
  58. seqslab-cli-3.3.2/python/seqslab/wes/commands.py +739 -0
  59. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/wes/internal/common.py +2 -3
  60. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/wes/internal/parameters.py +91 -52
  61. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/wes/resource/azure.py +2 -1
  62. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/wes/resource/base.py +110 -79
  63. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/wes/template/base.py +39 -25
  64. seqslab-cli-3.3.2/python/seqslab/wes/template/template.py +91 -0
  65. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/workspace/__init__.py +1 -3
  66. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/workspace/commands.py +90 -57
  67. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/workspace/internal/common.py +2 -2
  68. seqslab-cli-3.3.2/python/seqslab/workspace/resource/__init__.py +0 -0
  69. {seqslab-cli-3.3.1/python/seqslab/scr → seqslab-cli-3.3.2/python/seqslab/workspace}/resource/azure.py +2 -1
  70. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/workspace/resource/base.py +33 -23
  71. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab_cli.egg-info/PKG-INFO +4 -4
  72. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/setup.py +37 -29
  73. seqslab-cli-3.3.1/python/seqslab/drs/api/base.py +0 -379
  74. seqslab-cli-3.3.1/python/seqslab/drs/api/template.py +0 -126
  75. seqslab-cli-3.3.1/python/seqslab/drs/commands.py +0 -1109
  76. seqslab-cli-3.3.1/python/seqslab/drs/utils/__init__.py +0 -2
  77. seqslab-cli-3.3.1/python/seqslab/scr/commands.py +0 -206
  78. seqslab-cli-3.3.1/python/seqslab/trs/__init__.py +0 -15
  79. seqslab-cli-3.3.1/python/seqslab/trs/commands.py +0 -550
  80. seqslab-cli-3.3.1/python/seqslab/trs/register/base.py +0 -244
  81. seqslab-cli-3.3.1/python/seqslab/trs/template/template.py +0 -79
  82. seqslab-cli-3.3.1/python/seqslab/wes/__init__.py +0 -15
  83. seqslab-cli-3.3.1/python/seqslab/wes/commands.py +0 -569
  84. seqslab-cli-3.3.1/python/seqslab/wes/template/template.py +0 -88
  85. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/MANIFEST.in +0 -0
  86. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/auth/__init__.py +0 -0
  87. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/drs/api/__init__.py +0 -0
  88. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/drs/internal/__init__.py +0 -0
  89. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/drs/storage/__init__.py +0 -0
  90. {seqslab-cli-3.3.1/python/seqslab/organization/resource → seqslab-cli-3.3.2/python/seqslab/drs/utils}/__init__.py +0 -0
  91. {seqslab-cli-3.3.1/python/seqslab/role/internal → seqslab-cli-3.3.2/python/seqslab/organization/resource}/__init__.py +0 -0
  92. {seqslab-cli-3.3.1/python/seqslab/role/resource → seqslab-cli-3.3.2/python/seqslab/role/internal}/__init__.py +0 -0
  93. {seqslab-cli-3.3.1/python/seqslab/runsheet → seqslab-cli-3.3.2/python/seqslab/role/resource}/__init__.py +0 -0
  94. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/role/resource/azure.py +0 -0
  95. {seqslab-cli-3.3.1/python/seqslab/scr/internal → seqslab-cli-3.3.2/python/seqslab/runsheet}/__init__.py +0 -0
  96. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/sample_sheet/__init__.py +0 -0
  97. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/sample_sheet/_version.py +0 -0
  98. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/sample_sheet/util.py +0 -0
  99. {seqslab-cli-3.3.1/python/seqslab/scr/resource → seqslab-cli-3.3.2/python/seqslab/scr/internal}/__init__.py +0 -0
  100. {seqslab-cli-3.3.1/python/seqslab/trs/internal → seqslab-cli-3.3.2/python/seqslab/scr/resource}/__init__.py +0 -0
  101. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/session_logger.py +0 -0
  102. {seqslab-cli-3.3.1/python/seqslab/trs/register → seqslab-cli-3.3.2/python/seqslab/trs/internal}/__init__.py +0 -0
  103. {seqslab-cli-3.3.1/python/seqslab/trs/resource → seqslab-cli-3.3.2/python/seqslab/trs/register}/__init__.py +0 -0
  104. {seqslab-cli-3.3.1/python/seqslab/trs/template → seqslab-cli-3.3.2/python/seqslab/trs/resource}/__init__.py +0 -0
  105. {seqslab-cli-3.3.1/python/seqslab/user/internal → seqslab-cli-3.3.2/python/seqslab/trs/template}/__init__.py +0 -0
  106. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab/usage_logger.py +0 -0
  107. {seqslab-cli-3.3.1/python/seqslab/user/resource → seqslab-cli-3.3.2/python/seqslab/user/internal}/__init__.py +0 -0
  108. {seqslab-cli-3.3.1/python/seqslab/wes/internal → seqslab-cli-3.3.2/python/seqslab/user/resource}/__init__.py +0 -0
  109. {seqslab-cli-3.3.1/python/seqslab/wes/resource → seqslab-cli-3.3.2/python/seqslab/wes/internal}/__init__.py +0 -0
  110. {seqslab-cli-3.3.1/python/seqslab/wes/template → seqslab-cli-3.3.2/python/seqslab/wes/resource}/__init__.py +0 -0
  111. {seqslab-cli-3.3.1/python/seqslab/workspace/internal → seqslab-cli-3.3.2/python/seqslab/wes/template}/__init__.py +0 -0
  112. {seqslab-cli-3.3.1/python/seqslab/workspace/resource → seqslab-cli-3.3.2/python/seqslab/workspace/internal}/__init__.py +0 -0
  113. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab_cli.egg-info/SOURCES.txt +0 -0
  114. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab_cli.egg-info/dependency_links.txt +0 -0
  115. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab_cli.egg-info/entry_points.txt +0 -0
  116. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab_cli.egg-info/requires.txt +0 -0
  117. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab_cli.egg-info/top_level.txt +0 -0
  118. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/python/seqslab_cli.egg-info/zip-safe +0 -0
  119. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/requirements.txt +0 -0
  120. {seqslab-cli-3.3.1 → seqslab-cli-3.3.2}/setup.cfg +0 -0
@@ -12,6 +12,3 @@ laws and state trade secret laws, punishable by civil and criminal penalties.
12
12
  date 2022/02/11
13
13
 
14
14
  version 3.0.0
15
-
16
-
17
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: seqslab-cli
3
- Version: 3.3.1
3
+ Version: 3.3.2
4
4
  Summary: Atgenomix SeqsLab Command Line Tool
5
5
  Home-page: https://github.com/AnomeGAP/seqslab-cli
6
6
  Author: Allen Chang
@@ -43,7 +43,7 @@ Description: <!-- PROJECT SHIELDS -->
43
43
  ### Prerequisites
44
44
 
45
45
  * Python 3.8 or later
46
-
46
+
47
47
  * [keyring](https://pypi.org/project/keyring/)
48
48
  * [dbus](https://wiki.freedesktop.org/www/Software/dbus/)
49
49
  * [glib](https://docs.gtk.org/glib/)
@@ -75,7 +75,7 @@ Description: <!-- PROJECT SHIELDS -->
75
75
  ```bash
76
76
  root> exit
77
77
  ```
78
-
78
+
79
79
  ### CLI Mode
80
80
 
81
81
  Through the use of subcommands, you can operate the SeqsLab CLI like any traditional Linux-based command-line utility.
@@ -89,7 +89,7 @@ Description: <!-- PROJECT SHIELDS -->
89
89
  ### Basic Commands
90
90
  #### Authentication with the SeqsLab API
91
91
 
92
- Regardless of the mode that you intend to use, you must sign in to authenticate the session. The SeqsLab CLI follows the OAuth 2.0 Device Authorization Grant (external URL) process.
92
+ Regardless of the mode that you intend to use, you must sign in to authenticate the session. The SeqsLab CLI follows the OAuth 2.0 Device Authorization Grant (external URL) process.
93
93
 
94
94
  Example (interactive mode):
95
95
 
@@ -33,7 +33,7 @@ This package provides a unified command line interface to Atgenomix SeqsLab, a c
33
33
  ### Prerequisites
34
34
 
35
35
  * Python 3.8 or later
36
-
36
+
37
37
  * [keyring](https://pypi.org/project/keyring/)
38
38
  * [dbus](https://wiki.freedesktop.org/www/Software/dbus/)
39
39
  * [glib](https://docs.gtk.org/glib/)
@@ -65,7 +65,7 @@ This mode provides fish-style auto-completion functionality that is user-friendl
65
65
  ```bash
66
66
  root> exit
67
67
  ```
68
-
68
+
69
69
  ### CLI Mode
70
70
 
71
71
  Through the use of subcommands, you can operate the SeqsLab CLI like any traditional Linux-based command-line utility.
@@ -79,7 +79,7 @@ Through the use of subcommands, you can operate the SeqsLab CLI like any traditi
79
79
  ### Basic Commands
80
80
  #### Authentication with the SeqsLab API
81
81
 
82
- Regardless of the mode that you intend to use, you must sign in to authenticate the session. The SeqsLab CLI follows the OAuth 2.0 Device Authorization Grant (external URL) process.
82
+ Regardless of the mode that you intend to use, you must sign in to authenticate the session. The SeqsLab CLI follows the OAuth 2.0 Device Authorization Grant (external URL) process.
83
83
 
84
84
  Example (interactive mode):
85
85
 
@@ -18,13 +18,9 @@ laws and state trade secret laws, punishable by civil and criminal penalties.
18
18
 
19
19
  name = "seqslab"
20
20
 
21
- __all__ = [
21
+ __all__ = []
22
22
 
23
- ]
24
23
 
24
+ __version__ = "3.3.2"
25
25
 
26
- __version__ = "3.3.1"
27
-
28
- LOGGING = {
29
- "DIR_PATH": "/var/log/seqslab"
30
- }
26
+ LOGGING = {"DIR_PATH": "/var/log/seqslab"}
@@ -1,53 +1,64 @@
1
1
  from __future__ import absolute_import, unicode_literals
2
2
 
3
- from typing import TypeVar
4
-
5
- import msal
6
- import environ
3
+ # Standard Library
7
4
  import atexit
8
5
  import pickle
6
+ from typing import TypeVar
7
+ from urllib.parse import parse_qs
9
8
 
10
- from pydantic import BaseModel
11
-
9
+ import environ
10
+ import msal
12
11
  import seqslab
13
- from urllib.parse import parse_qs
14
- from nubia import context
15
12
  from msal.oauth2cli.authcode import (
16
- _AuthCodeHandler,
17
13
  AuthCodeReceiver,
18
- _AuthCodeHttpServer6,
14
+ _AuthCodeHandler,
19
15
  _AuthCodeHttpServer,
20
- _qs2kv)
16
+ _AuthCodeHttpServer6,
17
+ _qs2kv,
18
+ )
19
+ from nubia import context
20
+ from pydantic import BaseModel
21
21
 
22
22
  env = environ.Env()
23
23
  environ.Env.read_env("/etc/seqslab/cli_apps.env")
24
24
 
25
25
  PRIVATE_NAME = env.str("PRIVATE_NAME", "api")
26
26
  API_HOSTNAME = f"{PRIVATE_NAME}.seqslab.net"
27
- SOCIAL_AUTH_AZURE_KEY = env.str("AZUREAD_OAUTH2_KEY", "b10403db-7700-42c2-996e-116578438579")
27
+ SOCIAL_AUTH_AZURE_KEY = env.str(
28
+ "AZUREAD_OAUTH2_KEY", "b10403db-7700-42c2-996e-116578438579"
29
+ )
28
30
  SOCIAL_AUTH_AZURE_TENANT_ID = env.str("AZUREAD_OAUTH2_TENANT", "organizations")
29
- SOCIAL_AUTH_AZURE_SCOPE = env.json("AZUREAD_OAUTH2_SCOPE",
30
- ["email offline_access "
31
- "https://management.azure.com/user_impersonation"])
32
- SOCIAL_AUTH_AZURE_SCOPE_APP = env.json("AZUREAD_OAUTH2_SCOPE_APP",
33
- {"management": ['https://management.azure.com/.default'],
34
- "storage": ['https://storage.azure.com/.default']})
31
+ SOCIAL_AUTH_AZURE_SCOPE = env.json(
32
+ "AZUREAD_OAUTH2_SCOPE",
33
+ ["email offline_access " "https://management.azure.com/user_impersonation"],
34
+ )
35
+ SOCIAL_AUTH_AZURE_SCOPE_APP = env.json(
36
+ "AZUREAD_OAUTH2_SCOPE_APP",
37
+ {
38
+ "management": ["https://management.azure.com/.default"],
39
+ "storage": ["https://storage.azure.com/.default"],
40
+ },
41
+ )
35
42
 
36
43
  http_cache = f"/tmp/{seqslab.name}.{seqslab.__version__}.http_cache"
37
44
  try:
38
45
  with open(http_cache, "rb") as f:
39
46
  persisted_http_cache = pickle.load(f) # Take a snapshot
40
47
  except (
41
- FileNotFoundError,
42
- pickle.UnpicklingError, # A corrupted http cache file
48
+ FileNotFoundError,
49
+ pickle.UnpicklingError, # A corrupted http cache file
43
50
  ):
44
51
  persisted_http_cache = {} # Recover by starting afresh
45
52
 
46
- atexit.register(lambda: pickle.dump(
47
- # When exit, flush it back to the file.
48
- # It may occasionally overwrite another process's concurrent write,
49
- # but that is fine. Subsequent runs will reach eventual consistency.
50
- persisted_http_cache, open(http_cache, "wb")))
53
+ atexit.register(
54
+ lambda: pickle.dump(
55
+ # When exit, flush it back to the file.
56
+ # It may occasionally overwrite another process's concurrent write,
57
+ # but that is fine. Subsequent runs will reach eventual consistency.
58
+ persisted_http_cache,
59
+ open(http_cache, "wb"),
60
+ )
61
+ )
51
62
 
52
63
  Client = TypeVar("Client", bound=msal.ClientApplication)
53
64
 
@@ -72,7 +83,8 @@ class ClientApp(AuthBaseModel):
72
83
  app_name=seqslab.name,
73
84
  app_version=seqslab.__version__,
74
85
  proxies=proxies,
75
- http_cache=persisted_http_cache)
86
+ http_cache=persisted_http_cache,
87
+ )
76
88
  return self.confidential_app
77
89
  else:
78
90
  if not self.public_app:
@@ -82,7 +94,8 @@ class ClientApp(AuthBaseModel):
82
94
  app_name=seqslab.name,
83
95
  app_version=seqslab.__version__,
84
96
  proxies=proxies,
85
- http_cache=persisted_http_cache)
97
+ http_cache=persisted_http_cache,
98
+ )
86
99
  return self.public_app
87
100
 
88
101
 
@@ -90,24 +103,26 @@ app = ClientApp()
90
103
 
91
104
 
92
105
  class _AuthCodeHandlerEx(_AuthCodeHandler):
93
-
94
106
  def do_POST(self):
95
- clen = int(self.headers.get('Content-Length'))
107
+ clen = int(self.headers.get("Content-Length"))
96
108
  body = self.rfile.read(clen)
97
- qs = parse_qs(body.decode('utf-8'))
98
- if qs.get('code') or qs.get("error"):
109
+ qs = parse_qs(body.decode("utf-8"))
110
+ if qs.get("code") or qs.get("error"):
99
111
  self.server.auth_response = _qs2kv(qs)
100
- template = (self.server.success_template
101
- if "code" in qs else self.server.error_template)
112
+ template = (
113
+ self.server.success_template
114
+ if "code" in qs
115
+ else self.server.error_template
116
+ )
102
117
  self._send_full_response(
103
- template.safe_substitute(**self.server.auth_response))
118
+ template.safe_substitute(**self.server.auth_response)
119
+ )
104
120
  # NOTE: Don't do self.server.shutdown() here. It'll halt the server.
105
121
  else:
106
122
  self._send_full_response(self.server.welcome_page)
107
123
 
108
124
 
109
125
  class AuthCodeReceiverEx(AuthCodeReceiver):
110
-
111
126
  def __init__(self, port=None):
112
127
  """
113
128
  Create a Receiver waiting for incoming auth response.
@@ -118,5 +133,3 @@ class AuthCodeReceiverEx(AuthCodeReceiver):
118
133
  address = "127.0.0.1"
119
134
  Server = _AuthCodeHttpServer6 if ":" in address else _AuthCodeHttpServer
120
135
  self._server = Server((address, port or 0), _AuthCodeHandlerEx)
121
-
122
-
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env python3
2
+ # Standard Library
2
3
  import errno
3
4
  import getpass
4
5
  import json
@@ -13,14 +14,10 @@ import jwt
13
14
  import keyring
14
15
  import requests
15
16
  from keyring.errors import PasswordDeleteError
16
- from nubia import argument
17
- from nubia import command
18
- from nubia import context
19
- from requests import HTTPError
20
- from requests import request
17
+ from nubia import argument, command, context
18
+ from requests import HTTPError, request
21
19
  from seqslab import settings as api_settings
22
- from seqslab.auth import aad_app
23
- from seqslab.auth import azuread, __version__
20
+ from seqslab.auth import __version__, aad_app, azuread
24
21
  from seqslab.auth.azuread import API_HOSTNAME
25
22
  from termcolor import cprint
26
23
 
@@ -55,18 +52,26 @@ class BaseAuth:
55
52
  return response
56
53
 
57
54
  def _signin_azure_silent(
58
- self, credential: str, assertion: str, scope: str, backend: str, proxy: str = None
55
+ self,
56
+ credential: str,
57
+ assertion: str,
58
+ scope: str,
59
+ backend: str,
60
+ proxy: str = None,
59
61
  ) -> dict:
60
62
  scopes = azuread.SOCIAL_AUTH_AZURE_SCOPE_APP.get(scope)
61
63
  client = aad_app.load_client(credential=credential)
62
64
  result = client.acquire_token_silent(scopes, account=None)
63
65
  if not result:
64
- logging.info("No suitable token exists in cache. Let's get a new one from AAD.")
66
+ logging.info(
67
+ "No suitable token exists in cache. Let's get a new one from AAD."
68
+ )
65
69
  result = client.acquire_token_for_client(scopes=scopes)
66
70
 
67
71
  if "error" in result:
68
- raise PermissionError("{}: {}".format(result["error"],
69
- result["error_description"]))
72
+ raise PermissionError(
73
+ "{}: {}".format(result["error"], result["error_description"])
74
+ )
70
75
 
71
76
  result.update({"assertion": assertion, "scope": " ".join(scopes)})
72
77
  return self._request(
@@ -74,9 +79,12 @@ class BaseAuth:
74
79
  method=self.ACCESS_TOKEN_METHOD,
75
80
  params={"secure": True},
76
81
  data=result,
77
- headers={"Content-Type": "application/x-www-form-urlencoded",
78
- "Accept": "application/json"},
79
- proxies=proxy).json()
82
+ headers={
83
+ "Content-Type": "application/x-www-form-urlencoded",
84
+ "Accept": "application/json",
85
+ },
86
+ proxies=proxy,
87
+ ).json()
80
88
 
81
89
  def _signin_azure(
82
90
  self, device_code: bool, daemon: bool, backend: str, proxy: str = None
@@ -87,7 +95,9 @@ class BaseAuth:
87
95
  client = aad_app.load_client()
88
96
  accounts = client.get_accounts()
89
97
  if accounts:
90
- logging.info("Azure AD account(s) exists in cache and proceed with token cache.")
98
+ logging.info(
99
+ "Azure AD account(s) exists in cache and proceed with token cache."
100
+ )
91
101
 
92
102
  if 1 == len(accounts):
93
103
  chosen = accounts[0]
@@ -102,38 +112,50 @@ class BaseAuth:
102
112
  result = client.acquire_token_silent(scopes=scopes, account=chosen)
103
113
 
104
114
  if result is None:
105
- logging.info("No suitable token exists in cache. You must get a new one from AAD.")
115
+ logging.info(
116
+ "No suitable token exists in cache. You must get a new one from AAD."
117
+ )
106
118
 
107
119
  if not device_code:
108
120
  # obtain authorization uri
109
121
  auth_uri = (
110
- self._request(self.AUTHORIZATION_URL.format(backend=backend),
111
- method=self.ACCESS_TOKEN_METHOD,
112
- headers={"Content-Type": "application/x-www-form-urlencoded",
113
- "Accept": "application/json"},
114
- params={"format": "json"},
115
- proxies=proxy)
116
- .json()
117
- .get("url")
122
+ self._request(
123
+ self.AUTHORIZATION_URL.format(backend=backend),
124
+ method=self.ACCESS_TOKEN_METHOD,
125
+ headers={
126
+ "Content-Type": "application/x-www-form-urlencoded",
127
+ "Accept": "application/json",
128
+ },
129
+ params={"format": "json"},
130
+ proxies=proxy,
131
+ )
132
+ .json()
133
+ .get("url")
118
134
  )
119
135
 
120
136
  with azuread.AuthCodeReceiverEx(port=0) as receiver:
121
137
  # hardcode http://localhost here as msal also hardcodes that
122
138
  redirect_uri = f"http://localhost:{receiver.get_port()}"
123
- auth_uri = re.sub(r"(redirect_uri=)[^&]+",
124
- r"\1" + quote(redirect_uri, safe=""),
125
- auth_uri)
139
+ auth_uri = re.sub(
140
+ r"(redirect_uri=)[^&]+",
141
+ r"\1" + quote(redirect_uri, safe=""),
142
+ auth_uri,
143
+ )
126
144
  auth_uri = re.sub(r"prompt=[^&]+&", "", auth_uri)
127
145
  result = receiver.get_auth_response(
128
146
  auth_uri=auth_uri,
129
147
  timeout=60,
130
148
  success_template="You have signed in with your Microsoft "
131
- "account on your device. "
132
- "You may now close this window.")
149
+ "account on your device. "
150
+ "You may now close this window.",
151
+ )
133
152
 
134
153
  if "error" in result:
135
- raise PermissionError("{}: {}".format(
136
- result["error"], result["error_description"]))
154
+ raise PermissionError(
155
+ "{}: {}".format(
156
+ result["error"], result["error_description"]
157
+ )
158
+ )
137
159
 
138
160
  token_uri = self.AUTHORIZATION_URL.format(backend=backend)
139
161
  result.update({"redirect_uri": redirect_uri})
@@ -141,31 +163,39 @@ class BaseAuth:
141
163
  flow = client.initiate_device_flow(scopes=scopes)
142
164
  if "user_code" not in flow:
143
165
  raise ValueError(
144
- "Unable to create device flow. Err: %s" % json.dumps(flow, indent=4))
166
+ "Unable to create device flow. Err: %s"
167
+ % json.dumps(flow, indent=4)
168
+ )
145
169
 
146
170
  cprint(flow["message"], "red")
147
171
  # input("Press Enter after signing in from another device to proceed, CTRL+C to abort.")
148
172
  result = client.acquire_token_by_device_flow(flow)
149
173
  if "error" in result:
150
- raise PermissionError("{}: {}".format(
151
- result["error"], result["error_description"]))
174
+ raise PermissionError(
175
+ "{}: {}".format(result["error"], result["error_description"])
176
+ )
152
177
 
153
178
  return self._request(
154
179
  token_uri,
155
180
  method=self.ACCESS_TOKEN_METHOD,
156
181
  params={"secure": True} if daemon else None,
157
182
  data=result,
158
- headers={"Content-Type": "application/x-www-form-urlencoded",
159
- "Accept": "application/json"},
160
- proxies=proxy).json()
183
+ headers={
184
+ "Content-Type": "application/x-www-form-urlencoded",
185
+ "Accept": "application/json",
186
+ },
187
+ proxies=proxy,
188
+ ).json()
161
189
 
162
190
  @staticmethod
163
191
  def _decode(token) -> dict:
164
- attrs = jwt.decode(token,
165
- key=api_settings.JWT_VERIFYING_KEY,
166
- algorithms=[api_settings.JWT_ALGORITHM],
167
- verify=True,
168
- options={"verify_aud": False, "verify_exp": False})
192
+ attrs = jwt.decode(
193
+ token,
194
+ key=api_settings.JWT_VERIFYING_KEY,
195
+ algorithms=[api_settings.JWT_ALGORITHM],
196
+ verify=True,
197
+ options={"verify_aud": False, "verify_exp": False},
198
+ )
169
199
  if "exp" not in attrs:
170
200
  attrs["exp"] = 0
171
201
  return attrs
@@ -184,47 +214,63 @@ class BaseAuth:
184
214
  try:
185
215
  attrs = Auth._decode(access)
186
216
  except jwt.exceptions.ExpiredSignatureError:
187
- Auth.tokens.update({"tokens": {"refresh": refresh, "access": None},
188
- "attrs": {"exp": 0}})
217
+ Auth.tokens.update(
218
+ {
219
+ "tokens": {"refresh": refresh, "access": None},
220
+ "attrs": {"exp": 0},
221
+ }
222
+ )
189
223
  else:
190
- Auth.tokens.update({"tokens": {"refresh": refresh, "access": access},
191
- "attrs": attrs})
224
+ Auth.tokens.update(
225
+ {"tokens": {"refresh": refresh, "access": access}, "attrs": attrs}
226
+ )
192
227
 
193
228
  if arrow.utcnow() >= arrow.get(Auth.tokens["attrs"]["exp"]):
194
229
  # expired, refresh token
195
230
  proxy = context.get_context().args.proxy
196
- with Auth._request(Auth.REFRESH_TOKEN_URL,
197
- method=Auth.ACCESS_TOKEN_METHOD,
198
- json={"refresh": Auth.tokens["tokens"]["refresh"]},
199
- headers={"Content-Type": "application/json",
200
- "Accept": "application/json"},
201
- proxies=proxy) as response:
231
+ with Auth._request(
232
+ Auth.REFRESH_TOKEN_URL,
233
+ method=Auth.ACCESS_TOKEN_METHOD,
234
+ json={"refresh": Auth.tokens["tokens"]["refresh"]},
235
+ headers={
236
+ "Content-Type": "application/json",
237
+ "Accept": "application/json",
238
+ },
239
+ proxies=proxy,
240
+ ) as response:
202
241
  if response.status_code != requests.codes.ok:
203
242
  Auth.tokens.clear()
204
243
  return None
205
244
 
206
245
  result = response.json()
207
- Auth.tokens['tokens'].update(result)
246
+ Auth.tokens["tokens"].update(result)
208
247
  Auth.tokens.update({"attrs": Auth._decode(result["access"])})
209
- keyring.set_password("net.seqslab.api.tokens.access", user,
210
- result["access"])
248
+ keyring.set_password(
249
+ "net.seqslab.api.tokens.access", user, result["access"]
250
+ )
211
251
 
212
252
  return Auth.tokens
213
253
 
214
254
  """Authentication command help"""
215
255
 
216
- @command(aliases=["login"],
217
- help="Authenticate to the platform and obtain API access token.")
218
- @argument("device_code",
219
- type=bool,
220
- positional=False,
221
- description="Use the device authorization grant flow (optional).",
222
- aliases=["i"])
223
- @argument("daemon",
224
- type=bool,
225
- positional=False,
226
- description="Sign in for a long-running, non-interactive daemon process (optional).",
227
- aliases=["d"])
256
+ @command(
257
+ aliases=["login"],
258
+ help="Authenticate to the platform and obtain API access token.",
259
+ )
260
+ @argument(
261
+ "device_code",
262
+ type=bool,
263
+ positional=False,
264
+ description="Use the device authorization grant flow (optional).",
265
+ aliases=["i"],
266
+ )
267
+ @argument(
268
+ "daemon",
269
+ type=bool,
270
+ positional=False,
271
+ description="Sign in for a long-running, non-interactive daemon process (optional).",
272
+ aliases=["d"],
273
+ )
228
274
  def signin(self, device_code: bool = False, daemon: bool = False) -> int:
229
275
  """
230
276
  Sign in to the configured authentication backend and obtain API access token.
@@ -244,10 +290,12 @@ class BaseAuth:
244
290
  result = method(device_code, daemon, backend, proxy)
245
291
  # store in keyring secret service
246
292
  user = getpass.getuser()
247
- keyring.set_password("net.seqslab.api.tokens.refresh", user,
248
- result["tokens"]["refresh"])
249
- keyring.set_password("net.seqslab.api.tokens.access", user,
250
- result["tokens"]["access"])
293
+ keyring.set_password(
294
+ "net.seqslab.api.tokens.refresh", user, result["tokens"]["refresh"]
295
+ )
296
+ keyring.set_password(
297
+ "net.seqslab.api.tokens.access", user, result["tokens"]["access"]
298
+ )
251
299
  return 0
252
300
  except ValueError:
253
301
  return errno.EINVAL
@@ -260,14 +308,18 @@ class BaseAuth:
260
308
  cprint(err, "red")
261
309
  return errno.ECANCELED
262
310
 
263
- @command(aliases=["daemon"],
264
- help="Authenticate silently to the platform and obtain API access token. "
265
- "This requires that the SeqsLab API app admin consent has been granted.")
266
- @argument("scope",
267
- type=str,
268
- positional=False,
269
- description="Specify the scope of the application permission requested (default = management).",
270
- choices=["management", "storage"])
311
+ @command(
312
+ aliases=["daemon"],
313
+ help="Authenticate silently to the platform and obtain API access token. "
314
+ "This requires that the SeqsLab API app admin consent has been granted.",
315
+ )
316
+ @argument(
317
+ "scope",
318
+ type=str,
319
+ positional=False,
320
+ description="Specify the scope of the application permission requested (default = management).",
321
+ choices=["management", "storage"],
322
+ )
271
323
  def daemon(self, scope: str = "management") -> int:
272
324
  """
273
325
  Sign in silently to the configured authentication backend and obtain API access token.
@@ -284,7 +336,7 @@ class BaseAuth:
284
336
  return errno.ENODEV
285
337
 
286
338
  if len(Auth.tokens):
287
- assertion = Auth.tokens["tokens"].get('access')
339
+ assertion = Auth.tokens["tokens"].get("access")
288
340
  credential = Auth.tokens["attrs"].get("scrt")
289
341
  else:
290
342
  # load from Keyring secret service
@@ -302,10 +354,12 @@ class BaseAuth:
302
354
  method = getattr(self, mname)
303
355
  result = method(credential, assertion, scope, backend, proxy)
304
356
  # store in keyring secret service
305
- keyring.set_password("net.seqslab.api.tokens.refresh", user,
306
- result["tokens"]["refresh"])
307
- keyring.set_password("net.seqslab.api.tokens.access", user,
308
- result["tokens"]["access"])
357
+ keyring.set_password(
358
+ "net.seqslab.api.tokens.refresh", user, result["tokens"]["refresh"]
359
+ )
360
+ keyring.set_password(
361
+ "net.seqslab.api.tokens.access", user, result["tokens"]["access"]
362
+ )
309
363
  return 0
310
364
  except ValueError:
311
365
  return errno.EINVAL
@@ -16,12 +16,12 @@ copies by sale, rental, lease or lending are violations of federal copyright
16
16
  laws and state trade secret laws, punishable by civil and criminal penalties.
17
17
  """
18
18
 
19
- import sys
19
+ # Standard Library
20
20
  import signal
21
+ import sys
21
22
 
22
23
  from nubia import Nubia, Options
23
-
24
- from seqslab import auth, drs, wes, trs, workspace, user, role, organization, scr
24
+ from seqslab import auth, drs, organization, role, scr, trs, user, wes, workspace
25
25
  from seqslab.plugin import SQLBPlugin
26
26
 
27
27
 
@@ -30,7 +30,7 @@ def signal_handler(sig, frame):
30
30
  sys.stderr.write(msg)
31
31
  ans = input()
32
32
 
33
- if ans == 'y':
33
+ if ans == "y":
34
34
  print("")
35
35
  exit(1)
36
36
  else:
@@ -47,8 +47,7 @@ def main():
47
47
  command_pkgs=[auth, drs, wes, trs, workspace, user, role, organization, scr],
48
48
  plugin=plugin,
49
49
  options=Options(
50
- persistent_history=False,
51
- auto_execute_single_suggestions=False
50
+ persistent_history=False, auto_execute_single_suggestions=False
52
51
  ),
53
52
  )
54
53
  sys.exit(shell.run())
@@ -1,8 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
- from nubia import context
4
- from nubia import exceptions
5
- from nubia import eventbus
3
+ from nubia import context, eventbus, exceptions
6
4
 
7
5
 
8
6
  class SQLBContext(context.Context):
@@ -5,9 +5,7 @@ environ.Env.read_env("/etc/seqslab/cli_apps.env")
5
5
 
6
6
  name = "drs"
7
7
 
8
- __all__ = [
9
-
10
- ]
8
+ __all__ = []
11
9
 
12
10
  __version__ = "v1"
13
11