from django.contrib.auth import get_user_model from django.test import override_settings from django.urls import reverse from rest_framework.test import APITestCase from artifacts.models import ApiKey User = get_user_model() @override_settings(DAYBOOK_KEY_PROVISIONING_API=True) class ApiKeyProvisioningEnabledTestCase(APITestCase): def setUp(self): self.user = User.objects.create_user(username="alice", password="pw") self.bootstrap = ApiKey.objects.create(user=self.user, name="bootstrap") self.other = User.objects.create_user(username="bob", password="pw") def auth(self, key): return {"HTTP_AUTHORIZATION": f"Bearer {key.key}"} def test_create_key_with_token_auth(self): response = self.client.post( reverse("api-keys"), {"name": "hermes-agent"}, format="json", **self.auth(self.bootstrap) ) self.assertEqual(response.status_code, 201) self.assertEqual(response.data["name"], "hermes-agent") self.assertTrue(response.data["key"].startswith("dbk_")) self.assertTrue(ApiKey.objects.filter(user=self.user, name="hermes-agent").exists()) def test_created_key_can_authenticate(self): created = self.client.post( reverse("api-keys"), {"name": "worker"}, format="json", **self.auth(self.bootstrap) ).data # The brand-new key works against another token-authed endpoint. response = self.client.get( reverse("capabilities-api"), HTTP_AUTHORIZATION=f"Bearer {created['key']}" ) self.assertEqual(response.status_code, 200) def test_create_requires_name(self): response = self.client.post( reverse("api-keys"), {"name": " "}, format="json", **self.auth(self.bootstrap) ) self.assertEqual(response.status_code, 400) def test_list_returns_only_own_keys(self): ApiKey.objects.create(user=self.other, name="not-yours") response = self.client.get(reverse("api-keys"), **self.auth(self.bootstrap)) self.assertEqual(response.status_code, 200) names = {k["name"] for k in response.data} self.assertEqual(names, {"bootstrap"}) def test_revoke_key(self): victim = ApiKey.objects.create(user=self.user, name="doomed") response = self.client.delete( reverse("api-key-detail", args=[victim.pk]), **self.auth(self.bootstrap) ) self.assertEqual(response.status_code, 204) self.assertFalse(ApiKey.objects.filter(pk=victim.pk).exists()) def test_revoke_one_does_not_affect_others(self): keep = ApiKey.objects.create(user=self.user, name="keep") drop = ApiKey.objects.create(user=self.user, name="drop") self.client.delete(reverse("api-key-detail", args=[drop.pk]), **self.auth(self.bootstrap)) self.assertTrue(ApiKey.objects.filter(pk=keep.pk).exists()) def test_cannot_revoke_another_users_key(self): theirs = ApiKey.objects.create(user=self.other, name="theirs") response = self.client.delete( reverse("api-key-detail", args=[theirs.pk]), **self.auth(self.bootstrap) ) self.assertEqual(response.status_code, 404) self.assertTrue(ApiKey.objects.filter(pk=theirs.pk).exists()) def test_capabilities_advertises_provisioning(self): response = self.client.get(reverse("capabilities-api"), **self.auth(self.bootstrap)) self.assertTrue(response.data["key_provisioning_api"]) self.assertEqual(response.data["methods"]["/api/keys"], ["GET", "POST"]) @override_settings(DAYBOOK_KEY_PROVISIONING_API=False) class ApiKeyProvisioningDisabledTestCase(APITestCase): def setUp(self): self.user = User.objects.create_user(username="alice", password="pw") self.key = ApiKey.objects.create(user=self.user, name="bootstrap") def auth(self): return {"HTTP_AUTHORIZATION": f"Bearer {self.key.key}"} def test_create_disabled_returns_403(self): response = self.client.post( reverse("api-keys"), {"name": "nope"}, format="json", **self.auth() ) self.assertEqual(response.status_code, 403) self.assertFalse(ApiKey.objects.filter(name="nope").exists()) def test_list_disabled_returns_403(self): response = self.client.get(reverse("api-keys"), **self.auth()) self.assertEqual(response.status_code, 403) def test_revoke_disabled_returns_403(self): response = self.client.delete(reverse("api-key-detail", args=[self.key.pk]), **self.auth()) self.assertEqual(response.status_code, 403) self.assertTrue(ApiKey.objects.filter(pk=self.key.pk).exists()) def test_capabilities_hides_provisioning(self): response = self.client.get(reverse("capabilities-api"), **self.auth()) self.assertFalse(response.data["key_provisioning_api"]) self.assertNotIn("/api/keys", response.data["methods"])