Skip to content

Commit 1dcaf5f

Browse files
committed
feat (diracx): exchange a proxy for an equivalent token
1 parent 0f6a4c7 commit 1dcaf5f

File tree

4 files changed

+97
-1
lines changed

4 files changed

+97
-1
lines changed

docs/source/AdministratorGuide/ServerInstallations/environment_variable_configuration.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ DIRAC_DEPRECATED_FAIL
2121
DIRAC_ENABLE_DIRACX_JOB_MONITORING
2222
If set, calls the diracx job monitoring service. Off by default.
2323

24+
DIRAC_ENABLE_DIRACX_LOGIN
25+
If set, retrieve a DiracX token when calling dirac-proxy-init or dirac-login
26+
2427
DIRAC_FEWER_CFG_LOCKS
2528
If ``true`` or ``yes`` or ``on`` or ``1`` or ``y`` or ``t``, DIRAC will reduce the number of locks used when accessing the CS for better performance (default, ``no``).
2629

src/DIRAC/FrameworkSystem/Service/ProxyManagerHandler.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
:dedent: 2
77
:caption: ProxyManager options
88
"""
9+
910
from DIRAC import gLogger, S_OK, S_ERROR
1011
from DIRAC.Core.DISET.RequestHandler import RequestHandler, getServiceOption
1112
from DIRAC.Core.Security import Properties
@@ -406,6 +407,45 @@ def export_getVOMSProxyWithToken(self, userDN, userGroup, requestPem, requiredLi
406407
self.__proxyDB.logAction("download voms proxy with token", credDict["DN"], credDict["group"], userDN, userGroup)
407408
return self.__getVOMSProxy(userDN, userGroup, requestPem, requiredLifetime, vomsAttribute, True)
408409

410+
types_exchangeProxyForToken = []
411+
412+
def export_exchangeProxyForToken(self):
413+
"""Exchange a proxy for an equivalent token to be used with diracx"""
414+
try:
415+
from diracx.routers.auth import ( # pylint: disable=import-error
416+
AuthSettings,
417+
create_access_token,
418+
TokenResponse,
419+
) # pylint: disable=import-error
420+
421+
authSettings = AuthSettings()
422+
423+
from uuid import uuid4
424+
425+
credDict = self.getRemoteCredentials()
426+
vo = Registry.getVOForGroup(credDict["group"])
427+
payload = {
428+
"sub": f"{vo}:{credDict['username']}",
429+
"vo": vo,
430+
"aud": authSettings.token_audience,
431+
"iss": authSettings.token_issuer,
432+
"dirac_properties": list(
433+
set(credDict.get("groupProperties", [])) | set(credDict.get("properties", []))
434+
),
435+
"jti": str(uuid4()),
436+
"preferred_username": credDict["username"],
437+
"dirac_group": credDict["group"],
438+
}
439+
return S_OK(
440+
TokenResponse(
441+
access_token=create_access_token(payload, authSettings),
442+
expires_in=authSettings.access_token_expire_minutes * 60,
443+
state="None",
444+
).dict()
445+
)
446+
except Exception as e:
447+
return S_ERROR(f"Could not get token: {e!r}")
448+
409449

410450
class ProxyManagerHandler(ProxyManagerHandlerMixin, RequestHandler):
411451
pass

src/DIRAC/FrameworkSystem/scripts/dirac_login.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
import os
1717
import sys
1818
import copy
19+
import datetime
20+
import json
21+
from pathlib import Path
1922
from prompt_toolkit import prompt, print_formatted_text as print, HTML
2023

2124
import DIRAC
@@ -26,6 +29,13 @@
2629
from DIRAC.Core.Security.ProxyInfo import getProxyInfo, formatProxyInfoAsString
2730
from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error
2831
from DIRAC.Core.Base.Script import Script
32+
from DIRAC.Core.Base.Client import Client
33+
34+
35+
# token location
36+
DIRAC_TOKEN_FILE = Path.home() / ".cache" / "diracx" / "credentials.json"
37+
EXPIRES_GRACE_SECONDS = 15
38+
2939

3040
# At this point, we disable CS synchronization so that an error related
3141
# to the lack of a proxy certificate does not occur when trying to synchronize.
@@ -304,7 +314,27 @@ def loginWithCertificate(self):
304314
# Upload proxy to the server if it longer that uploaded one
305315
if credentials["secondsLeft"] > uploadedProxyLifetime:
306316
gLogger.notice("Upload proxy to server.")
307-
return gProxyManager.uploadProxy(proxy)
317+
res = gProxyManager.uploadProxy(proxy)
318+
if not res["OK"]:
319+
return res
320+
321+
# Get a token for use with diracx
322+
if os.getenv("DIRAC_ENABLE_DIRACX_LOGIN", "No").lower() in ("yes", "true"):
323+
res = Client(url="Framework/ProxyManager").exchangeProxyForToken()
324+
if not res["OK"]:
325+
return res
326+
DIRAC_TOKEN_FILE.parent.mkdir(parents=True, exist_ok=True)
327+
expires = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(
328+
seconds=res["Value"]["expires_in"] - EXPIRES_GRACE_SECONDS
329+
)
330+
credential_data = {
331+
"access_token": res["Value"]["access_token"],
332+
# TODO: "refresh_token":
333+
# TODO: "refresh_token_expires":
334+
"expires": expires.isoformat(),
335+
}
336+
DIRAC_TOKEN_FILE.write_text(json.dumps(credential_data))
337+
308338
return S_OK()
309339

310340
def __enableCS(self):

src/DIRAC/FrameworkSystem/scripts/dirac_proxy_init.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import os
1010
import sys
1111
import glob
12+
import json
1213
import time
1314
import datetime
1415

@@ -21,6 +22,12 @@
2122
from DIRAC.Core.Security.Locations import getCAsLocation
2223
from DIRAC.ConfigurationSystem.Client.Helpers import Registry
2324
from DIRAC.FrameworkSystem.Client.BundleDeliveryClient import BundleDeliveryClient
25+
from DIRAC.Core.Base.Client import Client
26+
from pathlib import Path
27+
28+
29+
DIRAC_TOKEN_FILE = Path.home() / ".cache" / "diracx" / "credentials.json"
30+
EXPIRES_GRACE_SECONDS = 15
2431

2532

2633
class Params(ProxyGeneration.CLIParams):
@@ -237,6 +244,22 @@ def doTheMagic(self):
237244
if self.__piParams.strict:
238245
return resultProxyUpload
239246

247+
if os.getenv("DIRAC_ENABLE_DIRACX_LOGIN", "No").lower() in ("yes", "true"):
248+
res = Client(url="Framework/ProxyManager").exchangeProxyForToken()
249+
if not res["OK"]:
250+
return res
251+
DIRAC_TOKEN_FILE.parent.mkdir(parents=True, exist_ok=True)
252+
expires = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(
253+
seconds=res["Value"]["expires_in"] - EXPIRES_GRACE_SECONDS
254+
)
255+
credential_data = {
256+
"access_token": res["Value"]["access_token"],
257+
# TODO: "refresh_token":
258+
# TODO: "refresh_token_expires":
259+
"expires": expires.isoformat(),
260+
}
261+
DIRAC_TOKEN_FILE.write_text(json.dumps(credential_data))
262+
240263
return S_OK()
241264

242265

0 commit comments

Comments
 (0)