Skip to content

Commit

Permalink
Merge pull request from GHSA-59qj-jcjv-662j
Browse files Browse the repository at this point in the history
[8.0] Remove server side cache in TokenManager
  • Loading branch information
chrisburr committed Feb 7, 2024
2 parents a048eb2 + a9ddc97 commit f9ddab7
Showing 1 changed file with 6 additions and 36 deletions.
42 changes: 6 additions & 36 deletions src/DIRAC/FrameworkSystem/Service/TokenManagerHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,16 @@
.. image:: /_static/Systems/FS/TokenManager_getToken.png
:alt: https://dirac.readthedocs.io/en/integration/_images/TokenManager_getToken.png (source https://github.com/TaykYoku/DIRACIMGS/raw/main/TokenManagerService_getToken.ai)
The service and its client have a mechanism for caching the received tokens.
The client has a mechanism for caching the received tokens.
This helps reduce the number of requests to both the service and the Identity Provider (IdP).
If the client has a valid **access token** in the cache, it is used until it expires.
After that you need to update. The client can update it independently if on the server where it is in ``dirac.cfg``
``client_id`` and ``client_secret`` of the Identity Provider client are registered.
Otherwise, the client makes an RPC call to the **TornadoManager** service.
It in turn checks the cache and if the ``access token`` is already invalid tries to update it using a ``refresh token``.
If the required token is not in the cache, then the ``refresh token`` from :py:class:`TokenDB <DIRAC.FrameworkSystem.DB.TokenDB.TokenDB>`
is taken and the **exchange token** request to Identity Provider is made. The received tokens are cached.
The ``refresh token`` from :py:class:`TokenDB <DIRAC.FrameworkSystem.DB.TokenDB.TokenDB>`
is taken and the **exchange token** request to Identity Provider is made.
"""

import pprint
Expand All @@ -42,16 +41,11 @@
from DIRAC.FrameworkSystem.Utilities.TokenManagementUtilities import (
getIdProviderClient,
getCachedKey,
getCachedToken,
DEFAULT_RT_EXPIRATION_TIME,
DEFAULT_AT_EXPIRATION_TIME,
)


# Used to synchronize the cache with user tokens
gTokensSync = ThreadSafe.Synchronizer()


class TokenManagerHandler(TornadoService):
DEFAULT_AUTHORIZATION = ["authenticated"]

Expand All @@ -61,8 +55,6 @@ def initializeHandler(cls, *args):
:return: S_OK()/S_ERROR()
"""
# Cache containing tokens from scope requested by the client
cls.__tokensCache = DictCache()

# The service plays an important OAuth 2.0 role, namely it is an Identity Provider client.
# This allows you to manage tokens without the involvement of their owners.
Expand Down Expand Up @@ -181,7 +173,6 @@ def __checkProperties(self, requestedUserDN: str, requestedUserGroup: str):
# Not authorized!
return S_ERROR("You can't get tokens!")

@gTokensSync
def export_getToken(
self,
username: str = None,
Expand Down Expand Up @@ -213,12 +204,8 @@ def export_getToken(
return result
idpObj = result["Value"]

# Search for an existing token in tokensCache
cachedKey = getCachedKey(idpObj, username, userGroup, scope, audience)
result = getCachedToken(self.__tokensCache, cachedKey, requiredTimeLeft)
if result["OK"]:
# A valid token has been found and is returned
return result
# getCachedKey is just used here to resolve the default scopes
_, scope, *_ = getCachedKey(idpObj, username, userGroup, scope, audience)

# A client token is requested
if not username:
Expand All @@ -227,19 +214,8 @@ def export_getToken(
return result

# Get the client token with requested scope and audience
scope = cachedKey[1]
audience = cachedKey[2]
result = idpObj.fetchToken(grant_type="client_credentials", scope=scope, audience=audience)
if not result["OK"]:
return result
token = result["Value"]

# Caching new token: only get an access token (no refresh token in this context)
self.__tokensCache.add(
cachedKey,
result["Value"].get_claim("exp", "access_token") or DEFAULT_AT_EXPIRATION_TIME,
token,
)

return result

# A user token is requested
Expand All @@ -262,12 +238,6 @@ def export_getToken(
# refresh token with requested scope
result = idpObj.refreshToken(tokens.get("refresh_token"), group=userGroup, scope=scope)
if result["OK"]:
# caching new tokens
self.__tokensCache.add(
cachedKey,
result["Value"].get_claim("exp", "refresh_token") or DEFAULT_RT_EXPIRATION_TIME,
result["Value"],
)
return result
# Did not find any token associated with the found user ID
err.append(result.get("Message", f"No token found for {uid}"))
Expand Down

0 comments on commit f9ddab7

Please sign in to comment.