diff --git a/src/DIRAC/FrameworkSystem/Service/DisetTokenManagerHandler.py b/src/DIRAC/FrameworkSystem/Service/DisetTokenManagerHandler.py new file mode 100644 index 00000000000..fe9c1d90cfc --- /dev/null +++ b/src/DIRAC/FrameworkSystem/Service/DisetTokenManagerHandler.py @@ -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 ` +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 diff --git a/src/DIRAC/FrameworkSystem/Service/TokenManagerHandler.py b/src/DIRAC/FrameworkSystem/Service/TokenManagerHandler.py index 70e9bc24907..2bcd6535671 100644 --- a/src/DIRAC/FrameworkSystem/Service/TokenManagerHandler.py +++ b/src/DIRAC/FrameworkSystem/Service/TokenManagerHandler.py @@ -32,8 +32,6 @@ 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 @@ -41,12 +39,10 @@ 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 @@ -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 @@ -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 @@ -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. @@ -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, @@ -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 @@ -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 @@ -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 @@ -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