Skip to content

Commit

Permalink
Merge pull request #7244 from chrisburr/diracx2
Browse files Browse the repository at this point in the history
Include EnabledVOs option in example dirac.cfg and DiracX token exchange
  • Loading branch information
chaen authored Oct 12, 2023
2 parents 1e2f6a7 + 5488406 commit 271a3e1
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 17 deletions.
3 changes: 2 additions & 1 deletion dirac.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ DiracX
URL = https://diracx.invalid:8000
# A key used to have priviledged interactions with diracx. see
LegacyExchangeApiKey = diracx:legacy:InsecureChangeMe

# List of VOs which should use DiracX via the legacy compatibility mechanism
EnabledVOs = gridpp,cta
}
### Registry section:
# Sections to register VOs, groups, users and hosts
Expand Down
7 changes: 6 additions & 1 deletion src/DIRAC/FrameworkSystem/Service/ProxyManagerHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,12 @@ def export_exchangeProxyForToken(self):
"""Exchange a proxy for an equivalent token to be used with diracx"""
from DIRAC.FrameworkSystem.Utilities.diracx import get_token

return get_token(self.getRemoteCredentials())
credDict = self.getRemoteCredentials()
return get_token(
credDict["username"],
credDict["group"],
set(credDict.get("groupProperties", []) + credDict.get("properties", [])),
)


class ProxyManagerHandler(ProxyManagerHandlerMixin, RequestHandler):
Expand Down
31 changes: 17 additions & 14 deletions src/DIRAC/FrameworkSystem/Utilities/diracx.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# pylint: disable=import-error

import requests

from cachetools import TTLCache, cached
from cachetools.keys import hashkey
from pathlib import Path
from tempfile import NamedTemporaryFile
from typing import Any
Expand All @@ -19,12 +19,10 @@

# How long tokens are kept
DEFAULT_TOKEN_CACHE_TTL = 5 * 60

# Add a cache not to query the token all the time
_token_cache = TTLCache(maxsize=100, ttl=DEFAULT_TOKEN_CACHE_TTL)
DEFAULT_TOKEN_CACHE_SIZE = 1024


def get_token(credDict, *, expires_minutes=None):
def get_token(username: str, group: str, dirac_properties: set[str], *, expires_minutes: int | None = None):
"""Do a legacy exchange to get a DiracX access_token+refresh_token"""
diracxUrl = gConfig.getValue("/DiracX/URL")
if not diracxUrl:
Expand All @@ -33,16 +31,13 @@ def get_token(credDict, *, expires_minutes=None):
if not apiKey:
raise ValueError("Missing mandatory /DiracX/LegacyExchangeApiKey configuration")

vo = Registry.getVOForGroup(credDict["group"])
dirac_properties = list(set(credDict.get("groupProperties", [])) | set(credDict.get("properties", [])))
group = credDict["group"]

vo = Registry.getVOForGroup(group)
scopes = [f"vo:{vo}", f"group:{group}"] + [f"property:{prop}" for prop in dirac_properties]

r = requests.get(
f"{diracxUrl}/api/auth/legacy-exchange",
params={
"preferred_username": credDict["username"],
"preferred_username": username,
"scope": " ".join(scopes),
"expires_minutes": expires_minutes,
},
Expand All @@ -55,10 +50,13 @@ def get_token(credDict, *, expires_minutes=None):
return r.json()


@cached(_token_cache, key=lambda x, y: repr(x))
def _get_token_file(credDict) -> Path:
@cached(
TTLCache(maxsize=DEFAULT_TOKEN_CACHE_SIZE, ttl=DEFAULT_TOKEN_CACHE_TTL),
key=lambda a, b, c: hashkey(a, b, *sorted(c)),
)
def _get_token_file(username: str, group: str, dirac_properties: set[str]) -> Path:
"""Write token to a temporary file and return the path to that file"""
data = get_token(credDict)
data = get_token(username, group, dirac_properties)
token_location = Path(NamedTemporaryFile().name)
write_credentials(TokenResponse(**data), location=token_location)
return token_location
Expand All @@ -76,7 +74,12 @@ def TheImpersonator(credDict: dict[str, Any]) -> DiracClient:
diracxUrl = gConfig.getValue("/DiracX/URL")
if not diracxUrl:
raise ValueError("Missing mandatory /DiracX/URL configuration")
token_location = _get_token_file(credDict)

token_location = _get_token_file(
credDict["username"],
credDict["group"],
set(credDict.get("groupProperties", []) + credDict.get("properties", [])),
)
pref = DiracxPreferences(url=diracxUrl, credentials_path=token_location)

return DiracClient(diracx_preferences=pref)
10 changes: 9 additions & 1 deletion src/DIRAC/FrameworkSystem/scripts/dirac_login.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,18 +319,26 @@ def loginWithCertificate(self):
if vo in enabledVOs:
from diracx.core.utils import write_credentials # pylint: disable=import-error
from diracx.core.models import TokenResponse # pylint: disable=import-error
from diracx.core.preferences import DiracxPreferences # pylint: disable=import-error

res = Client(url="Framework/ProxyManager").exchangeProxyForToken()
if not res["OK"]:
return res
token_content = res["Value"]

diracxUrl = gConfig.getValue("/DiracX/URL")
if not diracxUrl:
return S_ERROR("Missing mandatory /DiracX/URL configuration")

preferences = DiracxPreferences(url=diracxUrl)
write_credentials(
TokenResponse(
access_token=token_content["access_token"],
expires_in=token_content["expires_in"],
token_type=token_content.get("token_type"),
refresh_token=token_content.get("refresh_token"),
)
),
location=preferences.credentials_path,
)

return S_OK()
Expand Down

0 comments on commit 271a3e1

Please sign in to comment.