1
1
#!/usr/bin/env python
2
2
"""
3
3
With this command you can log in to DIRAC.
4
-
5
4
There are two options:
6
-
7
5
- using a user certificate, creating a proxy.
8
6
- go through DIRAC Authorization Server by selecting your Identity Provider.
9
7
10
-
11
8
Example:
12
9
# Login with default group
13
10
$ dirac-login
19
16
import os
20
17
import sys
21
18
import copy
22
- from prompt_toolkit import prompt
19
+ from prompt_toolkit import prompt , print_formatted_text as print , HTML
23
20
24
21
import DIRAC
25
22
from DIRAC import gConfig , gLogger , S_OK , S_ERROR
26
- from DIRAC .Core .Security import Locations
23
+ from DIRAC .Core .Security .Locations import getDefaultProxyLocation , getCertificateAndKeyLocation
24
+ from DIRAC .Core .Security .VOMS import VOMS
27
25
from DIRAC .Core .Security .ProxyFile import writeToProxyFile
28
26
from DIRAC .Core .Security .ProxyInfo import getProxyInfo , formatProxyInfoAsString
29
27
from DIRAC .Core .Security .X509Chain import X509Chain # pylint: disable=import-error
36
34
readTokenFromFile ,
37
35
getTokenFileLocation ,
38
36
)
37
+ from DIRAC .ConfigurationSystem .Client .Helpers .Registry import (
38
+ getGroupOption ,
39
+ getVOMSAttributeForGroup ,
40
+ getVOMSVOForGroup ,
41
+ findDefaultGroupForDN ,
42
+ )
43
+
44
+ # This value shows what authorization way will be by default
45
+ DEFAULT_AUTH_WAY = "certificate" # possible values are "certificate", "diracas"
46
+ # This value shows what response will be by default
47
+ DEFAULT_RESPONSE = "proxy" # possible values are "proxy", "token"
39
48
40
49
41
50
class Params :
@@ -54,109 +63,76 @@ def __init__(self):
54
63
self .issuer = None
55
64
self .certLoc = None
56
65
self .keyLoc = None
57
- self .result = "proxy"
58
- self .authWith = "certificate"
66
+ self .response = DEFAULT_RESPONSE
67
+ self .authWith = DEFAULT_AUTH_WAY
59
68
self .enableCS = True
60
69
61
- def disableCS (self , _arg ) -> dict :
62
- """Set issuer
63
-
64
- :param arg: issuer
65
- """
70
+ def disableCS (self , _ ) -> dict :
71
+ """Disable CS"""
66
72
self .enableCS = False
67
73
return S_OK ()
68
74
69
- def setIssuer (self , arg : str ) -> dict :
70
- """Set issuer
71
-
72
- :param arg: issuer
73
- """
75
+ def setIssuer (self , issuer : str ) -> dict :
76
+ """Set DIRAC Authorization Server issuer"""
74
77
self .useDIRACAS (None )
75
- self .issuer = arg
78
+ self .issuer = issuer
76
79
return S_OK ()
77
80
78
- def useDIRACAS (self , _arg ) -> dict :
79
- """Use DIRAC AS
80
-
81
- :param _arg: unuse
82
- """
81
+ def useDIRACAS (self , _ ) -> dict :
82
+ """Use DIRAC AS"""
83
83
self .authWith = "diracas"
84
84
return S_OK ()
85
85
86
- def useCertificate (self , _arg ) -> dict :
87
- """Use certificate
88
-
89
- :param _arg: unuse
90
- """
86
+ def useCertificate (self , _ ) -> dict :
87
+ """Use certificate"""
91
88
os .environ ["DIRAC_USE_ACCESS_TOKEN" ] = "false"
92
89
self .authWith = "certificate"
93
- self .result = "proxy"
90
+ self .response = "proxy"
94
91
return S_OK ()
95
92
96
- def setCertificate (self , arg : str ) -> dict :
97
- """Set certificate file path
98
-
99
- :param arg: path
100
- """
101
- if not os .path .exists (arg ):
102
- DIRAC .gLogger .error (f"{ arg } does not exist." )
93
+ def setCertificate (self , filePath : str ) -> dict :
94
+ """Set certificate file path"""
95
+ if not os .path .exists (filePath ):
96
+ DIRAC .gLogger .error (f"{ filePath } does not exist." )
103
97
DIRAC .exit (1 )
104
98
self .useCertificate (None )
105
- self .certLoc = arg
99
+ self .certLoc = filePath
106
100
return S_OK ()
107
101
108
- def setPrivateKey (self , arg : str ) -> dict :
109
- """Set private key file path
110
-
111
- :param arg: path
112
- """
113
- if not os .path .exists (arg ):
114
- DIRAC .gLogger .error (f"{ arg } is not exist." )
102
+ def setPrivateKey (self , filePath : str ) -> dict :
103
+ """Set private key file path"""
104
+ if not os .path .exists (filePath ):
105
+ DIRAC .gLogger .error (f"{ filePath } is not exist." )
115
106
DIRAC .exit (1 )
116
107
self .useCertificate (None )
117
- self .keyLoc = arg
108
+ self .keyLoc = filePath
118
109
return S_OK ()
119
110
120
- def setOutputFile (self , arg : str ) -> dict :
121
- """Set output file location
122
-
123
- :param arg: output file location
124
- """
125
- self .outputFile = arg
111
+ def setOutputFile (self , filePath : str ) -> dict :
112
+ """Set output file location"""
113
+ self .outputFile = filePath
126
114
return S_OK ()
127
115
128
- def setLifetime (self , arg : str ) -> dict :
129
- """Set proxy lifetime
130
-
131
- :param arg: lifetime
132
- """
133
- self .lifetime = arg
116
+ def setLifetime (self , lifetime : str ) -> dict :
117
+ """Set proxy lifetime"""
118
+ self .lifetime = lifetime
134
119
return S_OK ()
135
120
136
- def setProxy (self , _arg ) -> dict :
137
- """Return proxy
138
-
139
- :param _arg: unuse
140
- """
121
+ def setProxy (self , _ ) -> dict :
122
+ """Return proxy"""
141
123
os .environ ["DIRAC_USE_ACCESS_TOKEN" ] = "false"
142
- self .result = "proxy"
124
+ self .response = "proxy"
143
125
return S_OK ()
144
126
145
- def setToken (self , _arg ) -> dict :
146
- """Return tokens
147
-
148
- :param _arg: unuse
149
- """
127
+ def setToken (self , _ ) -> dict :
128
+ """Return tokens"""
150
129
os .environ ["DIRAC_USE_ACCESS_TOKEN" ] = "true"
151
130
self .useDIRACAS (None )
152
- self .result = "token"
131
+ self .response = "token"
153
132
return S_OK ()
154
133
155
- def authStatus (self , _arg ) -> dict :
156
- """Get authorization status
157
-
158
- :param _arg: unuse
159
- """
134
+ def authStatus (self , _ ) -> dict :
135
+ """Get authorization status"""
160
136
result = self .getAuthStatus ()
161
137
if result ["OK" ]:
162
138
self .howToSwitch ()
@@ -208,8 +184,8 @@ def doOAuthMagic(self):
208
184
idpObj = result ["Value" ]
209
185
if self .group and self .group not in self .scopes :
210
186
self .scopes .append (f"g:{ self .group } " )
211
- if self .result == "proxy" and self .result not in self .scopes :
212
- self .scopes .append (self .result )
187
+ if self .response == "proxy" and self .response not in self .scopes :
188
+ self .scopes .append (self .response )
213
189
if self .lifetime :
214
190
self .scopes .append ("lifetime:%s" % (int (self .lifetime or 12 ) * 3600 ))
215
191
idpObj .scope = "+" .join (self .scopes ) if self .scopes else ""
@@ -219,8 +195,8 @@ def doOAuthMagic(self):
219
195
if not result ["OK" ]:
220
196
return result
221
197
222
- if self .result == "proxy" :
223
- self .outputFile = self .outputFile or Locations . getDefaultProxyLocation ()
198
+ if self .response == "proxy" :
199
+ self .outputFile = self .outputFile or getDefaultProxyLocation ()
224
200
# Save new proxy certificate
225
201
result = writeToProxyFile (idpObj .token ["proxy" ].encode ("UTF-8" ), self .outputFile )
226
202
if not result ["OK" ]:
@@ -261,15 +237,16 @@ def loginWithCertificate(self):
261
237
"""Login with certificate"""
262
238
# Search certificate and key
263
239
if not self .certLoc or not self .keyLoc :
264
- cakLoc = Locations .getCertificateAndKeyLocation ()
265
- if not cakLoc :
240
+ if not (cakLoc := getCertificateAndKeyLocation ()):
241
+ if not self .authWith : # if user do not choose this way
242
+ print (HTML ("<yellow>Can't find user certificate and key</yellow>, trying to connact to DIRAC AS.." ))
243
+ return self .doOAuthMagic () # Then try to use DIRAC AS
266
244
return S_ERROR ("Can't find user certificate and key" )
267
245
self .certLoc = self .certLoc or cakLoc [0 ]
268
246
self .keyLoc = self .keyLoc or cakLoc [1 ]
269
247
270
248
chain = X509Chain ()
271
249
# Load user cert and key
272
-
273
250
if (result := chain .loadChainFromFile (self .certLoc ))["OK" ]:
274
251
# We try to download the key first without a password
275
252
if not (result := chain .loadKeyFromFile (self .keyLoc ))["OK" ]:
@@ -288,17 +265,30 @@ def loginWithCertificate(self):
288
265
proxy = copy .copy (chain )
289
266
290
267
# Create local proxy with group
291
- self .outputFile = self .outputFile or Locations .getDefaultProxyLocation ()
292
- result = chain .generateProxyToFile (self .outputFile , int (self .lifetime or 12 ) * 3600 , self .group )
268
+ self .outputFile = self .outputFile or getDefaultProxyLocation ()
269
+ parameters = (self .outputFile , int (self .lifetime or 12 ) * 3600 , self .group )
270
+
271
+ # Add a VOMS extension if the group requires it
272
+ if (result := chain .generateProxyToFile (* parameters ))["OK" ] and (result := self .__enableCS ())["OK" ]:
273
+ if not self .group and (result := findDefaultGroupForDN (credentials ["DN" ]))["OK" ]:
274
+ self .group = result ["Value" ] # Use default group if user don't set it
275
+ # based on the configuration we decide whether to add VOMS extensions
276
+ if getGroupOption (self .group , "AutoAddVOMS" , False ):
277
+ if not (vomsAttr := getVOMSAttributeForGroup (self .group )):
278
+ print (HTML (f"<yellow>No VOMS attribute foud for { self .group } </yellow>" ))
279
+ else :
280
+ vo = getVOMSVOForGroup (self .group )
281
+ if not (result := VOMS ().setVOMSAttributes (chain , attribute = vomsAttr , vo = vo ))["OK" ]:
282
+ return S_ERROR (f"Failed adding VOMS attribute: { result ['Message' ]} " )
283
+ chain = result ["Value" ]
284
+ result = chain .generateProxyToFile (* parameters )
293
285
if not result ["OK" ]:
294
286
return S_ERROR (f"Couldn't generate proxy: { result ['Message' ]} " )
295
287
296
288
if self .enableCS :
297
289
# After creating the proxy, we can try to connect to the server
298
- result = Script .enableCS ()
299
- if not result ["OK" ]:
300
- return S_ERROR (f"Cannot contact CS: { result ['Message' ]} " )
301
- gConfig .forceRefresh ()
290
+ if not (result := self .__enableCS ())["OK" ]:
291
+ return result
302
292
303
293
# Step 2: Upload proxy to DIRAC server
304
294
result = gProxyManager .getUploadedProxyLifeTime (credentials ["subject" ])
@@ -312,6 +302,11 @@ def loginWithCertificate(self):
312
302
return gProxyManager .uploadProxy (proxy )
313
303
return S_OK ()
314
304
305
+ def __enableCS (self ):
306
+ if not (result := Script .enableCS ())["OK" ] or not (result := gConfig .forceRefresh ())["OK" ]:
307
+ return S_ERROR (f"Cannot contact CS: { result ['Message' ]} " )
308
+ return result
309
+
315
310
def howToSwitch (self ) -> bool :
316
311
"""Helper message, how to switch access type(proxy or access token)"""
317
312
if "DIRAC_USE_ACCESS_TOKEN" in self .ENV :
@@ -326,21 +321,19 @@ def howToSwitch(self) -> bool:
326
321
if src == "conf" :
327
322
msg += f" set /DIRAC/Security/UseTokens={ not useTokens } in dirac.cfg\n or\n "
328
323
msg += f" export DIRAC_USE_ACCESS_TOKEN={ not useTokens } \n "
329
- gLogger .notice (msg )
330
324
331
- return useTokens
325
+ # Show infomation message only if the current state of the user environment does not match the authorization result
326
+ if (useTokens and (self .response == "proxy" )) or (not useTokens and (self .response == "token" )):
327
+ gLogger .notice (msg )
332
328
333
329
def getAuthStatus (self ):
334
330
"""Try to get user authorization status.
335
-
336
331
:return: S_OK()/S_ERROR()
337
332
"""
338
- result = Script .enableCS ()
339
- if not result ["OK" ]:
340
- return S_ERROR ("Cannot contact CS." )
341
- gConfig .forceRefresh ()
333
+ if not (result := self .__enableCS ())["OK" ]:
334
+ return result
342
335
343
- if self .result == "proxy" :
336
+ if self .response == "proxy" :
344
337
result = getProxyInfo (self .outputFile )
345
338
if result ["OK" ]:
346
339
gLogger .notice (formatProxyInfoAsString (result ["Value" ]))
@@ -354,8 +347,8 @@ def getAuthStatus(self):
354
347
355
348
@Script ()
356
349
def main ():
357
- p = Params ()
358
- p .registerCLISwitches ()
350
+ userParams = Params ()
351
+ userParams .registerCLISwitches ()
359
352
360
353
# Check time
361
354
deviation = getClockDeviation ()
@@ -375,24 +368,24 @@ def main():
375
368
)
376
369
DIRAC .exit (1 )
377
370
378
- p .group , p .scopes = Script .getPositionalArgs (group = True )
371
+ userParams .group , userParams .scopes = Script .getPositionalArgs (group = True )
379
372
# If you have chosen to use a certificate then a proxy will be generated locally using the specified certificate
380
- if p .authWith == "certificate" :
381
- result = p .loginWithCertificate ()
373
+ if userParams .authWith == "certificate" :
374
+ result = userParams .loginWithCertificate ()
382
375
383
376
# Otherwise, you must log in to the authorization server to gain access
384
377
else :
385
- result = p .doOAuthMagic ()
378
+ result = userParams .doOAuthMagic ()
386
379
387
380
# Print authorization status
388
- if result ["OK" ] and p .enableCS :
389
- result = p .getAuthStatus ()
381
+ if result ["OK" ] and userParams .enableCS :
382
+ result = userParams .getAuthStatus ()
390
383
391
384
if not result ["OK" ]:
392
- gLogger . fatal ( result [" Message" ] )
385
+ print ( HTML ( f"<red> { result [' Message' ] } </red>" ) )
393
386
sys .exit (1 )
394
387
395
- p .howToSwitch ()
388
+ userParams .howToSwitch ()
396
389
sys .exit (0 )
397
390
398
391
0 commit comments