Skip to content

Commit

Permalink
Merge pull request #7888 from atsareg/tokenmanager-notornado
Browse files Browse the repository at this point in the history
[8.0] DISET version of the TokenManager service
  • Loading branch information
fstagni authored Nov 19, 2024
2 parents 37b625a + 0fb2fb2 commit b90332c
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 5 deletions.
34 changes: 34 additions & 0 deletions src/DIRAC/FrameworkSystem/Service/DisetTokenManagerHandler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""TokenManager service is responsible for token management, namely storing, updating,
requesting new tokens for DIRAC components that have the appropriate permissions.
.. literalinclude:: ../ConfigTemplate.cfg
:start-after: ##BEGIN TokenManager:
:end-before: ##END
:dedent: 2
:caption: TokenManager options
The most common use of this service is to obtain tokens with certain scope to return to the user for its purposes,
or to provide to the DIRAC service to perform asynchronous tasks on behalf of the user.
This is mainly about the :py:meth:`export_getToken` method.
.. 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 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.
The ``refresh token`` from :py:class:`TokenDB <DIRAC.FrameworkSystem.DB.TokenDB.TokenDB>`
is taken and the **exchange token** request to Identity Provider is made.
"""

from DIRAC.Core.DISET.RequestHandler import RequestHandler
from DIRAC.FrameworkSystem.Service.TokenManagerHandler import TokenManagerHandlerMixin


class DisetTokenManagerHandler(TokenManagerHandlerMixin, RequestHandler):
pass
25 changes: 20 additions & 5 deletions src/DIRAC/FrameworkSystem/Service/TokenManagerHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,17 @@

from DIRAC import S_OK, S_ERROR
from DIRAC.Core.Security import Properties
from DIRAC.Core.Utilities import ThreadSafe
from DIRAC.Core.Utilities.DictCache import DictCache
from DIRAC.Core.Tornado.Server.TornadoService import TornadoService
from DIRAC.FrameworkSystem.DB.TokenDB import TokenDB
from DIRAC.ConfigurationSystem.Client.Helpers import Registry
from DIRAC.Resources.IdProvider.IdProviderFactory import IdProviderFactory
from DIRAC.FrameworkSystem.Utilities.TokenManagementUtilities import (
getIdProviderClient,
getCachedKey,
DEFAULT_RT_EXPIRATION_TIME,
DEFAULT_AT_EXPIRATION_TIME,
)


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

@classmethod
Expand All @@ -69,6 +65,9 @@ def initializeHandler(cls, *args):

return S_OK()

auth_getUserTokensInfo = ["authenticated"]
types_getUserTokensInfo = []

def export_getUserTokensInfo(self):
"""Generate information dict about user tokens
Expand All @@ -89,6 +88,7 @@ def export_getUserTokensInfo(self):
return S_OK(tokensInfo)

auth_getUsersTokensInfo = [Properties.PROXY_MANAGEMENT]
types_getUserTokensInfo = [list]

def export_getUsersTokensInfo(self, users: list):
"""Get the info about the user tokens in the database
Expand Down Expand Up @@ -119,6 +119,8 @@ def export_getUsersTokensInfo(self, users: list):
tokensInfo.append(tokenDict)
return S_OK(tokensInfo)

types_updateToken = [dict, str, str, int]

def export_updateToken(self, token: dict, userID: str, provider: str, rt_expired_in: int = 24 * 3600):
"""Using this method, you can transfer user tokens for storage in the TokenManager.
Expand Down Expand Up @@ -173,6 +175,8 @@ def __checkProperties(self, requestedUserDN: str, requestedUserGroup: str):
# Not authorized!
return S_ERROR("You can't get tokens!")

types_getToken = [None, None, None, None, None]

def export_getToken(
self,
username: str = None,
Expand Down Expand Up @@ -215,6 +219,9 @@ def export_getToken(

# Get the client token with requested scope and audience
result = idpObj.fetchToken(grant_type="client_credentials", scope=scope, audience=audience)
# DEncode can not encode OAuth2Token object
if result["OK"]:
result["Value"] = dict(result["Value"])

return result

Expand Down Expand Up @@ -244,6 +251,8 @@ def export_getToken(
# Collect all errors when trying to get a token, or if no user ID is registered
return S_ERROR("; ".join(err or [f"No user ID found for {username}"]))

types_deleteToken = [str]

def export_deleteToken(self, userDN: str):
"""Delete a token from the DB
Expand All @@ -263,6 +272,8 @@ def export_deleteToken(self, userDN: str):
result = Registry.getIDFromDN(userDN)
return self.__tokenDB.removeToken(user_id=result["Value"]) if result["OK"] else result

types_getTokensByUserID = [str]

def export_getTokensByUserID(self, userID: str):
"""Retrieve a token from the DB
Expand All @@ -271,3 +282,7 @@ def export_getTokensByUserID(self, userID: str):
:return: S_OK(list)/S_ERROR() token row in dict format
"""
return self.__tokenDB.getTokensByUserID(userID)


class TokenManagerHandler(TokenManagerHandlerMixin, TornadoService):
pass

0 comments on commit b90332c

Please sign in to comment.