diracx-db 0.0.1a11__py3-none-any.whl → 0.0.1a13__py3-none-any.whl
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.
- diracx/db/sql/auth/db.py +19 -12
- diracx/db/sql/auth/schema.py +2 -4
- diracx/db/sql/jobs/status_utility.py +1 -1
- diracx/db/sql/sandbox_metadata/db.py +85 -12
- {diracx_db-0.0.1a11.dist-info → diracx_db-0.0.1a13.dist-info}/METADATA +1 -1
- {diracx_db-0.0.1a11.dist-info → diracx_db-0.0.1a13.dist-info}/RECORD +9 -9
- {diracx_db-0.0.1a11.dist-info → diracx_db-0.0.1a13.dist-info}/WHEEL +1 -1
- {diracx_db-0.0.1a11.dist-info → diracx_db-0.0.1a13.dist-info}/entry_points.txt +0 -0
- {diracx_db-0.0.1a11.dist-info → diracx_db-0.0.1a13.dist-info}/top_level.txt +0 -0
diracx/db/sql/auth/db.py
CHANGED
@@ -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
|
|
diracx/db/sql/auth/schema.py
CHANGED
@@ -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
|
|
@@ -272,7 +272,7 @@ async def remove_jobs(
|
|
272
272
|
|
273
273
|
# TODO: this was also not done in the JobManagerHandler, but it was done in the JobCleaningAgent
|
274
274
|
# I think it should be done here as well
|
275
|
-
await sandbox_metadata_db.
|
275
|
+
await sandbox_metadata_db.unassign_sandboxes_to_jobs(job_ids)
|
276
276
|
|
277
277
|
# Remove the job from TaskQueueDB
|
278
278
|
await _remove_jobs_from_task_queue(job_ids, config, task_queue_db, background_task)
|
@@ -1,9 +1,10 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
from typing import Any
|
4
|
+
|
3
5
|
import sqlalchemy
|
4
|
-
from sqlalchemy import delete
|
5
6
|
|
6
|
-
from diracx.core.models import SandboxInfo, UserInfo
|
7
|
+
from diracx.core.models import SandboxInfo, SandboxType, UserInfo
|
7
8
|
from diracx.db.sql.utils import BaseSQLDB, utcnow
|
8
9
|
|
9
10
|
from .schema import Base as SandboxMetadataDBBase
|
@@ -76,7 +77,7 @@ class SandboxMetadataDB(BaseSQLDB):
|
|
76
77
|
result = await self.conn.execute(stmt)
|
77
78
|
assert result.rowcount == 1
|
78
79
|
|
79
|
-
async def sandbox_is_assigned(self,
|
80
|
+
async def sandbox_is_assigned(self, pfn: str, se_name: str) -> bool:
|
80
81
|
"""Checks if a sandbox exists and has been assigned."""
|
81
82
|
stmt: sqlalchemy.Executable = sqlalchemy.select(sb_SandBoxes.Assigned).where(
|
82
83
|
sb_SandBoxes.SEName == se_name, sb_SandBoxes.SEPFN == pfn
|
@@ -84,13 +85,85 @@ class SandboxMetadataDB(BaseSQLDB):
|
|
84
85
|
result = await self.conn.execute(stmt)
|
85
86
|
is_assigned = result.scalar_one()
|
86
87
|
return is_assigned
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
"""
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
88
|
+
|
89
|
+
@staticmethod
|
90
|
+
def jobid_to_entity_id(job_id: int) -> str:
|
91
|
+
"""Define the entity id as 'Entity:entity_id' due to the DB definition"""
|
92
|
+
return f"Job:{job_id}"
|
93
|
+
|
94
|
+
async def get_sandbox_assigned_to_job(
|
95
|
+
self, job_id: int, sb_type: SandboxType
|
96
|
+
) -> list[Any]:
|
97
|
+
"""Get the sandbox assign to job"""
|
98
|
+
entity_id = self.jobid_to_entity_id(job_id)
|
99
|
+
stmt = (
|
100
|
+
sqlalchemy.select(sb_SandBoxes.SEPFN)
|
101
|
+
.where(sb_SandBoxes.SBId == sb_EntityMapping.SBId)
|
102
|
+
.where(
|
103
|
+
sb_EntityMapping.EntityId == entity_id,
|
104
|
+
sb_EntityMapping.Type == sb_type,
|
105
|
+
)
|
95
106
|
)
|
96
|
-
await self.conn.execute(stmt)
|
107
|
+
result = await self.conn.execute(stmt)
|
108
|
+
return [result.scalar()]
|
109
|
+
|
110
|
+
async def assign_sandbox_to_jobs(
|
111
|
+
self,
|
112
|
+
jobs_ids: list[int],
|
113
|
+
pfn: str,
|
114
|
+
sb_type: SandboxType,
|
115
|
+
se_name: str,
|
116
|
+
) -> None:
|
117
|
+
"""Mapp sandbox and jobs"""
|
118
|
+
for job_id in jobs_ids:
|
119
|
+
# Define the entity id as 'Entity:entity_id' due to the DB definition:
|
120
|
+
entity_id = self.jobid_to_entity_id(job_id)
|
121
|
+
select_sb_id = sqlalchemy.select(
|
122
|
+
sb_SandBoxes.SBId,
|
123
|
+
sqlalchemy.literal(entity_id).label("EntityId"),
|
124
|
+
sqlalchemy.literal(sb_type).label("Type"),
|
125
|
+
).where(
|
126
|
+
sb_SandBoxes.SEName == se_name,
|
127
|
+
sb_SandBoxes.SEPFN == pfn,
|
128
|
+
)
|
129
|
+
stmt = sqlalchemy.insert(sb_EntityMapping).from_select(
|
130
|
+
["SBId", "EntityId", "Type"], select_sb_id
|
131
|
+
)
|
132
|
+
await self.conn.execute(stmt)
|
133
|
+
|
134
|
+
stmt = (
|
135
|
+
sqlalchemy.update(sb_SandBoxes)
|
136
|
+
.where(sb_SandBoxes.SEPFN == pfn)
|
137
|
+
.values(Assigned=True)
|
138
|
+
)
|
139
|
+
result = await self.conn.execute(stmt)
|
140
|
+
assert result.rowcount == 1
|
141
|
+
|
142
|
+
async def unassign_sandboxes_to_jobs(self, jobs_ids: list[int]) -> None:
|
143
|
+
"""Delete mapping between jobs and sandboxes"""
|
144
|
+
for job_id in jobs_ids:
|
145
|
+
entity_id = self.jobid_to_entity_id(job_id)
|
146
|
+
sb_sel_stmt = sqlalchemy.select(
|
147
|
+
sb_SandBoxes.SBId,
|
148
|
+
).where(sb_EntityMapping.EntityId == entity_id)
|
149
|
+
|
150
|
+
result = await self.conn.execute(sb_sel_stmt)
|
151
|
+
sb_ids = [row.SBId for row in result]
|
152
|
+
|
153
|
+
del_stmt = sqlalchemy.delete(sb_EntityMapping).where(
|
154
|
+
sb_EntityMapping.EntityId == entity_id
|
155
|
+
)
|
156
|
+
await self.conn.execute(del_stmt)
|
157
|
+
|
158
|
+
sb_entity_sel_stmt = sqlalchemy.select(sb_EntityMapping.SBId).where(
|
159
|
+
sb_EntityMapping.SBId.in_(sb_ids)
|
160
|
+
)
|
161
|
+
result = await self.conn.execute(sb_entity_sel_stmt)
|
162
|
+
remaining_sb_ids = [row.SBId for row in result]
|
163
|
+
if not remaining_sb_ids:
|
164
|
+
unassign_stmt = (
|
165
|
+
sqlalchemy.update(sb_SandBoxes)
|
166
|
+
.where(sb_SandBoxes.SBId.in_(sb_ids))
|
167
|
+
.values(Assigned=False)
|
168
|
+
)
|
169
|
+
await self.conn.execute(unassign_stmt)
|
@@ -8,20 +8,20 @@ diracx/db/os/utils.py,sha256=mau0_2uRi-I3geefmKQRWFKo4JcIkIUADvnwBiQX700,9129
|
|
8
8
|
diracx/db/sql/__init__.py,sha256=R6tk5lo1EHbt8joGDesesYHcc1swIq9T4AaSixhh7lA,252
|
9
9
|
diracx/db/sql/utils.py,sha256=BuXjIuXN-_v8YkCoMoMhw2tHVUqG6lTBx-e4VEYWE8o,7857
|
10
10
|
diracx/db/sql/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
-
diracx/db/sql/auth/db.py,sha256=
|
12
|
-
diracx/db/sql/auth/schema.py,sha256=
|
11
|
+
diracx/db/sql/auth/db.py,sha256=mKjy5B8orw0yu6nOwxyzbBqyeE-J9iYq6fKjuELmr9g,10273
|
12
|
+
diracx/db/sql/auth/schema.py,sha256=JCkSa2IRzqMHTpaSc9aB9h33XsFyEM_Ohsenex6xagY,2835
|
13
13
|
diracx/db/sql/dummy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
14
|
diracx/db/sql/dummy/db.py,sha256=5PIPv6aKY7CGIwmvnGKowjVr9ZQWpbjFSd2PIX7YOUw,1627
|
15
15
|
diracx/db/sql/dummy/schema.py,sha256=uEkGDNVZbmJecytkHY1CO-M1MiKxe5w1_h0joJMPC9E,680
|
16
16
|
diracx/db/sql/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
17
|
diracx/db/sql/jobs/db.py,sha256=Y_2mx5kPTeuz6nxXVwGLzTssKsIH6nfnoTvWvilSgxA,29876
|
18
18
|
diracx/db/sql/jobs/schema.py,sha256=YkxIdjTkvLlEZ9IQt86nj80eMvOPbcrfk9aisjmNpqY,9275
|
19
|
-
diracx/db/sql/jobs/status_utility.py,sha256=
|
19
|
+
diracx/db/sql/jobs/status_utility.py,sha256=_3Wdd11ShA4Z6HKr0_D_o8-zPZhdzgFpZSYAyYkH4Q0,10525
|
20
20
|
diracx/db/sql/sandbox_metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
|
-
diracx/db/sql/sandbox_metadata/db.py,sha256=
|
21
|
+
diracx/db/sql/sandbox_metadata/db.py,sha256=0EDFMfOW_O3pEPTShqBCME9z4j-JKpyYM6-BBccr27E,6303
|
22
22
|
diracx/db/sql/sandbox_metadata/schema.py,sha256=rngYYkJxBhjETBHGLD1CTipDGe44mRYR0wdaFoAJwp0,1400
|
23
|
-
diracx_db-0.0.
|
24
|
-
diracx_db-0.0.
|
25
|
-
diracx_db-0.0.
|
26
|
-
diracx_db-0.0.
|
27
|
-
diracx_db-0.0.
|
23
|
+
diracx_db-0.0.1a13.dist-info/METADATA,sha256=jmbXQvJykcvn3vGnxvO8GUGP3D3yjL-cXZwqXXJkzP4,681
|
24
|
+
diracx_db-0.0.1a13.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
25
|
+
diracx_db-0.0.1a13.dist-info/entry_points.txt,sha256=xEFGu_zgmPgQPlUeFtdahQfQIboJ1ugFOK8eMio9gtw,271
|
26
|
+
diracx_db-0.0.1a13.dist-info/top_level.txt,sha256=vJx10tdRlBX3rF2Psgk5jlwVGZNcL3m_7iQWwgPXt-U,7
|
27
|
+
diracx_db-0.0.1a13.dist-info/RECORD,,
|
File without changes
|
File without changes
|