Skip to content

Commit

Permalink
feat: lock credentials path to avoid concurrent access
Browse files Browse the repository at this point in the history
  • Loading branch information
natthan-pigoux committed Nov 5, 2024
1 parent 7c2b8b8 commit 523cf68
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 66 deletions.
58 changes: 35 additions & 23 deletions diracx-client/src/diracx/client/patches/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,27 +83,37 @@ def get_token(
fcntl.flock(f, fcntl.LOCK_UN)
return None

# If we are here, it means the token needs to be refreshed
token_response = refresh_token(
token_endpoint,
client_id,
response.refresh_token,
verify=verify,
)

# Write the new credentials to the file
f.seek(0)
f.truncate()
f.write(serialize_credentials(token_response))
f.flush()
os.fsync(f.fileno())

# Get an AccessToken instance
return AccessToken(
token=token_response.access_token,
expires_on=datetime.now(tz=timezone.utc)
+ timedelta(seconds=token_response.expires_in - EXPIRES_GRACE_SECONDS),
)
if response.status == TokenStatus.REFRESH and response.refresh_token:
# If we are here, it means the token needs to be refreshed
token_response = refresh_token(
token_endpoint,
client_id,
response.refresh_token,
verify=verify,
)

# Write the new credentials to the file
f.seek(0)
f.truncate()
f.write(serialize_credentials(token_response))
f.flush()
os.fsync(f.fileno())

# Get an AccessToken instance
return AccessToken(
token=token_response.access_token,
expires_on=int(
(
datetime.now(tz=timezone.utc)
+ timedelta(
seconds=token_response.expires_in
- EXPIRES_GRACE_SECONDS
)
).timestamp()
),
)
else:
return None
finally:
# Release the lock
fcntl.flock(f, fcntl.LOCK_UN)
Expand Down Expand Up @@ -166,7 +176,7 @@ def extract_token_from_credentials(
return TokenResult(TokenStatus.VALID, access_token=token)

if is_refresh_token_valid(refresh_token):
return TokenResult(TokenStatus.REFRESH, refresh_token=credentials.refresh_token)
return TokenResult(TokenStatus.REFRESH, refresh_token=refresh_token)

# If we are here, it means the refresh token is not valid anymore
return TokenResult(TokenStatus.INVALID)
Expand Down Expand Up @@ -243,7 +253,9 @@ def on_request(self, request: PipelineRequest) -> None:
:type request: ~azure.core.pipeline.PipelineRequest
:raises: :class:`~azure.core.exceptions.ServiceRequestError`
"""
self._token = self._credential.get_token("", token=self._token)
self._token: AccessToken | None = self._credential.get_token(
"", token=self._token
)
if not self._token:
# If we are here, it means the token is not available
# we suppose it is not needed to perform the request
Expand Down
6 changes: 3 additions & 3 deletions diracx-core/src/diracx/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ def read_credentials(location: Path) -> TokenResponse:
try:
with open(credentials_path, "r") as f:
# Lock the file to prevent other processes from writing to it at the same time
fcntl.flock(f, fcntl.LOCK_SH | fcntl.LOCK_NB)
fcntl.flock(f, fcntl.LOCK_SH)
# Read the credentials from the file
try:
credentials = json.load(f)
finally:
# Release the lock
fcntl.flock(f, fcntl.LOCK_UN)
except (BlockingIOError, FileNotFoundError, json.JSONDecodeError) as e:
except (FileNotFoundError, json.JSONDecodeError) as e:
raise RuntimeError(f"Error reading credentials: {e}") from e

return TokenResponse(
Expand All @@ -74,7 +74,7 @@ def write_credentials(token_response: TokenResponse, *, location: Path | None =

with open(credentials_path, "w") as f:
# Lock the file to prevent other processes from writing to it at the same time
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
fcntl.flock(f, fcntl.LOCK_EX)
try:
# Write the credentials to the file
f.write(serialize_credentials(token_response))
Expand Down
Loading

0 comments on commit 523cf68

Please sign in to comment.