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

[9.0] Populate diracx user subs from IAM #7711

Merged
merged 1 commit into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
16 changes: 14 additions & 2 deletions src/DIRAC/ConfigurationSystem/Client/VOMS2CSSynchronizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from collections import defaultdict

from diraccfg import CFG
from DIRAC import S_OK, S_ERROR, gLogger, gConfig
from DIRAC.Core.Utilities.ReturnValues import returnValueOrRaise, convertToReturnValue
from DIRAC.Core.Security.IAMService import IAMService
Expand Down Expand Up @@ -162,6 +163,7 @@ def __init__(
self.autoLiftSuspendedStatus = autoLiftSuspendedStatus
self.voChanged = False
self.syncPlugin = None
self.iamSrv = None
self.compareWithIAM = compareWithIAM
self.useIAM = useIAM
self.accessToken = accessToken
Expand Down Expand Up @@ -222,8 +224,8 @@ def compareUsers(self, voms_users, iam_users):
@convertToReturnValue
def _getUsers(self):
if self.compareWithIAM or self.useIAM:
iamSrv = IAMService(self.accessToken, vo=self.vo)
iam_users = returnValueOrRaise(iamSrv.getUsers())
self.iamSrv = IAMService(self.accessToken, vo=self.vo)
iam_users = returnValueOrRaise(self.iamSrv.getUsers())
if self.useIAM:
return iam_users

Expand Down Expand Up @@ -567,6 +569,16 @@ def syncCSWithVOMS(self):
)
self.log.info("The following users to be checked for deletion:", "\n\t".join(sorted(oldUsers)))

# Try to fill in the DiracX section
if self.useIAM:
iam_subs = self.iamSrv.getUsersSub()
diracx_vo_config = {"DiracX": {"CsSync": {"VOs": {self.vo: {"UserSubjects": iam_subs}}}}}
iam_sub_cfg = CFG()
iam_sub_cfg.loadFromDict(diracx_vo_config)
result = self.csapi.mergeFromCFG(iam_sub_cfg)
if not result["OK"]:
return result

resultDict["CSAPI"] = self.csapi
resultDict["AdminMessages"] = self.adminMsgs
resultDict["VOChanged"] = self.voChanged
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ def syncCSWithVOMS(vomsSync):
if csapi and csapi.csModified:
if dryRun:
gLogger.notice("There are changes to Registry ready to commit, skipped because of dry run")
csapi.showDiff()
else:
yn = input("There are changes to Registry ready to commit, do you want to proceed ? [Y|n]:")
if yn == "" or yn[0].lower() == "y":
Expand Down
77 changes: 54 additions & 23 deletions src/DIRAC/Core/Security/IAMService.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,31 +52,32 @@ def __init__(self, access_token, vo=None):

self.userDict = None
self.access_token = access_token
self.iam_users_raw = []

def _getIamUserDump(self):
"""List the users from IAM"""

headers = {"Authorization": f"Bearer {self.access_token}"}
iam_list_url = f"{self.iam_url}/scim/Users"
iam_users = []
startIndex = 1
# These are just initial values, they are updated
# while we loop to their actual values
totalResults = 1000 # total number of users
itemsPerPage = 10
while startIndex <= totalResults:
resp = requests.get(iam_list_url, headers=headers, params={"startIndex": startIndex})
resp.raise_for_status()
data = resp.json()
# These 2 should never change while looping
# but you may have a new user appearing
# while looping
totalResults = data["totalResults"]
itemsPerPage = data["itemsPerPage"]

startIndex += itemsPerPage
iam_users.extend(data["Resources"])
return iam_users
if not self.iam_users_raw:
headers = {"Authorization": f"Bearer {self.access_token}"}
iam_list_url = f"{self.iam_url}/scim/Users"
startIndex = 1
# These are just initial values, they are updated
# while we loop to their actual values
totalResults = 1000 # total number of users
itemsPerPage = 10
while startIndex <= totalResults:
resp = requests.get(iam_list_url, headers=headers, params={"startIndex": startIndex})
resp.raise_for_status()
data = resp.json()
# These 2 should never change while looping
# but you may have a new user appearing
# while looping
totalResults = data["totalResults"]
itemsPerPage = data["itemsPerPage"]

startIndex += itemsPerPage
self.iam_users_raw.extend(data["Resources"])
return self.iam_users_raw

@staticmethod
def convert_iam_to_voms(iam_output):
Expand Down Expand Up @@ -113,10 +114,10 @@ def convert_iam_to_voms(iam_output):
return converted_output

def getUsers(self):
self.iam_users_raw = self._getIamUserDump()
iam_users_raw = self._getIamUserDump()
users = {}
errors = 0
for user in self.iam_users_raw:
for user in iam_users_raw:
try:
users.update(self.convert_iam_to_voms(user))
except Exception as e:
Expand All @@ -125,3 +126,33 @@ def getUsers(self):
print(f"There were in total {errors} errors")
self.userDict = dict(users)
return S_OK(users)

def getUsersSub(self) -> dict[str, str]:
"""
Return the mapping based on IAM sub:
{sub : nickname}

"""
iam_users_raw = self._getIamUserDump()
diracx_user_section = {}
for user_info in iam_users_raw:
# The nickname is available in the list of attributes
# (if configured so)
# in the form {'name': 'nickname', 'value': 'chaen'}
# otherwise, we take the userName
try:
nickname = [
attr["value"]
for attr in user_info["urn:indigo-dc:scim:schemas:IndigoUser"]["attributes"]
if attr["name"] == "nickname"
][0]
except (KeyError, IndexError):
nickname = user_info["userName"]
sub = user_info["id"]

diracx_user_section[sub] = nickname

# reorder it
diracx_user_section = dict(sorted(diracx_user_section.items()))

return diracx_user_section
Loading