Skip to content

Commit

Permalink
Merge pull request DIRACGrid#7711 from chaen/v9.0_feat_iamToDiracX
Browse files Browse the repository at this point in the history
[9.0] Populate diracx user subs from IAM
  • Loading branch information
fstagni authored Jun 28, 2024
2 parents daa062c + e849247 commit 613d7ee
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 25 deletions.
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 @@ -125,10 +126,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 @@ -137,3 +138,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

0 comments on commit 613d7ee

Please sign in to comment.