Skip to content

Commit a7b5630

Browse files
authored
Merge pull request #5896 from TaykYoku/8.0_fixOAuth-1
[8.0][OAuth 2] dirac-login try to connect to DIRAC AS if no local certificate
2 parents c066d54 + 0fea696 commit a7b5630

File tree

1 file changed

+66
-94
lines changed

1 file changed

+66
-94
lines changed

src/DIRAC/FrameworkSystem/scripts/dirac_login.py

Lines changed: 66 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
#!/usr/bin/env python
22
"""
33
With this command you can log in to DIRAC.
4-
54
There are two options:
6-
75
- using a user certificate, creating a proxy.
86
- go through DIRAC Authorization Server by selecting your Identity Provider.
97
10-
118
Example:
129
# Login with default group
1310
$ dirac-login
@@ -19,11 +16,11 @@
1916
import os
2017
import sys
2118
import copy
22-
from prompt_toolkit import prompt
19+
from prompt_toolkit import prompt, print_formatted_text as print, HTML
2320

2421
import DIRAC
2522
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
2724
from DIRAC.Core.Security.ProxyFile import writeToProxyFile
2825
from DIRAC.Core.Security.ProxyInfo import getProxyInfo, formatProxyInfoAsString
2926
from DIRAC.Core.Security.X509Chain import X509Chain # pylint: disable=import-error
@@ -37,6 +34,11 @@
3734
getTokenFileLocation,
3835
)
3936

37+
# This value shows what authorization way will be by default
38+
DEFAULT_AUTH_WAY = "certificate" # possible values are "certificate", "diracas"
39+
# This value shows what response will be by default
40+
DEFAULT_RESPONSE = "proxy" # possible values are "proxy", "token"
41+
4042

4143
class Params:
4244
"""This class describes the input parameters"""
@@ -54,109 +56,76 @@ def __init__(self):
5456
self.issuer = None
5557
self.certLoc = None
5658
self.keyLoc = None
57-
self.result = "proxy"
58-
self.authWith = "certificate"
59+
self.response = DEFAULT_RESPONSE
60+
self.authWith = DEFAULT_AUTH_WAY
5961
self.enableCS = True
6062

61-
def disableCS(self, _arg) -> dict:
62-
"""Set issuer
63-
64-
:param arg: issuer
65-
"""
63+
def disableCS(self, _) -> dict:
64+
"""Disable CS"""
6665
self.enableCS = False
6766
return S_OK()
6867

69-
def setIssuer(self, arg: str) -> dict:
70-
"""Set issuer
71-
72-
:param arg: issuer
73-
"""
68+
def setIssuer(self, issuer: str) -> dict:
69+
"""Set DIRAC Authorization Server issuer"""
7470
self.useDIRACAS(None)
75-
self.issuer = arg
71+
self.issuer = issuer
7672
return S_OK()
7773

78-
def useDIRACAS(self, _arg) -> dict:
79-
"""Use DIRAC AS
80-
81-
:param _arg: unuse
82-
"""
74+
def useDIRACAS(self, _) -> dict:
75+
"""Use DIRAC AS"""
8376
self.authWith = "diracas"
8477
return S_OK()
8578

86-
def useCertificate(self, _arg) -> dict:
87-
"""Use certificate
88-
89-
:param _arg: unuse
90-
"""
79+
def useCertificate(self, _) -> dict:
80+
"""Use certificate"""
9181
os.environ["DIRAC_USE_ACCESS_TOKEN"] = "false"
9282
self.authWith = "certificate"
93-
self.result = "proxy"
83+
self.response = "proxy"
9484
return S_OK()
9585

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.")
86+
def setCertificate(self, filePath: str) -> dict:
87+
"""Set certificate file path"""
88+
if not os.path.exists(filePath):
89+
DIRAC.gLogger.error(f"{filePath} does not exist.")
10390
DIRAC.exit(1)
10491
self.useCertificate(None)
105-
self.certLoc = arg
92+
self.certLoc = filePath
10693
return S_OK()
10794

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.")
95+
def setPrivateKey(self, filePath: str) -> dict:
96+
"""Set private key file path"""
97+
if not os.path.exists(filePath):
98+
DIRAC.gLogger.error(f"{filePath} is not exist.")
11599
DIRAC.exit(1)
116100
self.useCertificate(None)
117-
self.keyLoc = arg
101+
self.keyLoc = filePath
118102
return S_OK()
119103

120-
def setOutputFile(self, arg: str) -> dict:
121-
"""Set output file location
122-
123-
:param arg: output file location
124-
"""
125-
self.outputFile = arg
104+
def setOutputFile(self, filePath: str) -> dict:
105+
"""Set output file location"""
106+
self.outputFile = filePath
126107
return S_OK()
127108

128-
def setLifetime(self, arg: str) -> dict:
129-
"""Set proxy lifetime
130-
131-
:param arg: lifetime
132-
"""
133-
self.lifetime = arg
109+
def setLifetime(self, lifetime: str) -> dict:
110+
"""Set proxy lifetime"""
111+
self.lifetime = lifetime
134112
return S_OK()
135113

136-
def setProxy(self, _arg) -> dict:
137-
"""Return proxy
138-
139-
:param _arg: unuse
140-
"""
114+
def setProxy(self, _) -> dict:
115+
"""Return proxy"""
141116
os.environ["DIRAC_USE_ACCESS_TOKEN"] = "false"
142-
self.result = "proxy"
117+
self.response = "proxy"
143118
return S_OK()
144119

145-
def setToken(self, _arg) -> dict:
146-
"""Return tokens
147-
148-
:param _arg: unuse
149-
"""
120+
def setToken(self, _) -> dict:
121+
"""Return tokens"""
150122
os.environ["DIRAC_USE_ACCESS_TOKEN"] = "true"
151123
self.useDIRACAS(None)
152-
self.result = "token"
124+
self.response = "token"
153125
return S_OK()
154126

155-
def authStatus(self, _arg) -> dict:
156-
"""Get authorization status
157-
158-
:param _arg: unuse
159-
"""
127+
def authStatus(self, _) -> dict:
128+
"""Get authorization status"""
160129
result = self.getAuthStatus()
161130
if result["OK"]:
162131
self.howToSwitch()
@@ -208,8 +177,8 @@ def doOAuthMagic(self):
208177
idpObj = result["Value"]
209178
if self.group and self.group not in self.scopes:
210179
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)
180+
if self.response == "proxy" and self.response not in self.scopes:
181+
self.scopes.append(self.response)
213182
if self.lifetime:
214183
self.scopes.append("lifetime:%s" % (int(self.lifetime or 12) * 3600))
215184
idpObj.scope = "+".join(self.scopes) if self.scopes else ""
@@ -219,8 +188,8 @@ def doOAuthMagic(self):
219188
if not result["OK"]:
220189
return result
221190

222-
if self.result == "proxy":
223-
self.outputFile = self.outputFile or Locations.getDefaultProxyLocation()
191+
if self.response == "proxy":
192+
self.outputFile = self.outputFile or getDefaultProxyLocation()
224193
# Save new proxy certificate
225194
result = writeToProxyFile(idpObj.token["proxy"].encode("UTF-8"), self.outputFile)
226195
if not result["OK"]:
@@ -261,8 +230,10 @@ def loginWithCertificate(self):
261230
"""Login with certificate"""
262231
# Search certificate and key
263232
if not self.certLoc or not self.keyLoc:
264-
cakLoc = Locations.getCertificateAndKeyLocation()
265-
if not cakLoc:
233+
if not (cakLoc := getCertificateAndKeyLocation()):
234+
if not self.authWith: # if user do not choose this way
235+
print(HTML("<yellow>Can't find user certificate and key</yellow>, trying to connact to DIRAC AS.."))
236+
return self.doOAuthMagic() # Then try to use DIRAC AS
266237
return S_ERROR("Can't find user certificate and key")
267238
self.certLoc = self.certLoc or cakLoc[0]
268239
self.keyLoc = self.keyLoc or cakLoc[1]
@@ -287,7 +258,7 @@ def loginWithCertificate(self):
287258
proxy = copy.copy(chain)
288259

289260
# Create local proxy with group
290-
self.outputFile = self.outputFile or Locations.getDefaultProxyLocation()
261+
self.outputFile = self.outputFile or getDefaultProxyLocation()
291262
result = chain.generateProxyToFile(self.outputFile, int(self.lifetime or 12) * 3600, self.group)
292263
if not result["OK"]:
293264
return S_ERROR(f"Couldn't generate proxy: {result['Message']}")
@@ -325,9 +296,10 @@ def howToSwitch(self) -> bool:
325296
if src == "conf":
326297
msg += f" set /DIRAC/Security/UseTokens={not useTokens} in dirac.cfg\nor\n"
327298
msg += f" export DIRAC_USE_ACCESS_TOKEN={not useTokens}\n"
328-
gLogger.notice(msg)
329299

330-
return useTokens
300+
# Show infomation message only if the current state of the user environment does not match the authorization result
301+
if (useTokens and (self.response == "proxy")) or (not useTokens and (self.response == "token")):
302+
gLogger.notice(msg)
331303

332304
def getAuthStatus(self):
333305
"""Try to get user authorization status.
@@ -339,7 +311,7 @@ def getAuthStatus(self):
339311
return S_ERROR("Cannot contact CS.")
340312
gConfig.forceRefresh()
341313

342-
if self.result == "proxy":
314+
if self.response == "proxy":
343315
result = getProxyInfo(self.outputFile)
344316
if result["OK"]:
345317
gLogger.notice(formatProxyInfoAsString(result["Value"]))
@@ -353,8 +325,8 @@ def getAuthStatus(self):
353325

354326
@Script()
355327
def main():
356-
p = Params()
357-
p.registerCLISwitches()
328+
userParams = Params()
329+
userParams.registerCLISwitches()
358330

359331
# Check time
360332
deviation = getClockDeviation()
@@ -374,24 +346,24 @@ def main():
374346
)
375347
DIRAC.exit(1)
376348

377-
p.group, p.scopes = Script.getPositionalArgs(group=True)
349+
userParams.group, userParams.scopes = Script.getPositionalArgs(group=True)
378350
# If you have chosen to use a certificate then a proxy will be generated locally using the specified certificate
379-
if p.authWith == "certificate":
380-
result = p.loginWithCertificate()
351+
if userParams.authWith == "certificate":
352+
result = userParams.loginWithCertificate()
381353

382354
# Otherwise, you must log in to the authorization server to gain access
383355
else:
384-
result = p.doOAuthMagic()
356+
result = userParams.doOAuthMagic()
385357

386358
# Print authorization status
387-
if result["OK"] and p.enableCS:
388-
result = p.getAuthStatus()
359+
if result["OK"] and userParams.enableCS:
360+
result = userParams.getAuthStatus()
389361

390362
if not result["OK"]:
391-
gLogger.fatal(result["Message"])
363+
print(HTML(f"<red>{result['Message']}</red>"))
392364
sys.exit(1)
393365

394-
p.howToSwitch()
366+
userParams.howToSwitch()
395367
sys.exit(0)
396368

397369

0 commit comments

Comments
 (0)