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

Automated copy feature/OIDC oauth groups #1199

Open
wants to merge 44 commits into
base: master
Choose a base branch
from

Conversation

Avantol13
Copy link
Contributor

@Avantol13 Avantol13 commented Nov 14, 2024

duplicated from here: #1188

Link to JIRA ticket if there is one:

New Features

  • Support for OIDC Group Claims
  • Implementation of token refresh

Breaking Changes

Bug Fixes

Improvements

Dependency updates

Deployment changes

@coveralls
Copy link

coveralls commented Nov 14, 2024

Pull Request Test Coverage Report for Build 12416371639

Details

  • 0 of 0 changed or added relevant lines in 0 files are covered.
  • 121 unchanged lines in 7 files lost coverage.
  • Overall coverage decreased (-0.1%) to 75.106%

Files with Coverage Reduction New Missed Lines %
resources/openid/microsoft_oauth2.py 1 95.0%
error_handler.py 1 97.92%
init.py 3 90.84%
config.py 4 72.58%
scripting/fence_create.py 5 60.35%
blueprints/login/base.py 29 78.72%
resources/openid/idp_oauth2.py 78 60.29%
Totals Coverage Status
Change from base Build 12356407492: -0.1%
Covered Lines: 7965
Relevant Lines: 10605

💛 - Coveralls

Copy link

filepath $$\textcolor{#23d18b}{\tt{passed}}$$ $$\textcolor{#f14c4c}{\tt{failed}}$$ $$\textcolor{#ffa500}{\tt{skipped}}$$ SUBTOTAL
$$\textcolor{#23d18b}{\tt{tests/test\_oauth2.py}}$$ $$\textcolor{#23d18b}{\tt{15}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{15}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_centralized\_auth.py}}$$ $$\textcolor{#23d18b}{\tt{16}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{16}}$$
$$\textcolor{#f14c4c}{\tt{tests/test\_data\_upload.py}}$$ $$\textcolor{#23d18b}{\tt{7}}$$ $$\textcolor{#f14c4c}{\tt{1}}$$ $$\textcolor{#ffa500}{\tt{1}}$$ $$\textcolor{#f14c4c}{\tt{9}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_presigned\_url.py}}$$ $$\textcolor{#23d18b}{\tt{7}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{7}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_user\_token.py}}$$ $$\textcolor{#23d18b}{\tt{5}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{5}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_drs\_endpoint.py}}$$ $$\textcolor{#23d18b}{\tt{4}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{4}}$$
$$\textcolor{#f14c4c}{\tt{tests/test\_ras\_authn.py}}$$ $$\textcolor{#23d18b}{\tt{2}}$$ $$\textcolor{#f14c4c}{\tt{1}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#f14c4c}{\tt{3}}$$
$$\textcolor{#ffa500}{\tt{tests/test\_dbgap.py}}$$ $$\textcolor{#23d18b}{\tt{4}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#ffa500}{\tt{1}}$$ $$\textcolor{#ffa500}{\tt{5}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_oidc\_client.py}}$$ $$\textcolor{#23d18b}{\tt{2}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{2}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_client\_credentials.py}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_google\_data\_access.py}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_audit\_service.py}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#ffa500}{\tt{tests/test\_register\_user.py}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#ffa500}{\tt{2}}$$ $$\textcolor{#ffa500}{\tt{2}}$$
$$\textcolor{#f14c4c}{\tt{TOTAL}}$$ $$\textcolor{#23d18b}{\tt{65}}$$ $$\textcolor{#f14c4c}{\tt{2}}$$ $$\textcolor{#ffa500}{\tt{4}}$$ $$\textcolor{#f14c4c}{\tt{71}}$$

Please find the detailed integration test report here

Please find the ci env pod logs here

@flashguerdon
Copy link

@Avantol13 -- Will you be able to give me access to the detailed integration test report? I get 401 when I click on the link.

Copy link

filepath $$\textcolor{#23d18b}{\tt{passed}}$$ $$\textcolor{#ffa500}{\tt{skipped}}$$ SUBTOTAL
$$\textcolor{#23d18b}{\tt{tests/test\_oauth2.py}}$$ $$\textcolor{#23d18b}{\tt{15}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{15}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_centralized\_auth.py}}$$ $$\textcolor{#23d18b}{\tt{16}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{16}}$$
$$\textcolor{#ffa500}{\tt{tests/test\_data\_upload.py}}$$ $$\textcolor{#23d18b}{\tt{8}}$$ $$\textcolor{#ffa500}{\tt{1}}$$ $$\textcolor{#ffa500}{\tt{9}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_user\_token.py}}$$ $$\textcolor{#23d18b}{\tt{5}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{5}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_presigned\_url.py}}$$ $$\textcolor{#23d18b}{\tt{7}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{7}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_drs\_endpoint.py}}$$ $$\textcolor{#23d18b}{\tt{4}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{4}}$$
$$\textcolor{#ffa500}{\tt{tests/test\_dbgap.py}}$$ $$\textcolor{#23d18b}{\tt{4}}$$ $$\textcolor{#ffa500}{\tt{1}}$$ $$\textcolor{#ffa500}{\tt{5}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_ras\_authn.py}}$$ $$\textcolor{#23d18b}{\tt{3}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{3}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_oidc\_client.py}}$$ $$\textcolor{#23d18b}{\tt{2}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{2}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_client\_credentials.py}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_google\_data\_access.py}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#23d18b}{\tt{tests/test\_audit\_service.py}}$$ $$\textcolor{#23d18b}{\tt{1}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#23d18b}{\tt{1}}$$
$$\textcolor{#ffa500}{\tt{tests/test\_register\_user.py}}$$ $$\textcolor{#666666}{\tt{0}}$$ $$\textcolor{#ffa500}{\tt{2}}$$ $$\textcolor{#ffa500}{\tt{2}}$$
$$\textcolor{#ffa500}{\tt{TOTAL}}$$ $$\textcolor{#23d18b}{\tt{67}}$$ $$\textcolor{#ffa500}{\tt{4}}$$ $$\textcolor{#ffa500}{\tt{71}}$$

Please find the detailed integration test report here

Please find the ci env pod logs here

@@ -27,6 +28,11 @@ def get_error_response(error):
)
)

# TODO: Issue: Error messages are obfuscated, the line below needs be
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we get this fixed before we merge?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

self.app = app

# This block of code probably need to be made more concise
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do this cleanup before we merge?

@@ -68,7 +96,8 @@ async def update_tokens(self, db_session):

"""
start_time = time.time()
self.logger.info("Initializing Visa Update Cronjob . . .")
# Change this line to reflect we are refreshing tokens, not just visas
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make this change?

# Initialize visa clients:
oidc = config.get("OPENID_CONNECT", {})

if not isinstance(oidc, dict):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we try not to do type-checking explicitly, the Pythonic way is to treat things as they are expected to be and then error when it behaves unexpectedly.

Do you need this here? Can we move this to be a post-configuration check rather than runtime?

@@ -72,18 +94,101 @@ def get_jwt_keys(self, jwks_uri):
return None
return resp.json()["keys"]

def get_raw_token_claims(self, token_id):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is unused, we should remove it

@@ -187,14 +315,15 @@ def get_access_token(self, user, token_endpoint, db_session=None):
"""
Get access_token using a refresh_token and store new refresh in upstream_refresh_token table.
"""
# this function is not correct. use self.session.fetch_access_token,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you expand on this or fix the underlying issue? I'm not sure I'm following

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function is not retrieving the expires_at from token_response. I'm thinking we should be storing the new expires retrieved from token_response.

@@ -274,3 +403,150 @@ def store_refresh_token(self, user, refresh_token, expires, db_session=None):
current_db_session = db_session.object_session(upstream_refresh_token)
current_db_session.add(upstream_refresh_token)
db_session.commit()

def get_groups_from_token(self, decoded_id_token, group_prefix=""):
"""Retrieve and format groups from the decoded token."""
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need vars in the docstring


def get_groups_from_token(self, decoded_id_token, group_prefix=""):
"""Retrieve and format groups from the decoded token."""
authz_groups_from_idp = decoded_id_token.get("groups", [])
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See previous comment re: this being a configuration per IdP

self.arborist.add_user_to_group(
username=user.username,
group_name=arborist_group["name"],
expires_at=exp,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expiration here is the access_token expiration, was that intentional? Usually those are very short lived, like on the order of minutes, not hours. I wonder if we want to make the expiration configurable per IdP (so I could set it to be 8 hours for example) up to the maximum allowed in the refresh_token (which could be like 30 days?)?

Copy link

@flashguerdon flashguerdon Dec 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, setting per IdP will be better

expires_at=exp,
)

# Remove user from groups in Arborist that they are not part of in IDP
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this design will not work generally. IdP's are not the only source of user membership, so you can't remove users from groups that don't match simply by querying for all groups and diffing with what's in a token, b/c there are other, external sources of other group memberships (admin user.yamls, RAS Passports) that need to be able to maintain their own policies. This logic would wipe users from those groups.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either we rely on policy expiration to cleanup things, or you need a smarter comparison by knowing the list of groups managed by and IdP

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants