diracx-db 0.0.1a10__tar.gz → 0.0.1a12__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/PKG-INFO +1 -1
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/os/utils.py +1 -2
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/sql/auth/db.py +19 -12
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/sql/auth/schema.py +2 -4
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx_db.egg-info/PKG-INFO +1 -1
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/tests/auth/test_authorization_flow.py +2 -3
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/tests/auth/test_device_flow.py +11 -6
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/README.md +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/pyproject.toml +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/setup.cfg +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/__init__.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/__main__.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/exceptions.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/os/__init__.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/os/job_parameters.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/py.typed +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/sql/__init__.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/sql/auth/__init__.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/sql/dummy/__init__.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/sql/dummy/db.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/sql/dummy/schema.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/sql/jobs/__init__.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/sql/jobs/db.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/sql/jobs/schema.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/sql/jobs/status_utility.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/sql/sandbox_metadata/__init__.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/sql/sandbox_metadata/db.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/sql/sandbox_metadata/schema.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx/db/sql/utils.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx_db.egg-info/SOURCES.txt +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx_db.egg-info/dependency_links.txt +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx_db.egg-info/entry_points.txt +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx_db.egg-info/requires.txt +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/src/diracx_db.egg-info/top_level.txt +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/tests/auth/test_refresh_token.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/tests/jobs/test_jobDB.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/tests/jobs/test_jobLoggingDB.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/tests/opensearch/test_connection.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/tests/opensearch/test_index_template.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/tests/opensearch/test_search.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/tests/test_dummyDB.py +0 -0
- {diracx-db-0.0.1a10 → diracx-db-0.0.1a12}/tests/test_sandbox_metadata.py +0 -0
@@ -34,8 +34,7 @@ class BaseOSDB(metaclass=ABCMeta):
|
|
34
34
|
index_prefix: str
|
35
35
|
|
36
36
|
@abstractmethod
|
37
|
-
def index_name(self, doc_id: int) -> str:
|
38
|
-
...
|
37
|
+
def index_name(self, doc_id: int) -> str: ...
|
39
38
|
|
40
39
|
def __init__(self, connection_kwargs: dict[str, Any]) -> None:
|
41
40
|
self._client: AsyncOpenSearch | None = None
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import hashlib
|
3
4
|
import secrets
|
4
5
|
from datetime import datetime
|
5
6
|
from uuid import uuid4
|
@@ -63,7 +64,7 @@ class AuthDB(BaseSQLDB):
|
|
63
64
|
),
|
64
65
|
).with_for_update()
|
65
66
|
stmt = stmt.where(
|
66
|
-
DeviceFlows.device_code == device_code,
|
67
|
+
DeviceFlows.device_code == hashlib.sha256(device_code.encode()).hexdigest(),
|
67
68
|
)
|
68
69
|
res = dict((await self.conn.execute(stmt)).one()._mapping)
|
69
70
|
|
@@ -74,7 +75,10 @@ class AuthDB(BaseSQLDB):
|
|
74
75
|
# Update the status to Done before returning
|
75
76
|
await self.conn.execute(
|
76
77
|
update(DeviceFlows)
|
77
|
-
.where(
|
78
|
+
.where(
|
79
|
+
DeviceFlows.device_code
|
80
|
+
== hashlib.sha256(device_code.encode()).hexdigest()
|
81
|
+
)
|
78
82
|
.values(status=FlowStatus.DONE)
|
79
83
|
)
|
80
84
|
return res
|
@@ -110,7 +114,6 @@ class AuthDB(BaseSQLDB):
|
|
110
114
|
self,
|
111
115
|
client_id: str,
|
112
116
|
scope: str,
|
113
|
-
audience: str,
|
114
117
|
) -> tuple[str, str]:
|
115
118
|
# Because the user_code might be short, there is a risk of conflicts
|
116
119
|
# This is why we retry multiple times
|
@@ -119,14 +122,16 @@ class AuthDB(BaseSQLDB):
|
|
119
122
|
secrets.choice(USER_CODE_ALPHABET)
|
120
123
|
for _ in range(DeviceFlows.user_code.type.length) # type: ignore
|
121
124
|
)
|
122
|
-
# user_code = "2QRKPY"
|
123
125
|
device_code = secrets.token_urlsafe()
|
126
|
+
|
127
|
+
# Hash the the device_code to avoid leaking information
|
128
|
+
hashed_device_code = hashlib.sha256(device_code.encode()).hexdigest()
|
129
|
+
|
124
130
|
stmt = insert(DeviceFlows).values(
|
125
131
|
client_id=client_id,
|
126
132
|
scope=scope,
|
127
|
-
audience=audience,
|
128
133
|
user_code=user_code,
|
129
|
-
device_code=
|
134
|
+
device_code=hashed_device_code,
|
130
135
|
)
|
131
136
|
try:
|
132
137
|
await self.conn.execute(stmt)
|
@@ -143,7 +148,6 @@ class AuthDB(BaseSQLDB):
|
|
143
148
|
self,
|
144
149
|
client_id: str,
|
145
150
|
scope: str,
|
146
|
-
audience: str,
|
147
151
|
code_challenge: str,
|
148
152
|
code_challenge_method: str,
|
149
153
|
redirect_uri: str,
|
@@ -154,7 +158,6 @@ class AuthDB(BaseSQLDB):
|
|
154
158
|
uuid=uuid,
|
155
159
|
client_id=client_id,
|
156
160
|
scope=scope,
|
157
|
-
audience=audience,
|
158
161
|
code_challenge=code_challenge,
|
159
162
|
code_challenge_method=code_challenge_method,
|
160
163
|
redirect_uri=redirect_uri,
|
@@ -172,7 +175,10 @@ class AuthDB(BaseSQLDB):
|
|
172
175
|
:raises: AuthorizationError if no such uuid or status not pending
|
173
176
|
"""
|
174
177
|
|
178
|
+
# Hash the code to avoid leaking information
|
175
179
|
code = secrets.token_urlsafe()
|
180
|
+
hashed_code = hashlib.sha256(code.encode()).hexdigest()
|
181
|
+
|
176
182
|
stmt = update(AuthorizationFlows)
|
177
183
|
|
178
184
|
stmt = stmt.where(
|
@@ -181,7 +187,7 @@ class AuthDB(BaseSQLDB):
|
|
181
187
|
AuthorizationFlows.creation_time > substract_date(seconds=max_validity),
|
182
188
|
)
|
183
189
|
|
184
|
-
stmt = stmt.values(id_token=id_token, code=
|
190
|
+
stmt = stmt.values(id_token=id_token, code=hashed_code, status=FlowStatus.READY)
|
185
191
|
res = await self.conn.execute(stmt)
|
186
192
|
|
187
193
|
if res.rowcount != 1:
|
@@ -190,15 +196,16 @@ class AuthDB(BaseSQLDB):
|
|
190
196
|
stmt = select(AuthorizationFlows.code, AuthorizationFlows.redirect_uri)
|
191
197
|
stmt = stmt.where(AuthorizationFlows.uuid == uuid)
|
192
198
|
row = (await self.conn.execute(stmt)).one()
|
193
|
-
return
|
199
|
+
return code, row.redirect_uri
|
194
200
|
|
195
201
|
async def get_authorization_flow(self, code: str, max_validity: int):
|
202
|
+
hashed_code = hashlib.sha256(code.encode()).hexdigest()
|
196
203
|
# The with_for_update
|
197
204
|
# prevents that the token is retrieved
|
198
205
|
# multiple time concurrently
|
199
206
|
stmt = select(AuthorizationFlows).with_for_update()
|
200
207
|
stmt = stmt.where(
|
201
|
-
AuthorizationFlows.code ==
|
208
|
+
AuthorizationFlows.code == hashed_code,
|
202
209
|
AuthorizationFlows.creation_time > substract_date(seconds=max_validity),
|
203
210
|
)
|
204
211
|
|
@@ -208,7 +215,7 @@ class AuthDB(BaseSQLDB):
|
|
208
215
|
# Update the status to Done before returning
|
209
216
|
await self.conn.execute(
|
210
217
|
update(AuthorizationFlows)
|
211
|
-
.where(AuthorizationFlows.code ==
|
218
|
+
.where(AuthorizationFlows.code == hashed_code)
|
212
219
|
.values(status=FlowStatus.DONE)
|
213
220
|
)
|
214
221
|
|
@@ -45,8 +45,7 @@ class DeviceFlows(Base):
|
|
45
45
|
creation_time = DateNowColumn()
|
46
46
|
client_id = Column(String(255))
|
47
47
|
scope = Column(String(1024))
|
48
|
-
|
49
|
-
device_code = Column(String(128), unique=True) # hash it ?
|
48
|
+
device_code = Column(String(128), unique=True) # Should be a hash
|
50
49
|
id_token = NullColumn(JSON())
|
51
50
|
|
52
51
|
|
@@ -57,11 +56,10 @@ class AuthorizationFlows(Base):
|
|
57
56
|
client_id = Column(String(255))
|
58
57
|
creation_time = DateNowColumn()
|
59
58
|
scope = Column(String(1024))
|
60
|
-
audience = Column(String(255))
|
61
59
|
code_challenge = Column(String(255))
|
62
60
|
code_challenge_method = Column(String(8))
|
63
61
|
redirect_uri = Column(String(255))
|
64
|
-
code = NullColumn(String(255)) #
|
62
|
+
code = NullColumn(String(255)) # Should be a hash
|
65
63
|
id_token = NullColumn(JSON())
|
66
64
|
|
67
65
|
|
@@ -23,7 +23,7 @@ async def test_insert_id_token(auth_db: AuthDB):
|
|
23
23
|
# First insert
|
24
24
|
async with auth_db as auth_db:
|
25
25
|
uuid = await auth_db.insert_authorization_flow(
|
26
|
-
"client_id", "scope", "
|
26
|
+
"client_id", "scope", "code_challenge", "S256", "redirect_uri"
|
27
27
|
)
|
28
28
|
|
29
29
|
id_token = {"sub": "myIdToken"}
|
@@ -68,12 +68,11 @@ async def test_insert(auth_db: AuthDB):
|
|
68
68
|
# First insert
|
69
69
|
async with auth_db as auth_db:
|
70
70
|
uuid1 = await auth_db.insert_authorization_flow(
|
71
|
-
"client_id", "scope", "
|
71
|
+
"client_id", "scope", "code_challenge", "S256", "redirect_uri"
|
72
72
|
)
|
73
73
|
uuid2 = await auth_db.insert_authorization_flow(
|
74
74
|
"client_id2",
|
75
75
|
"scope2",
|
76
|
-
"audience2",
|
77
76
|
"code_challenge2",
|
78
77
|
"S256",
|
79
78
|
"redirect_uri2",
|
@@ -28,20 +28,22 @@ async def test_device_user_code_collision(auth_db: AuthDB, monkeypatch):
|
|
28
28
|
# First insert should work
|
29
29
|
async with auth_db as auth_db:
|
30
30
|
code, device = await auth_db.insert_device_flow(
|
31
|
-
"client_id",
|
31
|
+
"client_id",
|
32
|
+
"scope",
|
32
33
|
)
|
33
34
|
assert code == "A" * USER_CODE_LENGTH
|
34
35
|
assert device
|
35
36
|
|
36
37
|
async with auth_db as auth_db:
|
37
38
|
with pytest.raises(NotImplementedError, match="insert new device flow"):
|
38
|
-
await auth_db.insert_device_flow("client_id", "scope"
|
39
|
+
await auth_db.insert_device_flow("client_id", "scope")
|
39
40
|
|
40
41
|
monkeypatch.setattr(secrets, "choice", lambda _: "B")
|
41
42
|
|
42
43
|
async with auth_db as auth_db:
|
43
44
|
code, device = await auth_db.insert_device_flow(
|
44
|
-
"client_id",
|
45
|
+
"client_id",
|
46
|
+
"scope",
|
45
47
|
)
|
46
48
|
assert code == "B" * USER_CODE_LENGTH
|
47
49
|
assert device
|
@@ -59,10 +61,12 @@ async def test_device_flow_lookup(auth_db: AuthDB, monkeypatch):
|
|
59
61
|
# First insert
|
60
62
|
async with auth_db as auth_db:
|
61
63
|
user_code1, device_code1 = await auth_db.insert_device_flow(
|
62
|
-
"client_id1",
|
64
|
+
"client_id1",
|
65
|
+
"scope1",
|
63
66
|
)
|
64
67
|
user_code2, device_code2 = await auth_db.insert_device_flow(
|
65
|
-
"client_id2",
|
68
|
+
"client_id2",
|
69
|
+
"scope2",
|
66
70
|
)
|
67
71
|
|
68
72
|
assert user_code1 != user_code2
|
@@ -123,7 +127,8 @@ async def test_device_flow_insert_id_token(auth_db: AuthDB):
|
|
123
127
|
# First insert
|
124
128
|
async with auth_db as auth_db:
|
125
129
|
user_code, device_code = await auth_db.insert_device_flow(
|
126
|
-
"client_id",
|
130
|
+
"client_id",
|
131
|
+
"scope",
|
127
132
|
)
|
128
133
|
|
129
134
|
# Make sure it exists, and is Pending
|
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
|