Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.0] IAM2CS: ForceNickname, fix duplicate accounts for users #7784

Merged
merged 6 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/DIRAC/ConfigurationSystem/Agent/VOMS2CSAgent.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def __init__(self, *args, **kwargs):
self.syncPluginName = None
self.compareWithIAM = False
self.useIAM = False
self.forceNickname = False

def initialize(self):
"""Initialize the default parameters"""
Expand All @@ -70,6 +71,7 @@ def initialize(self):
self.syncPluginName = self.am_getOption("SyncPluginName", self.syncPluginName)
self.compareWithIAM = self.am_getOption("CompareWithIAM", self.compareWithIAM)
self.useIAM = self.am_getOption("UseIAM", self.useIAM)
self.forceNickname = self.am_getOption("ForceNickname", self.forceNickname)

self.detailedReport = self.am_getOption("DetailedReport", self.detailedReport)
self.mailFrom = self.am_getOption("MailFrom", self.mailFrom)
Expand Down Expand Up @@ -127,6 +129,7 @@ def execute(self):
compareWithIAM=compareWithIAM,
useIAM=useIAM,
accessToken=accessToken,
forceNickname=self.forceNickname,
)

result = self.__syncCSWithVOMS( # pylint: disable=unexpected-keyword-arg
Expand All @@ -145,6 +148,7 @@ def execute(self):
csapi = resultDict.get("CSAPI")
adminMessages = resultDict.get("AdminMessages", {"Errors": [], "Info": []})
voChanged = resultDict.get("VOChanged", False)
noNickname = resultDict.get("NoNickname", [])
self.log.info(
"Run user results",
": new %d, modified %d, deleted %d, new/suspended %d"
Expand Down Expand Up @@ -194,6 +198,11 @@ def execute(self):
mailMsg = ""
if adminMessages["Errors"]:
mailMsg += "\nErrors list:\n %s" % "\n ".join(adminMessages["Errors"])
if self.forceNickname and noNickname:
mailMsg += "There are users without nicknames in the IAM\n"
for entry in noNickname:
mailMsg += str(entry)
mailMsg += "\n\n"
if adminMessages["Info"]:
mailMsg += "\nRun result:\n %s" % "\n ".join(adminMessages["Info"])
if self.detailedReport:
Expand Down
36 changes: 23 additions & 13 deletions src/DIRAC/ConfigurationSystem/Client/VOMS2CSSynchronizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ def __init__(
compareWithIAM=False,
useIAM=False,
accessToken=None,
forceNickname=False,
):
"""VOMS2CSSynchronizer class constructor

Expand Down Expand Up @@ -165,6 +166,7 @@ def __init__(
self.compareWithIAM = compareWithIAM
self.useIAM = useIAM
self.accessToken = accessToken
self.forceNickname = forceNickname

if syncPluginName:
objLoader = ObjectLoader()
Expand Down Expand Up @@ -257,7 +259,8 @@ def syncCSWithVOMS(self):
if not result["OK"]:
self.log.error("Could not retrieve user information", result["Message"])
return result

if getUserErrors := result.get("Errors", []):
andresailer marked this conversation as resolved.
Show resolved Hide resolved
self.adminMsgs["Errors"].extend(getUserErrors)
self.vomsUserDict = result["Value"]
message = f"There are {len(self.vomsUserDict)} user entries in VOMS for VO {self.vomsVOName}"
self.adminMsgs["Info"].append(message)
Expand Down Expand Up @@ -329,30 +332,37 @@ def syncCSWithVOMS(self):
# Check the nickName in the same VO to see if the user is already registered
# with another DN
nickName = self.vomsUserDict[dn].get("nickname")
if not nickName and self.forceNickname:
resultDict["NoNickname"].append(self.vomsUserDict[dn])
self.log.error("No nickname defined for", self.vomsUserDict[dn])
continue
if nickName in diracUserDict or nickName in newAddedUserDict:
diracName = nickName
# This is a flag for adding the new DN to an already existing user
newDNForExistingUser = dn

# We have a real new user
if not diracName:
if nickName:
newDiracName = nickName.strip()
else:
newDiracName = self.getUserName(dn)

# Do not consider users with Suspended status in VOMS
if self.vomsUserDict[dn]["suspended"] or self.vomsUserDict[dn]["certSuspended"]:
resultDict["SuspendedUsers"].append(newDiracName)
continue

# If the chosen user name exists already, append a distinguishing suffix
ind = 1
trialName = newDiracName
while newDiracName in allDiracUsers:
# We have a user with the same name but with a different DN
newDiracName = "%s_%d" % (trialName, ind)
ind += 1
# if we have a nickname, we use the nickname no
# matter what so we can have users from different
# VOs with the same nickname / username
if nickName:
newDiracName = nickName.strip()
else:
newDiracName = self.getUserName(dn)

# If the chosen user name exists already, append a distinguishing suffix
ind = 1
trialName = newDiracName
while newDiracName in allDiracUsers:
# We have a user with the same name but with a different DN
newDiracName = "%s_%d" % (trialName, ind)
ind += 1

# We now have everything to add the new user
userDict = {"DN": dn, "CA": self.vomsUserDict[dn]["CA"], "Email": self.vomsUserDict[dn]["mail"]}
Expand Down
2 changes: 2 additions & 0 deletions src/DIRAC/ConfigurationSystem/ConfigTemplate.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ Agents
CompareWithIAM = False
# If set to true, will only query IAM and return the list of users from there
UseIAM = False
# If set to true only users with a nickname attribute defined in the IAM are created in DIRAC
ForceNickname = False
}
##END
##BEGIN GOCDB2CSAgent
Expand Down
13 changes: 8 additions & 5 deletions src/DIRAC/Core/Security/IAMService.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def __init__(self, access_token, vo=None):
:param str access_token: the token used to talk to IAM, with the scim:read property

"""
self.log = gLogger.getSubLogger(self.__class__.__name__)

if not access_token:
raise ValueError("access_token not set")
Expand Down Expand Up @@ -127,13 +128,15 @@ def convert_iam_to_voms(iam_output):
def getUsers(self):
self.iam_users_raw = self._getIamUserDump()
users = {}
errors = 0
errors = []
for user in self.iam_users_raw:
try:
users.update(self.convert_iam_to_voms(user))
except Exception as e:
errors += 1
print(f"Could not convert {user['name']} {e!r} ")
print(f"There were in total {errors} errors")
errors.append(f"{user['name']} {e!r}")
self.log.error("Could not convert", f"{user['name']} {e!r}")
self.log.error("There were in total", f"{len(errors)} errors")
self.userDict = dict(users)
return S_OK(users)
result = S_OK(users)
result["Errors"] = errors
return result
Loading