Skip to content

Commit

Permalink
Enable/Disable authentication maps (#530)
Browse files Browse the repository at this point in the history
  • Loading branch information
dmzoneill authored Nov 22, 2024
1 parent 3adf499 commit 7c71771
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,9 @@ class Migration(migrations.Migration):
name='revoke',
field=models.BooleanField(default=False, help_text='Revoke the permission if a user does not meet this rule.'),
),
migrations.AddField(
model_name='authenticatormap',
name='enabled',
field=models.BooleanField(default=True, help_text='Enables or disables this authenticator map'),
),
]
5 changes: 5 additions & 0 deletions ansible_base/authentication/models/authenticator_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,8 @@ class Meta:
)
),
)
enabled = models.BooleanField(
null=False,
default=True,
help_text=_("Enables or disables this authenticator map"),
)
20 changes: 17 additions & 3 deletions ansible_base/authentication/utils/claims.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,29 @@ def create_claims(authenticator: Authenticator, username: str, attrs: dict, grou
logger.debug(f"{username}'s attrs: {attrs}")

# load the maps
logger.debug(f"Authenticator ID: {authenticator.id}")
maps = AuthenticatorMap.objects.order_by("order")
logger.debug(maps)
maps = AuthenticatorMap.objects.filter(authenticator=authenticator.id).order_by("order")
logger.debug("==============================================================")
logger.debug(maps)

for auth_map in maps:
logger.debug(auth_map)
logger.debug("++++")
has_permission = None
trigger_result = TriggerResult.SKIP
allowed_keys = TRIGGER_DEFINITION.keys()
invalid_keys = set(auth_map.triggers.keys()) - set(allowed_keys)

if auth_map.enabled is False:
logger.info(f"Skipping AuthenticatorMap {auth_map.id} because it is disabled")
rule_responses.append({auth_map.id: 'skipped', 'enabled': auth_map.enabled})
continue

if invalid_keys:
logger.warning(f"In AuthenticatorMap {auth_map.id} the following trigger keys are invalid: {', '.join(invalid_keys)}, rule will be ignored")
rule_responses.append({auth_map.id: 'invalid'})
rule_responses.append({auth_map.id: 'invalid', 'enabled': auth_map.enabled})
continue

for trigger_type, trigger in auth_map.triggers.items():
Expand All @@ -84,15 +98,15 @@ def create_claims(authenticator: Authenticator, username: str, attrs: dict, grou

# If the trigger result is still SKIP, this auth map is not applicable to this user => no action needed
if trigger_result is TriggerResult.SKIP:
rule_responses.append({auth_map.id: 'skipped'})
rule_responses.append({auth_map.id: 'skipped', 'enabled': auth_map.enabled})
continue

if trigger_result is TriggerResult.ALLOW:
has_permission = True
elif trigger_result is TriggerResult.DENY:
has_permission = False

rule_responses.append({auth_map.id: has_permission})
rule_responses.append({auth_map.id: has_permission, 'enabled': auth_map.enabled})

if auth_map.map_type == 'allow' and not has_permission:
# If any rule does not allow we don't want to return this to true
Expand Down
51 changes: 35 additions & 16 deletions test_app/tests/authentication/utils/test_claims.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pytest
from django.db import connection

from ansible_base.authentication.models import AuthenticatorUser
from ansible_base.authentication.models import AuthenticatorMap, AuthenticatorUser
from ansible_base.authentication.utils import claims
from test_app.tests.authentication.conftest import SYSTEM_ROLE_NAME

Expand All @@ -20,7 +20,7 @@
True,
True,
{"team_membership": {}, "organization_membership": {}, 'rbac_roles': {'system': {'roles': {}}, 'organizations': {}}},
[{1: True}],
[{1: True, 'enabled': True}],
id="Set flag 'is_superuser' to True (trigger 'always')",
),
pytest.param(
Expand All @@ -32,7 +32,7 @@
True,
False,
{"team_membership": {}, "organization_membership": {}, 'rbac_roles': {'system': {'roles': {}}, 'organizations': {}}},
[{1: False}],
[{1: False, 'enabled': True}],
id="Set flag 'is_superuser' to False (trigger 'never')",
),
pytest.param(
Expand All @@ -44,7 +44,7 @@
True,
None,
{"team_membership": {}, "organization_membership": {}, 'rbac_roles': {'system': {'roles': {}}, 'organizations': {}}},
[{1: "invalid"}],
[{1: "invalid", 'enabled': True}],
id="Wrong trigger, thus flag 'is_superuser' is not set, auth. map is ignored",
),
pytest.param(
Expand All @@ -56,7 +56,7 @@
True,
None,
{"team_membership": {}, "organization_membership": {}, 'rbac_roles': {'system': {'roles': {}}, 'organizations': {}}},
[{1: "skipped"}],
[{1: "skipped", 'enabled': True}],
id="Define no trigger, thus flag 'is_superuser' is not set",
),
pytest.param(
Expand All @@ -68,7 +68,7 @@
False,
None,
{"team_membership": {}, "organization_membership": {}, 'rbac_roles': {'system': {'roles': {}}, 'organizations': {}}},
[{1: False}],
[{1: False, 'enabled': True}],
id="map_type 'allow' with trigger 'never' sets 'access_allowed' to False",
),
pytest.param(
Expand All @@ -84,7 +84,7 @@
"team_membership": {"testorg": {"testteam": True}},
'rbac_roles': {'system': {'roles': {}}, 'organizations': {'testorg': {'roles': {}, 'teams': {'testteam': {'roles': {'Team Member': True}}}}}},
},
[{1: True}],
[{1: True, 'enabled': True}],
id="Assign 'Team Member' role to team 'testteam'",
),
pytest.param(
Expand All @@ -100,7 +100,7 @@
"team_membership": {"testorg": {"testteam": False}},
'rbac_roles': {'system': {'roles': {}}, 'organizations': {'testorg': {'roles': {}, 'teams': {'testteam': {'roles': {'Team Member': False}}}}}},
},
[{1: False}],
[{1: False, 'enabled': True}],
id="Remove 'Team Member' role from team 'testteam'",
),
pytest.param(
Expand All @@ -116,7 +116,7 @@
"team_membership": {},
'rbac_roles': {'system': {'roles': {}}, 'organizations': {'testorg': {'roles': {'Organization Member': True}, 'teams': {}}}},
},
[{1: True}],
[{1: True, 'enabled': True}],
id="Assign 'Organization Member' role to organization 'testorg'",
),
pytest.param(
Expand All @@ -132,7 +132,7 @@
"team_membership": {},
'rbac_roles': {'system': {'roles': {}}, 'organizations': {'testorg': {'roles': {'Organization Member': False}, 'teams': {}}}},
},
[{1: False}],
[{1: False, 'enabled': True}],
id="Remove 'Organization Member' role from organization 'testorg'",
),
pytest.param(
Expand All @@ -148,7 +148,7 @@
"team_membership": {"testorg": {"testteam": True}},
'rbac_roles': {'system': {'roles': {}}, 'organizations': {'testorg': {'roles': {}, 'teams': {'testteam': {'roles': {'Team Member': True}}}}}},
},
[{1: True}],
[{1: True, 'enabled': True}],
id="Assign 'Team Member' role to team 'testteam' using map_type 'role'",
),
pytest.param(
Expand All @@ -164,7 +164,7 @@
"team_membership": {},
'rbac_roles': {'system': {'roles': {}}, 'organizations': {'testorg': {'roles': {'Organization Member': True}, 'teams': {}}}},
},
[{1: True}],
[{1: True, 'enabled': True}],
id="Assign 'Organization Member' role to organization 'testorg' using map_type 'role'",
),
pytest.param(
Expand All @@ -176,7 +176,7 @@
True,
None,
{"organization_membership": {}, "team_membership": {}, 'rbac_roles': {'system': {'roles': {SYSTEM_ROLE_NAME: True}}, 'organizations': {}}},
[{1: True}],
[{1: True, 'enabled': True}],
id="Assign System role to user",
),
pytest.param(
Expand All @@ -188,7 +188,7 @@
True,
None,
{"organization_membership": {}, "team_membership": {}, 'rbac_roles': {'system': {'roles': {}}, 'organizations': {}}},
[{1: False}],
[{1: False, 'enabled': True}],
id="Wrong map type, this auth. map is ignored",
),
],
Expand Down Expand Up @@ -324,9 +324,9 @@ def test_create_claims_revoke(local_authenticator_map, process_function, trigger
assert res["is_superuser"] is granted
assert res["claims"] == {"team_membership": {}, "organization_membership": {}, "rbac_roles": default_rbac_roles_claims}
if revoke:
assert res["last_login_map_results"] == [{local_authenticator_map.pk: False}]
assert res["last_login_map_results"] == [{local_authenticator_map.pk: False, 'enabled': True}]
else:
assert res["last_login_map_results"] == [{local_authenticator_map.pk: "skipped"}]
assert res["last_login_map_results"] == [{local_authenticator_map.pk: "skipped", 'enabled': True}]


@pytest.mark.parametrize(
Expand Down Expand Up @@ -772,3 +772,22 @@ def test_update_user_claims_groups(user, local_authenticator_map):
assert local_authenticator_map.authenticator == authenticator_user.provider # sanity check
result = claims.update_user_claims(user, authenticator, ["foo"])
assert result is user


@pytest.mark.parametrize("enabled", [True, False])
def test_create_claims_with_map_enabled_or_disabled(enabled, local_authenticator):
# Create an AuthenticatorMap object with the parameterized "enabled" value
AuthenticatorMap.objects.create(
authenticator=local_authenticator,
triggers={"always": {}},
map_type="is_superuser",
enabled=enabled,
)

result = claims.create_claims(local_authenticator, "testuser", {}, [])

# Assert based on the "enabled" value
if enabled:
assert result["is_superuser"] is not None, "Claim should be present when enabled is True"
else:
assert result["is_superuser"] is None, "Claim should be None when enabled is False"
1 change: 1 addition & 0 deletions test_app/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ def local_authenticator_map(db, local_authenticator, user, randname):
triggers={"always": {}},
organization="testorg",
team="testteam",
enabled=True,
)
return authenticator_map

Expand Down

0 comments on commit 7c71771

Please sign in to comment.