diff --git a/authentik/policies/event_matcher/migrations/0019_alter_eventmatcherpolicy_app.py b/authentik/policies/event_matcher/migrations/0019_alter_eventmatcherpolicy_app.py index bc46851b2..8dbf28590 100644 --- a/authentik/policies/event_matcher/migrations/0019_alter_eventmatcherpolicy_app.py +++ b/authentik/policies/event_matcher/migrations/0019_alter_eventmatcherpolicy_app.py @@ -39,6 +39,7 @@ class Migration(migrations.Migration): ("authentik.sources.oauth", "authentik Sources.OAuth"), ("authentik.sources.plex", "authentik Sources.Plex"), ("authentik.sources.saml", "authentik Sources.SAML"), + ("authentik.sources.scim", "authentik Sources.SCIM"), ("authentik.stages.authenticator_duo", "authentik Stages.Authenticator.Duo"), ("authentik.stages.authenticator_sms", "authentik Stages.Authenticator.SMS"), ( diff --git a/authentik/sources/scim/tests/__init__.py b/authentik/sources/scim/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/authentik/sources/scim/tests/test_auth.py b/authentik/sources/scim/tests/test_auth.py new file mode 100644 index 000000000..64830feb0 --- /dev/null +++ b/authentik/sources/scim/tests/test_auth.py @@ -0,0 +1,86 @@ +"""Test SCIM Auth""" +from django.urls import reverse +from rest_framework.test import APITestCase + +from authentik.core.models import Token, TokenIntents +from authentik.core.tests.utils import create_test_admin_user +from authentik.lib.generators import generate_id +from authentik.sources.scim.models import SCIMSource + + +class TestSCIMAuth(APITestCase): + """Test SCIM Auth view""" + + def setUp(self) -> None: + self.user = create_test_admin_user() + self.token = Token.objects.create( + user=self.user, + identifier=generate_id(), + intent=TokenIntents.INTENT_API, + ) + self.token2 = Token.objects.create( + user=self.user, + identifier=generate_id(), + intent=TokenIntents.INTENT_API, + ) + self.token3 = Token.objects.create( + user=self.user, + identifier=generate_id(), + intent=TokenIntents.INTENT_API, + ) + self.source = SCIMSource.objects.create( + name=generate_id(), slug=generate_id(), token=self.token + ) + self.source2 = SCIMSource.objects.create( + name=generate_id(), slug=generate_id(), token=self.token2 + ) + + def test_auth_ok(self): + """Test successful auth""" + response = self.client.get( + reverse( + "authentik_sources_scim:v2-schema", + kwargs={ + "source_slug": self.source.slug, + }, + ), + HTTP_AUTHORIZATION=f"Bearer {self.token.key}", + ) + self.assertEqual(response.status_code, 200) + + def test_auth_missing(self): + """Test without header""" + response = self.client.get( + reverse( + "authentik_sources_scim:v2-schema", + kwargs={ + "source_slug": self.source.slug, + }, + ), + ) + self.assertEqual(response.status_code, 403) + + def test_auth_wrong_token(self): + """Test with wrong token""" + # Token for wrong source + response = self.client.get( + reverse( + "authentik_sources_scim:v2-schema", + kwargs={ + "source_slug": self.source.slug, + }, + ), + HTTP_AUTHORIZATION=f"Bearer {self.token2.key}", + ) + self.assertEqual(response.status_code, 403) + # Token for no source + response = self.client.get( + reverse( + "authentik_sources_scim:v2-schema", + kwargs={ + "source_slug": self.source.slug, + }, + ), + HTTP_AUTHORIZATION=f"Bearer {self.token3.key}", + ) + self.assertEqual(response.status_code, 403) diff --git a/authentik/sources/scim/tests/test_resource_types.py b/authentik/sources/scim/tests/test_resource_types.py new file mode 100644 index 000000000..ac6805105 --- /dev/null +++ b/authentik/sources/scim/tests/test_resource_types.py @@ -0,0 +1,64 @@ +"""Test SCIM ResourceTypes""" +from django.urls import reverse +from rest_framework.test import APITestCase + +from authentik.core.models import Token, TokenIntents +from authentik.core.tests.utils import create_test_admin_user +from authentik.lib.generators import generate_id +from authentik.sources.scim.models import SCIMSource + + +class TestSCIMResourceTypes(APITestCase): + """Test SCIM ResourceTypes view""" + + def setUp(self) -> None: + self.user = create_test_admin_user() + self.token = Token.objects.create( + user=self.user, + identifier=generate_id(), + intent=TokenIntents.INTENT_API, + ) + self.source = SCIMSource.objects.create( + name=generate_id(), slug=generate_id(), token=self.token + ) + + def test_resource_type(self): + """Test full resource type view""" + response = self.client.get( + reverse( + "authentik_sources_scim:v2-resource-types", + kwargs={ + "source_slug": self.source.slug, + }, + ), + HTTP_AUTHORIZATION=f"Bearer {self.token.key}", + ) + self.assertEqual(response.status_code, 200) + + def test_resource_type_single(self): + """Test single resource type""" + response = self.client.get( + reverse( + "authentik_sources_scim:v2-resource-types", + kwargs={ + "source_slug": self.source.slug, + "resource_type": "ServiceProviderConfig", + }, + ), + HTTP_AUTHORIZATION=f"Bearer {self.token.key}", + ) + self.assertEqual(response.status_code, 200) + + def test_resource_type_single_404(self): + """Test single resource type (404""" + response = self.client.get( + reverse( + "authentik_sources_scim:v2-resource-types", + kwargs={ + "source_slug": self.source.slug, + "resource_type": "foo", + }, + ), + HTTP_AUTHORIZATION=f"Bearer {self.token.key}", + ) + self.assertEqual(response.status_code, 404) diff --git a/authentik/sources/scim/tests/test_schemas.py b/authentik/sources/scim/tests/test_schemas.py new file mode 100644 index 000000000..4abea043c --- /dev/null +++ b/authentik/sources/scim/tests/test_schemas.py @@ -0,0 +1,64 @@ +"""Test SCIM Schema""" +from django.urls import reverse +from rest_framework.test import APITestCase + +from authentik.core.models import Token, TokenIntents +from authentik.core.tests.utils import create_test_admin_user +from authentik.lib.generators import generate_id +from authentik.sources.scim.models import SCIMSource + + +class TestSCIMSchemas(APITestCase): + """Test SCIM Schema view""" + + def setUp(self) -> None: + self.user = create_test_admin_user() + self.token = Token.objects.create( + user=self.user, + identifier=generate_id(), + intent=TokenIntents.INTENT_API, + ) + self.source = SCIMSource.objects.create( + name=generate_id(), slug=generate_id(), token=self.token + ) + + def test_schema(self): + """Test full schema view""" + response = self.client.get( + reverse( + "authentik_sources_scim:v2-schema", + kwargs={ + "source_slug": self.source.slug, + }, + ), + HTTP_AUTHORIZATION=f"Bearer {self.token.key}", + ) + self.assertEqual(response.status_code, 200) + + def test_schema_single(self): + """Test single schema""" + response = self.client.get( + reverse( + "authentik_sources_scim:v2-schema", + kwargs={ + "source_slug": self.source.slug, + "schema_uri": "urn:ietf:params:scim:schemas:core:2.0:Meta", + }, + ), + HTTP_AUTHORIZATION=f"Bearer {self.token.key}", + ) + self.assertEqual(response.status_code, 200) + + def test_schema_single_404(self): + """Test single schema (404""" + response = self.client.get( + reverse( + "authentik_sources_scim:v2-schema", + kwargs={ + "source_slug": self.source.slug, + "schema_uri": "foo", + }, + ), + HTTP_AUTHORIZATION=f"Bearer {self.token.key}", + ) + self.assertEqual(response.status_code, 404) diff --git a/authentik/sources/scim/tests/test_service_provider_config.py b/authentik/sources/scim/tests/test_service_provider_config.py new file mode 100644 index 000000000..3a96e4322 --- /dev/null +++ b/authentik/sources/scim/tests/test_service_provider_config.py @@ -0,0 +1,36 @@ +"""Test SCIM ServiceProviderConfig""" +from django.urls import reverse +from rest_framework.test import APITestCase + +from authentik.core.models import Token, TokenIntents +from authentik.core.tests.utils import create_test_admin_user +from authentik.lib.generators import generate_id +from authentik.sources.scim.models import SCIMSource + + +class TestSCIMServiceProviderConfig(APITestCase): + """Test SCIM ServiceProviderConfig view""" + + def setUp(self) -> None: + self.user = create_test_admin_user() + self.token = Token.objects.create( + user=self.user, + identifier=generate_id(), + intent=TokenIntents.INTENT_API, + ) + self.source = SCIMSource.objects.create( + name=generate_id(), slug=generate_id(), token=self.token + ) + + def test_config(self): + """Test full config view""" + response = self.client.get( + reverse( + "authentik_sources_scim:v2-service-provider-config", + kwargs={ + "source_slug": self.source.slug, + }, + ), + HTTP_AUTHORIZATION=f"Bearer {self.token.key}", + ) + self.assertEqual(response.status_code, 200) diff --git a/authentik/sources/scim/views/v2/auth.py b/authentik/sources/scim/views/v2/auth.py index 72462c9b9..60ef9fb05 100644 --- a/authentik/sources/scim/views/v2/auth.py +++ b/authentik/sources/scim/views/v2/auth.py @@ -38,4 +38,6 @@ class SCIMTokenAuth(BaseAuthentication): if auth_type != "Bearer": return self.legacy(key, source_slug) token = self.check_token(key, source_slug) + if not token: + return None return (token.user, token)