Skip to content

Commit

Permalink
Implements #1552
Browse files Browse the repository at this point in the history
  • Loading branch information
micafer committed May 3, 2024
1 parent 80fb893 commit 6e650d8
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 15 deletions.
24 changes: 19 additions & 5 deletions IM/InfrastructureManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1425,6 +1425,20 @@ def check_oidc_token(im_auth):
if not issuer.endswith('/'):
issuer += '/'
im_auth['password'] = issuer + str(userinfo.get("sub"))


if Config.OIDC_GROUPS:
# Get user groups from any of the possible fields
user_groups = userinfo.get('groups', # Generic
userinfo.get('entitlements', # GEANT
userinfo.get('eduperson_entitlement', # EGI Check-in
[])))

if not set(Config.OIDC_GROUPS).issubset(user_groups):
raise InvaliddUserException("Invalid InfrastructureManager credentials. " +
"User not in configured groups.")


except Exception as ex:
InfrastructureManager.logger.exception("Error trying to validate OIDC auth token: %s" % str(ex))
raise Exception("Error trying to validate OIDC auth token: %s" % str(ex))
Expand Down Expand Up @@ -1928,10 +1942,10 @@ def EstimateResouces(radl_data, auth):

# If any deploy is defined, only update definitions.
if not radl.deploys:
InfrastructureManager.logger.warn("Getting cost of and infrastructure without any deploy.")
InfrastructureManager.logger.warn("Getting cost of an infrastructure without any deploy.")
return res
except Exception as ex:
InfrastructureManager.logger.exception("Error getting cost of and infrastructure when parsing RADL")
InfrastructureManager.logger.exception("Error getting cost of an infrastructure when parsing RADL")
raise ex

InfrastructureManager.add_app_reqs(radl, inf.id)
Expand All @@ -1940,7 +1954,7 @@ def EstimateResouces(radl_data, auth):
try:
systems_with_iis = InfrastructureManager.systems_with_iis(inf, radl, auth)
except Exception as ex:
InfrastructureManager.logger.exception("Error getting cost of and infrastructure error getting VM images")
InfrastructureManager.logger.exception("Error getting cost of an infrastructure error getting VM images")
raise ex

# Concrete systems with cloud providers and select systems with the greatest score
Expand All @@ -1956,7 +1970,7 @@ def EstimateResouces(radl_data, auth):
deploy_items = []
for deploy_group in deploy_groups:
if not deploy_group:
InfrastructureManager.logger.warning("Error getting cost of and infrastructure: No VMs to deploy!")
InfrastructureManager.logger.warning("Error getting cost of an infrastructure: No VMs to deploy!")

cloud_id = deploys_group_cloud[id(deploy_group)]
cloud = cloud_list[cloud_id]
Expand All @@ -1971,7 +1985,7 @@ def EstimateResouces(radl_data, auth):
concrete_system = concrete_systems[cloud_id][deploy.id][0]

if not concrete_system:
InfrastructureManager.logger.warn("Error getting cost of and infrastructure:" +
InfrastructureManager.logger.warn("Error getting cost of an infrastructure:" +
"Error, no concrete system to deploy: " +
deploy.id + " in cloud: " + cloud_id +
". Check if a correct image is being used")
Expand Down
1 change: 1 addition & 0 deletions IM/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class Config:
OIDC_SCOPES = []
OIDC_USER_INFO_PATH = "/userinfo"
OIDC_INSTROSPECT_PATH = "/introspect"
OIDC_GROUPS = []
VM_NUM_USE_CTXT_DIST = 30
DELAY_BETWEEN_VM_RETRIES = 5
VERIFI_SSL = False
Expand Down
7 changes: 7 additions & 0 deletions doc/source/manual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,13 @@ OPENID CONNECT OPTIONS
Client ID and Secret must be provided to make it work.
The default value is ``''``.

.. confval:: OIDC_GROUPS

List of OIDC groups supported.
It must be a coma separated string of group names.
(see the `AARC guidelines for group names <https://aarc-community.org/guidelines/AARC-G069/>`_).
The default value is ``''``.

.. confval:: FORCE_OIDC_AUTH

If ``True`` the IM will force the users to pass a valid OIDC token.
Expand Down
2 changes: 2 additions & 0 deletions etc/im.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ OIDC_ISSUERS = https://aai.egi.eu/auth/realms/egi
# Paths to the userinfo and introspection OIDC
#OIDC_USER_INFO_PATH = "/userinfo"
#OIDC_INSTROSPECT_PATH = "/introspect"
# List of OIDC groups that will be allowed to access the IM service
#OIDC_GROUPS =
# Force the users to pass a valid OIDC token
#FORCE_OIDC_AUTH = False

Expand Down
13 changes: 4 additions & 9 deletions test/files/iam_user_info.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,10 @@
"email": "",
"email_verified": true,
"phone_number_verified": false,
"groups": [
{
"id": "gid",
"name": "Users"
},
{
"id": "gid",
"name": "Developers"
}
"eduperson_entitlement": [
"urn:mace:egi.eu:group:demo.fedcloud.egi.eu:members:role=member#aai.egi.eu",
"urn:mace:egi.eu:group:demo.fedcloud.egi.eu:role=member#aai.egi.eu",
"urn:mace:egi.eu:group:demo.fedcloud.egi.eu:vm_operator:role=member#aai.egi.eu"
],
"organisation_name": "indigo-dc"
}
30 changes: 29 additions & 1 deletion test/unit/test_im_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def get_cloud_connector_mock(self, name="MyMock0"):
return cloud

@staticmethod
def gen_token(aud=None, exp=None, user_sub="user_sub"):
def gen_token(aud=None, exp=None, user_sub="user_sub", groups=None):
data = {
"sub": user_sub,
"iss": "https://iam-test.indigo-datacloud.eu/",
Expand All @@ -147,6 +147,8 @@ def gen_token(aud=None, exp=None, user_sub="user_sub"):
data["aud"] = aud
if exp:
data["exp"] = int(time.time()) + exp
if groups:
data["groups"] = groups
return ("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.%s.ignored" %
base64.urlsafe_b64encode(json.dumps(data).encode("utf-8")).decode("utf-8"))

Expand Down Expand Up @@ -1126,6 +1128,32 @@ def test_check_oidc_valid_token(self, openidclient):
self.assertEqual(im_auth['username'], InfrastructureInfo.OPENID_USER_PREFIX + "micafer")
self.assertEqual(im_auth['password'], "https://iam-test.indigo-datacloud.eu/sub")

@patch('IM.InfrastructureManager.OpenIDClient')
def test_check_oidc_groups(self, openidclient):
im_auth = {"token": (self.gen_token())}

user_info = json.loads(read_file_as_string('../files/iam_user_info.json'))

openidclient.is_access_token_expired.return_value = False, "Valid Token for 100 seconds"
openidclient.get_user_info_request.return_value = True, user_info

Config.OIDC_ISSUERS = ["https://iam-test.indigo-datacloud.eu/"]
Config.OIDC_AUDIENCE = None
Config.OIDC_GROUPS = ["urn:mace:egi.eu:group:demo.fedcloud.egi.eu:role=member#aai.egi.eu"]

IM.check_oidc_token(im_auth)

self.assertEqual(im_auth['username'], InfrastructureInfo.OPENID_USER_PREFIX + "micafer")
self.assertEqual(im_auth['password'], "https://iam-test.indigo-datacloud.eu/sub")

Config.OIDC_GROUPS = ["urn:mace:egi.eu:group:demo.fedcloud.egi.eu:role=INVALID#aai.egi.eu"]

with self.assertRaises(Exception) as ex:
IM.check_oidc_token(im_auth)
self.assertEqual(str(ex.exception),
"Error trying to validate OIDC auth token: Invalid InfrastructureManager" +
" credentials. User not in configured groups.")

def test_inf_auth_with_token(self):
im_auth = {"token": (self.gen_token())}
im_auth['username'] = InfrastructureInfo.OPENID_USER_PREFIX + "micafer"
Expand Down

0 comments on commit 6e650d8

Please sign in to comment.