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

Fix MFA policy management with unknown users #1125

Merged
merged 7 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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: 11 additions & 5 deletions fence/sync/sync_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -1868,13 +1868,17 @@ def _update_arborist(self, session, user_yaml):

return True

def _revoke_all_policies_preserve_mfa(self, user):
def _revoke_all_policies_preserve_mfa(self, username, idp=None):
"""
If MFA is enabled for the user's idp, check if they have the /multifactor_auth resource and restore the
mfa_policy after revoking all policies.
"""
username = user.username
idp = user.identity_provider.name if user.identity_provider else None
user_data_from_arborist = None
try:
user_data_from_arborist = self.arborist_client.get_user(username)
except ArboristError:
# user doesn't exist in Arborist, nothing to revoke
return

is_mfa_enabled = "multifactor_auth_claim_info" in config["OPENID_CONNECT"].get(
idp, {}
Expand All @@ -1886,7 +1890,7 @@ def _revoke_all_policies_preserve_mfa(self, user):

policies = []
try:
policies = self.arborist_client.get_user()["policies"]
Copy link
Contributor

Choose a reason for hiding this comment

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

not sure how this ever worked... get_user is missing the required username parameter

policies = user_data_from_arborist["policies"]
except Exception as e:
self.logger.error(
f"Could not retrieve user's policies, revoking all policies anyway. {e}"
Expand Down Expand Up @@ -1998,12 +2002,14 @@ def _update_authz_in_arborist(
for username, user_project_info in user_projects.items():
self.logger.info("processing user `{}`".format(username))
user = query_for_user(session=session, username=username)
idp = None
if user:
username = user.username
idp = user.identity_provider.name if user.identity_provider else None

self.arborist_client.create_user_if_not_exist(username)
if not single_user_sync:
self._revoke_all_policies_preserve_mfa(user)
self._revoke_all_policies_preserve_mfa(username, idp)

# as of 2/11/2022, for single_user_sync, as RAS visa parsing has
# previously mapped each project to the same set of privileges
Expand Down
22 changes: 18 additions & 4 deletions tests/dbgap_sync/test_user_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,20 @@ def test_user_sync_with_visa_sync_job(
)


@pytest.mark.parametrize("syncer", ["cleversafe", "google"], indirect=True)
def test_revoke_all_policies_no_user(db_session, syncer):
"""
Test that function returns even when there's no user
"""
# no arborist user with that username
user_that_doesnt_exist = "foobar"
syncer.arborist_client.get_user.return_value = None

syncer._revoke_all_policies_preserve_mfa(user_that_doesnt_exist, "mock_idp")

# we only care that this doesn't error
assert True
Comment on lines +1045 to +1048
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
syncer._revoke_all_policies_preserve_mfa(user_that_doesnt_exist, "mock_idp")
# we only care that this doesn't error
assert True
# we only care that this doesn't error
syncer._revoke_all_policies_preserve_mfa(user_that_doesnt_exist, "mock_idp")


@pytest.mark.parametrize("syncer", ["cleversafe", "google"], indirect=True)
def test_revoke_all_policies_preserve_mfa(monkeypatch, db_session, syncer):
"""
Expand All @@ -1051,7 +1065,7 @@ def test_revoke_all_policies_preserve_mfa(monkeypatch, db_session, syncer):
username="mockuser", identity_provider=IdentityProvider(name="mock_idp")
)
syncer.arborist_client.get_user.return_value = {"policies": ["mfa_policy"]}
syncer._revoke_all_policies_preserve_mfa(user)
syncer._revoke_all_policies_preserve_mfa(user.username, user.identity_provider.name)
syncer.arborist_client.revoke_all_policies_for_user.assert_called_with(
user.username
)
Expand Down Expand Up @@ -1080,7 +1094,7 @@ def test_revoke_all_policies_preserve_mfa_no_mfa(monkeypatch, db_session, syncer
syncer.arborist_client.list_resources_for_user.return_value = [
"/programs/phs0001111"
]
syncer._revoke_all_policies_preserve_mfa(user)
syncer._revoke_all_policies_preserve_mfa(user.username, user.identity_provider.name)
syncer.arborist_client.revoke_all_policies_for_user.assert_called_with(
user.username
)
Expand All @@ -1102,7 +1116,7 @@ def test_revoke_all_policies_preserve_mfa_no_idp(monkeypatch, db_session, syncer
},
)
user = User(username="mockuser")
syncer._revoke_all_policies_preserve_mfa(user)
syncer._revoke_all_policies_preserve_mfa(user.username)
syncer.arborist_client.revoke_all_policies_for_user.assert_called_with(
user.username
)
Expand Down Expand Up @@ -1132,7 +1146,7 @@ def test_revoke_all_policies_preserve_mfa_ensure_revoke_on_error(
syncer.arborist_client.list_resources_for_user.side_effect = Exception(
"Unknown error"
)
syncer._revoke_all_policies_preserve_mfa(user)
syncer._revoke_all_policies_preserve_mfa(user.username, user.identity_provider.name)
syncer.arborist_client.revoke_all_policies_for_user.assert_called_with(
user.username
)