authentik.enterprise.lifecycle.tests.test_api
1from django.apps import apps 2from django.contrib.contenttypes.models import ContentType 3from django.urls import reverse 4from rest_framework.test import APITestCase 5 6from authentik.core.models import Application, Group 7from authentik.core.tests.utils import create_test_admin_user, create_test_user 8from authentik.enterprise.lifecycle.models import LifecycleIteration, LifecycleRule, ReviewState 9from authentik.enterprise.reports.tests.utils import patch_license 10from authentik.lib.generators import generate_id 11 12 13@patch_license 14class TestLifecycleRuleAPI(APITestCase): 15 16 def setUp(self): 17 self.user = create_test_admin_user() 18 self.client.force_login(self.user) 19 self.app = Application.objects.create(name=generate_id(), slug=generate_id()) 20 self.content_type = ContentType.objects.get_for_model(Application) 21 self.reviewer_group = Group.objects.create(name=generate_id()) 22 23 @classmethod 24 def setUpTestData(cls): 25 config = apps.get_app_config("authentik_tasks_schedules") 26 config._on_startup_callback(None) 27 28 def test_list_rules(self): 29 rule = LifecycleRule.objects.create( 30 name=generate_id(), 31 content_type=self.content_type, 32 object_id=str(self.app.pk), 33 ) 34 rule.reviewer_groups.add(self.reviewer_group) 35 36 response = self.client.get(reverse("authentik_api:lifecyclerule-list")) 37 self.assertEqual(response.status_code, 200) 38 self.assertGreaterEqual(len(response.data["results"]), 1) 39 40 def test_create_rule_with_reviewer_group(self): 41 response = self.client.post( 42 reverse("authentik_api:lifecyclerule-list"), 43 { 44 "name": generate_id(), 45 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 46 "object_id": str(self.app.pk), 47 "interval": "days=30", 48 "grace_period": "days=10", 49 "reviewer_groups": [str(self.reviewer_group.pk)], 50 "reviewers": [], 51 "min_reviewers": 1, 52 }, 53 ) 54 self.assertEqual(response.status_code, 201) 55 self.assertEqual(response.data["object_id"], str(self.app.pk)) 56 self.assertEqual(response.data["interval"], "days=30") 57 58 def test_create_rule_with_explicit_reviewer(self): 59 reviewer = create_test_user() 60 response = self.client.post( 61 reverse("authentik_api:lifecyclerule-list"), 62 { 63 "name": generate_id(), 64 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 65 "object_id": str(self.app.pk), 66 "interval": "days=60", 67 "grace_period": "days=15", 68 "reviewer_groups": [], 69 "reviewers": [str(reviewer.uuid)], 70 "min_reviewers": 1, 71 }, 72 ) 73 self.assertEqual(response.status_code, 201) 74 self.assertIn(reviewer.uuid, response.data["reviewers"]) 75 76 def test_create_rule_type_level(self): 77 response = self.client.post( 78 reverse("authentik_api:lifecyclerule-list"), 79 { 80 "name": generate_id(), 81 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 82 "object_id": None, 83 "interval": "days=90", 84 "grace_period": "days=30", 85 "reviewer_groups": [str(self.reviewer_group.pk)], 86 "reviewers": [], 87 "min_reviewers": 1, 88 }, 89 ) 90 self.assertEqual(response.status_code, 201) 91 self.assertIsNone(response.data["object_id"]) 92 93 def test_create_rule_fails_without_reviewers(self): 94 response = self.client.post( 95 reverse("authentik_api:lifecyclerule-list"), 96 { 97 "name": generate_id(), 98 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 99 "object_id": str(self.app.pk), 100 "interval": "days=30", 101 "grace_period": "days=10", 102 "reviewer_groups": [], 103 "reviewers": [], 104 "min_reviewers": 1, 105 }, 106 ) 107 self.assertEqual(response.status_code, 400) 108 109 def test_create_rule_fails_grace_period_longer_than_interval(self): 110 response = self.client.post( 111 reverse("authentik_api:lifecyclerule-list"), 112 { 113 "name": generate_id(), 114 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 115 "object_id": str(self.app.pk), 116 "interval": "days=10", 117 "grace_period": "days=30", 118 "reviewer_groups": [str(self.reviewer_group.pk)], 119 "reviewers": [], 120 "min_reviewers": 1, 121 }, 122 ) 123 self.assertEqual(response.status_code, 400) 124 self.assertIn("grace_period", response.data) 125 126 def test_create_rule_fails_invalid_object_id(self): 127 response = self.client.post( 128 reverse("authentik_api:lifecyclerule-list"), 129 { 130 "name": generate_id(), 131 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 132 "object_id": "00000000-0000-0000-0000-000000000000", 133 "interval": "days=30", 134 "grace_period": "days=10", 135 "reviewer_groups": [str(self.reviewer_group.pk)], 136 "reviewers": [], 137 "min_reviewers": 1, 138 }, 139 ) 140 self.assertEqual(response.status_code, 400) 141 self.assertIn("object_id", response.data) 142 143 def test_retrieve_rule(self): 144 rule = LifecycleRule.objects.create( 145 name=generate_id(), 146 content_type=self.content_type, 147 object_id=str(self.app.pk), 148 ) 149 rule.reviewer_groups.add(self.reviewer_group) 150 151 response = self.client.get( 152 reverse("authentik_api:lifecyclerule-detail", kwargs={"pk": rule.pk}) 153 ) 154 self.assertEqual(response.status_code, 200) 155 self.assertEqual(response.data["id"], str(rule.pk)) 156 157 def test_update_rule(self): 158 rule = LifecycleRule.objects.create( 159 name=generate_id(), 160 content_type=self.content_type, 161 object_id=str(self.app.pk), 162 interval="days=30", 163 ) 164 rule.reviewer_groups.add(self.reviewer_group) 165 166 response = self.client.patch( 167 reverse("authentik_api:lifecyclerule-detail", kwargs={"pk": rule.pk}), 168 {"interval": "days=60"}, 169 ) 170 self.assertEqual(response.status_code, 200) 171 self.assertEqual(response.data["interval"], "days=60") 172 173 def test_delete_rule(self): 174 rule = LifecycleRule.objects.create( 175 name=generate_id(), 176 content_type=self.content_type, 177 object_id=str(self.app.pk), 178 ) 179 rule.reviewer_groups.add(self.reviewer_group) 180 181 response = self.client.delete( 182 reverse("authentik_api:lifecyclerule-detail", kwargs={"pk": rule.pk}) 183 ) 184 self.assertEqual(response.status_code, 204) 185 self.assertFalse(LifecycleRule.objects.filter(pk=rule.pk).exists()) 186 187 188@patch_license 189class TestIterationAPI(APITestCase): 190 191 def setUp(self): 192 self.user = create_test_admin_user() 193 self.client.force_login(self.user) 194 self.app = Application.objects.create(name=generate_id(), slug=generate_id()) 195 self.content_type = ContentType.objects.get_for_model(Application) 196 self.reviewer_group = Group.objects.create(name=generate_id()) 197 self.reviewer_group.users.add(self.user) 198 199 @classmethod 200 def setUpTestData(cls): 201 config = apps.get_app_config("authentik_tasks_schedules") 202 config._on_startup_callback(None) 203 204 def test_open_iterations(self): 205 rule = LifecycleRule.objects.create( 206 name=generate_id(), 207 content_type=self.content_type, 208 object_id=str(self.app.pk), 209 ) 210 rule.reviewer_groups.add(self.reviewer_group) 211 212 response = self.client.get(reverse("authentik_api:lifecycleiteration-open-iterations")) 213 self.assertEqual(response.status_code, 200) 214 self.assertGreaterEqual(len(response.data["results"]), 1) 215 216 for iteration in response.data["results"]: 217 self.assertEqual(iteration["state"], ReviewState.PENDING) 218 219 def test_open_iterations_filter_user_is_reviewer(self): 220 rule = LifecycleRule.objects.create( 221 name=generate_id(), 222 content_type=self.content_type, 223 object_id=str(self.app.pk), 224 ) 225 rule.reviewer_groups.add(self.reviewer_group) 226 227 response = self.client.get( 228 reverse("authentik_api:lifecycleiteration-open-iterations"), 229 {"user_is_reviewer": "true"}, 230 ) 231 self.assertEqual(response.status_code, 200) 232 # User is in reviewer_group, so should see the iteration 233 self.assertGreaterEqual(len(response.data["results"]), 1) 234 235 def test_latest_iteration(self): 236 rule = LifecycleRule.objects.create( 237 name=generate_id(), 238 content_type=self.content_type, 239 object_id=str(self.app.pk), 240 ) 241 rule.reviewer_groups.add(self.reviewer_group) 242 243 response = self.client.get( 244 reverse( 245 "authentik_api:lifecycleiteration-latest-iterations", 246 kwargs={ 247 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 248 "object_id": str(self.app.pk), 249 }, 250 ) 251 ) 252 self.assertEqual(response.status_code, 200) 253 self.assertEqual(len(response.data), 1) 254 self.assertEqual(response.data[0]["object_id"], str(self.app.pk)) 255 256 def test_latest_iteration_not_found(self): 257 response = self.client.get( 258 reverse( 259 "authentik_api:lifecycleiteration-latest-iterations", 260 kwargs={ 261 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 262 "object_id": "00000000-0000-0000-0000-000000000000", 263 }, 264 ) 265 ) 266 self.assertEqual(response.data, []) 267 268 def test_iteration_includes_user_can_review(self): 269 rule = LifecycleRule.objects.create( 270 name=generate_id(), 271 content_type=self.content_type, 272 object_id=str(self.app.pk), 273 ) 274 rule.reviewer_groups.add(self.reviewer_group) 275 276 response = self.client.get(reverse("authentik_api:lifecycleiteration-open-iterations")) 277 self.assertEqual(response.status_code, 200) 278 self.assertGreaterEqual(len(response.data["results"]), 1) 279 # user_can_review should be present 280 self.assertIn("user_can_review", response.data["results"][0]) 281 282 283@patch_license 284class TestReviewAPI(APITestCase): 285 286 def setUp(self): 287 self.user = create_test_admin_user() 288 self.client.force_login(self.user) 289 self.app = Application.objects.create(name=generate_id(), slug=generate_id()) 290 self.content_type = ContentType.objects.get_for_model(Application) 291 self.reviewer_group = Group.objects.create(name=generate_id()) 292 self.reviewer_group.users.add(self.user) 293 294 @classmethod 295 def setUpTestData(cls): 296 config = apps.get_app_config("authentik_tasks_schedules") 297 config._on_startup_callback(None) 298 299 def test_create_review(self): 300 rule = LifecycleRule.objects.create( 301 name=generate_id(), 302 content_type=self.content_type, 303 object_id=str(self.app.pk), 304 min_reviewers=1, 305 ) 306 rule.reviewer_groups.add(self.reviewer_group) 307 308 # Get the auto-created iteration 309 iteration = LifecycleIteration.objects.get( 310 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 311 ) 312 313 response = self.client.post( 314 reverse("authentik_api:review-list"), 315 { 316 "iteration": str(iteration.pk), 317 "note": "Reviewed and approved", 318 }, 319 ) 320 self.assertEqual(response.status_code, 201) 321 self.assertEqual(response.data["iteration"], iteration.pk) 322 self.assertEqual(response.data["note"], "Reviewed and approved") 323 self.assertEqual(response.data["reviewer"]["pk"], self.user.pk) 324 325 def test_create_review_completes_iteration(self): 326 rule = LifecycleRule.objects.create( 327 name=generate_id(), 328 content_type=self.content_type, 329 object_id=str(self.app.pk), 330 min_reviewers=1, 331 ) 332 rule.reviewer_groups.add(self.reviewer_group) 333 334 iteration = LifecycleIteration.objects.get( 335 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 336 ) 337 self.assertEqual(iteration.state, ReviewState.PENDING) 338 339 response = self.client.post( 340 reverse("authentik_api:review-list"), 341 { 342 "iteration": str(iteration.pk), 343 }, 344 ) 345 self.assertEqual(response.status_code, 201) 346 347 iteration.refresh_from_db() 348 self.assertEqual(iteration.state, ReviewState.REVIEWED) 349 350 def test_create_review_sets_reviewer_from_request(self): 351 rule = LifecycleRule.objects.create( 352 name=generate_id(), 353 content_type=self.content_type, 354 object_id=str(self.app.pk), 355 min_reviewers=1, 356 ) 357 rule.reviewer_groups.add(self.reviewer_group) 358 359 iteration = LifecycleIteration.objects.get( 360 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 361 ) 362 363 response = self.client.post( 364 reverse("authentik_api:review-list"), 365 { 366 "iteration": str(iteration.pk), 367 }, 368 ) 369 self.assertEqual(response.status_code, 201) 370 # Reviewer should be the logged-in user 371 self.assertEqual(response.data["reviewer"]["pk"], self.user.pk) 372 373 def test_non_reviewer_cannot_review(self): 374 other_group = Group.objects.create(name=generate_id()) 375 other_user = create_test_user() 376 other_group.users.add(other_user) 377 378 rule = LifecycleRule.objects.create( 379 name=generate_id(), 380 content_type=self.content_type, 381 object_id=str(self.app.pk), 382 min_reviewers=1, 383 ) 384 rule.reviewer_groups.add(other_group) 385 386 iteration = LifecycleIteration.objects.get( 387 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 388 ) 389 390 # Current user is not in the reviewer group 391 self.assertFalse(iteration.user_can_review(self.user)) 392 393 def test_non_reviewer_review_via_api_rejected(self): 394 other_group = Group.objects.create(name=generate_id()) 395 other_user = create_test_user() 396 other_group.users.add(other_user) 397 398 rule = LifecycleRule.objects.create( 399 name=generate_id(), 400 content_type=self.content_type, 401 object_id=str(self.app.pk), 402 min_reviewers=1, 403 ) 404 rule.reviewer_groups.add(other_group) 405 406 iteration = LifecycleIteration.objects.get( 407 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 408 ) 409 410 # Current user (self.user) is NOT in the reviewer group 411 response = self.client.post( 412 reverse("authentik_api:review-list"), 413 {"iteration": str(iteration.pk)}, 414 ) 415 self.assertEqual(response.status_code, 400) 416 417 def test_duplicate_review_via_api_rejected(self): 418 rule = LifecycleRule.objects.create( 419 name=generate_id(), 420 content_type=self.content_type, 421 object_id=str(self.app.pk), 422 min_reviewers=2, 423 ) 424 rule.reviewer_groups.add(self.reviewer_group) 425 426 iteration = LifecycleIteration.objects.get( 427 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 428 ) 429 430 # First review should succeed 431 response = self.client.post( 432 reverse("authentik_api:review-list"), 433 {"iteration": str(iteration.pk)}, 434 ) 435 self.assertEqual(response.status_code, 201) 436 437 # Second review by same user should be rejected 438 response = self.client.post( 439 reverse("authentik_api:review-list"), 440 {"iteration": str(iteration.pk)}, 441 ) 442 self.assertEqual(response.status_code, 400)
14@patch_license 15class TestLifecycleRuleAPI(APITestCase): 16 17 def setUp(self): 18 self.user = create_test_admin_user() 19 self.client.force_login(self.user) 20 self.app = Application.objects.create(name=generate_id(), slug=generate_id()) 21 self.content_type = ContentType.objects.get_for_model(Application) 22 self.reviewer_group = Group.objects.create(name=generate_id()) 23 24 @classmethod 25 def setUpTestData(cls): 26 config = apps.get_app_config("authentik_tasks_schedules") 27 config._on_startup_callback(None) 28 29 def test_list_rules(self): 30 rule = LifecycleRule.objects.create( 31 name=generate_id(), 32 content_type=self.content_type, 33 object_id=str(self.app.pk), 34 ) 35 rule.reviewer_groups.add(self.reviewer_group) 36 37 response = self.client.get(reverse("authentik_api:lifecyclerule-list")) 38 self.assertEqual(response.status_code, 200) 39 self.assertGreaterEqual(len(response.data["results"]), 1) 40 41 def test_create_rule_with_reviewer_group(self): 42 response = self.client.post( 43 reverse("authentik_api:lifecyclerule-list"), 44 { 45 "name": generate_id(), 46 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 47 "object_id": str(self.app.pk), 48 "interval": "days=30", 49 "grace_period": "days=10", 50 "reviewer_groups": [str(self.reviewer_group.pk)], 51 "reviewers": [], 52 "min_reviewers": 1, 53 }, 54 ) 55 self.assertEqual(response.status_code, 201) 56 self.assertEqual(response.data["object_id"], str(self.app.pk)) 57 self.assertEqual(response.data["interval"], "days=30") 58 59 def test_create_rule_with_explicit_reviewer(self): 60 reviewer = create_test_user() 61 response = self.client.post( 62 reverse("authentik_api:lifecyclerule-list"), 63 { 64 "name": generate_id(), 65 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 66 "object_id": str(self.app.pk), 67 "interval": "days=60", 68 "grace_period": "days=15", 69 "reviewer_groups": [], 70 "reviewers": [str(reviewer.uuid)], 71 "min_reviewers": 1, 72 }, 73 ) 74 self.assertEqual(response.status_code, 201) 75 self.assertIn(reviewer.uuid, response.data["reviewers"]) 76 77 def test_create_rule_type_level(self): 78 response = self.client.post( 79 reverse("authentik_api:lifecyclerule-list"), 80 { 81 "name": generate_id(), 82 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 83 "object_id": None, 84 "interval": "days=90", 85 "grace_period": "days=30", 86 "reviewer_groups": [str(self.reviewer_group.pk)], 87 "reviewers": [], 88 "min_reviewers": 1, 89 }, 90 ) 91 self.assertEqual(response.status_code, 201) 92 self.assertIsNone(response.data["object_id"]) 93 94 def test_create_rule_fails_without_reviewers(self): 95 response = self.client.post( 96 reverse("authentik_api:lifecyclerule-list"), 97 { 98 "name": generate_id(), 99 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 100 "object_id": str(self.app.pk), 101 "interval": "days=30", 102 "grace_period": "days=10", 103 "reviewer_groups": [], 104 "reviewers": [], 105 "min_reviewers": 1, 106 }, 107 ) 108 self.assertEqual(response.status_code, 400) 109 110 def test_create_rule_fails_grace_period_longer_than_interval(self): 111 response = self.client.post( 112 reverse("authentik_api:lifecyclerule-list"), 113 { 114 "name": generate_id(), 115 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 116 "object_id": str(self.app.pk), 117 "interval": "days=10", 118 "grace_period": "days=30", 119 "reviewer_groups": [str(self.reviewer_group.pk)], 120 "reviewers": [], 121 "min_reviewers": 1, 122 }, 123 ) 124 self.assertEqual(response.status_code, 400) 125 self.assertIn("grace_period", response.data) 126 127 def test_create_rule_fails_invalid_object_id(self): 128 response = self.client.post( 129 reverse("authentik_api:lifecyclerule-list"), 130 { 131 "name": generate_id(), 132 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 133 "object_id": "00000000-0000-0000-0000-000000000000", 134 "interval": "days=30", 135 "grace_period": "days=10", 136 "reviewer_groups": [str(self.reviewer_group.pk)], 137 "reviewers": [], 138 "min_reviewers": 1, 139 }, 140 ) 141 self.assertEqual(response.status_code, 400) 142 self.assertIn("object_id", response.data) 143 144 def test_retrieve_rule(self): 145 rule = LifecycleRule.objects.create( 146 name=generate_id(), 147 content_type=self.content_type, 148 object_id=str(self.app.pk), 149 ) 150 rule.reviewer_groups.add(self.reviewer_group) 151 152 response = self.client.get( 153 reverse("authentik_api:lifecyclerule-detail", kwargs={"pk": rule.pk}) 154 ) 155 self.assertEqual(response.status_code, 200) 156 self.assertEqual(response.data["id"], str(rule.pk)) 157 158 def test_update_rule(self): 159 rule = LifecycleRule.objects.create( 160 name=generate_id(), 161 content_type=self.content_type, 162 object_id=str(self.app.pk), 163 interval="days=30", 164 ) 165 rule.reviewer_groups.add(self.reviewer_group) 166 167 response = self.client.patch( 168 reverse("authentik_api:lifecyclerule-detail", kwargs={"pk": rule.pk}), 169 {"interval": "days=60"}, 170 ) 171 self.assertEqual(response.status_code, 200) 172 self.assertEqual(response.data["interval"], "days=60") 173 174 def test_delete_rule(self): 175 rule = LifecycleRule.objects.create( 176 name=generate_id(), 177 content_type=self.content_type, 178 object_id=str(self.app.pk), 179 ) 180 rule.reviewer_groups.add(self.reviewer_group) 181 182 response = self.client.delete( 183 reverse("authentik_api:lifecyclerule-detail", kwargs={"pk": rule.pk}) 184 ) 185 self.assertEqual(response.status_code, 204) 186 self.assertFalse(LifecycleRule.objects.filter(pk=rule.pk).exists())
Similar to TransactionTestCase, but use transaction.atomic() to achieve
test isolation.
In most situations, TestCase should be preferred to TransactionTestCase as it allows faster execution. However, there are some situations where using TransactionTestCase might be necessary (e.g. testing some transactional behavior).
On database backends with no transaction support, TestCase behaves as TransactionTestCase.
17 def setUp(self): 18 self.user = create_test_admin_user() 19 self.client.force_login(self.user) 20 self.app = Application.objects.create(name=generate_id(), slug=generate_id()) 21 self.content_type = ContentType.objects.get_for_model(Application) 22 self.reviewer_group = Group.objects.create(name=generate_id())
Hook method for setting up the test fixture before exercising it.
24 @classmethod 25 def setUpTestData(cls): 26 config = apps.get_app_config("authentik_tasks_schedules") 27 config._on_startup_callback(None)
Load initial data for the TestCase.
29 def test_list_rules(self): 30 rule = LifecycleRule.objects.create( 31 name=generate_id(), 32 content_type=self.content_type, 33 object_id=str(self.app.pk), 34 ) 35 rule.reviewer_groups.add(self.reviewer_group) 36 37 response = self.client.get(reverse("authentik_api:lifecyclerule-list")) 38 self.assertEqual(response.status_code, 200) 39 self.assertGreaterEqual(len(response.data["results"]), 1)
41 def test_create_rule_with_reviewer_group(self): 42 response = self.client.post( 43 reverse("authentik_api:lifecyclerule-list"), 44 { 45 "name": generate_id(), 46 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 47 "object_id": str(self.app.pk), 48 "interval": "days=30", 49 "grace_period": "days=10", 50 "reviewer_groups": [str(self.reviewer_group.pk)], 51 "reviewers": [], 52 "min_reviewers": 1, 53 }, 54 ) 55 self.assertEqual(response.status_code, 201) 56 self.assertEqual(response.data["object_id"], str(self.app.pk)) 57 self.assertEqual(response.data["interval"], "days=30")
59 def test_create_rule_with_explicit_reviewer(self): 60 reviewer = create_test_user() 61 response = self.client.post( 62 reverse("authentik_api:lifecyclerule-list"), 63 { 64 "name": generate_id(), 65 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 66 "object_id": str(self.app.pk), 67 "interval": "days=60", 68 "grace_period": "days=15", 69 "reviewer_groups": [], 70 "reviewers": [str(reviewer.uuid)], 71 "min_reviewers": 1, 72 }, 73 ) 74 self.assertEqual(response.status_code, 201) 75 self.assertIn(reviewer.uuid, response.data["reviewers"])
77 def test_create_rule_type_level(self): 78 response = self.client.post( 79 reverse("authentik_api:lifecyclerule-list"), 80 { 81 "name": generate_id(), 82 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 83 "object_id": None, 84 "interval": "days=90", 85 "grace_period": "days=30", 86 "reviewer_groups": [str(self.reviewer_group.pk)], 87 "reviewers": [], 88 "min_reviewers": 1, 89 }, 90 ) 91 self.assertEqual(response.status_code, 201) 92 self.assertIsNone(response.data["object_id"])
94 def test_create_rule_fails_without_reviewers(self): 95 response = self.client.post( 96 reverse("authentik_api:lifecyclerule-list"), 97 { 98 "name": generate_id(), 99 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 100 "object_id": str(self.app.pk), 101 "interval": "days=30", 102 "grace_period": "days=10", 103 "reviewer_groups": [], 104 "reviewers": [], 105 "min_reviewers": 1, 106 }, 107 ) 108 self.assertEqual(response.status_code, 400)
110 def test_create_rule_fails_grace_period_longer_than_interval(self): 111 response = self.client.post( 112 reverse("authentik_api:lifecyclerule-list"), 113 { 114 "name": generate_id(), 115 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 116 "object_id": str(self.app.pk), 117 "interval": "days=10", 118 "grace_period": "days=30", 119 "reviewer_groups": [str(self.reviewer_group.pk)], 120 "reviewers": [], 121 "min_reviewers": 1, 122 }, 123 ) 124 self.assertEqual(response.status_code, 400) 125 self.assertIn("grace_period", response.data)
127 def test_create_rule_fails_invalid_object_id(self): 128 response = self.client.post( 129 reverse("authentik_api:lifecyclerule-list"), 130 { 131 "name": generate_id(), 132 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 133 "object_id": "00000000-0000-0000-0000-000000000000", 134 "interval": "days=30", 135 "grace_period": "days=10", 136 "reviewer_groups": [str(self.reviewer_group.pk)], 137 "reviewers": [], 138 "min_reviewers": 1, 139 }, 140 ) 141 self.assertEqual(response.status_code, 400) 142 self.assertIn("object_id", response.data)
144 def test_retrieve_rule(self): 145 rule = LifecycleRule.objects.create( 146 name=generate_id(), 147 content_type=self.content_type, 148 object_id=str(self.app.pk), 149 ) 150 rule.reviewer_groups.add(self.reviewer_group) 151 152 response = self.client.get( 153 reverse("authentik_api:lifecyclerule-detail", kwargs={"pk": rule.pk}) 154 ) 155 self.assertEqual(response.status_code, 200) 156 self.assertEqual(response.data["id"], str(rule.pk))
158 def test_update_rule(self): 159 rule = LifecycleRule.objects.create( 160 name=generate_id(), 161 content_type=self.content_type, 162 object_id=str(self.app.pk), 163 interval="days=30", 164 ) 165 rule.reviewer_groups.add(self.reviewer_group) 166 167 response = self.client.patch( 168 reverse("authentik_api:lifecyclerule-detail", kwargs={"pk": rule.pk}), 169 {"interval": "days=60"}, 170 ) 171 self.assertEqual(response.status_code, 200) 172 self.assertEqual(response.data["interval"], "days=60")
174 def test_delete_rule(self): 175 rule = LifecycleRule.objects.create( 176 name=generate_id(), 177 content_type=self.content_type, 178 object_id=str(self.app.pk), 179 ) 180 rule.reviewer_groups.add(self.reviewer_group) 181 182 response = self.client.delete( 183 reverse("authentik_api:lifecyclerule-detail", kwargs={"pk": rule.pk}) 184 ) 185 self.assertEqual(response.status_code, 204) 186 self.assertFalse(LifecycleRule.objects.filter(pk=rule.pk).exists())
189@patch_license 190class TestIterationAPI(APITestCase): 191 192 def setUp(self): 193 self.user = create_test_admin_user() 194 self.client.force_login(self.user) 195 self.app = Application.objects.create(name=generate_id(), slug=generate_id()) 196 self.content_type = ContentType.objects.get_for_model(Application) 197 self.reviewer_group = Group.objects.create(name=generate_id()) 198 self.reviewer_group.users.add(self.user) 199 200 @classmethod 201 def setUpTestData(cls): 202 config = apps.get_app_config("authentik_tasks_schedules") 203 config._on_startup_callback(None) 204 205 def test_open_iterations(self): 206 rule = LifecycleRule.objects.create( 207 name=generate_id(), 208 content_type=self.content_type, 209 object_id=str(self.app.pk), 210 ) 211 rule.reviewer_groups.add(self.reviewer_group) 212 213 response = self.client.get(reverse("authentik_api:lifecycleiteration-open-iterations")) 214 self.assertEqual(response.status_code, 200) 215 self.assertGreaterEqual(len(response.data["results"]), 1) 216 217 for iteration in response.data["results"]: 218 self.assertEqual(iteration["state"], ReviewState.PENDING) 219 220 def test_open_iterations_filter_user_is_reviewer(self): 221 rule = LifecycleRule.objects.create( 222 name=generate_id(), 223 content_type=self.content_type, 224 object_id=str(self.app.pk), 225 ) 226 rule.reviewer_groups.add(self.reviewer_group) 227 228 response = self.client.get( 229 reverse("authentik_api:lifecycleiteration-open-iterations"), 230 {"user_is_reviewer": "true"}, 231 ) 232 self.assertEqual(response.status_code, 200) 233 # User is in reviewer_group, so should see the iteration 234 self.assertGreaterEqual(len(response.data["results"]), 1) 235 236 def test_latest_iteration(self): 237 rule = LifecycleRule.objects.create( 238 name=generate_id(), 239 content_type=self.content_type, 240 object_id=str(self.app.pk), 241 ) 242 rule.reviewer_groups.add(self.reviewer_group) 243 244 response = self.client.get( 245 reverse( 246 "authentik_api:lifecycleiteration-latest-iterations", 247 kwargs={ 248 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 249 "object_id": str(self.app.pk), 250 }, 251 ) 252 ) 253 self.assertEqual(response.status_code, 200) 254 self.assertEqual(len(response.data), 1) 255 self.assertEqual(response.data[0]["object_id"], str(self.app.pk)) 256 257 def test_latest_iteration_not_found(self): 258 response = self.client.get( 259 reverse( 260 "authentik_api:lifecycleiteration-latest-iterations", 261 kwargs={ 262 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 263 "object_id": "00000000-0000-0000-0000-000000000000", 264 }, 265 ) 266 ) 267 self.assertEqual(response.data, []) 268 269 def test_iteration_includes_user_can_review(self): 270 rule = LifecycleRule.objects.create( 271 name=generate_id(), 272 content_type=self.content_type, 273 object_id=str(self.app.pk), 274 ) 275 rule.reviewer_groups.add(self.reviewer_group) 276 277 response = self.client.get(reverse("authentik_api:lifecycleiteration-open-iterations")) 278 self.assertEqual(response.status_code, 200) 279 self.assertGreaterEqual(len(response.data["results"]), 1) 280 # user_can_review should be present 281 self.assertIn("user_can_review", response.data["results"][0])
Similar to TransactionTestCase, but use transaction.atomic() to achieve
test isolation.
In most situations, TestCase should be preferred to TransactionTestCase as it allows faster execution. However, there are some situations where using TransactionTestCase might be necessary (e.g. testing some transactional behavior).
On database backends with no transaction support, TestCase behaves as TransactionTestCase.
192 def setUp(self): 193 self.user = create_test_admin_user() 194 self.client.force_login(self.user) 195 self.app = Application.objects.create(name=generate_id(), slug=generate_id()) 196 self.content_type = ContentType.objects.get_for_model(Application) 197 self.reviewer_group = Group.objects.create(name=generate_id()) 198 self.reviewer_group.users.add(self.user)
Hook method for setting up the test fixture before exercising it.
200 @classmethod 201 def setUpTestData(cls): 202 config = apps.get_app_config("authentik_tasks_schedules") 203 config._on_startup_callback(None)
Load initial data for the TestCase.
205 def test_open_iterations(self): 206 rule = LifecycleRule.objects.create( 207 name=generate_id(), 208 content_type=self.content_type, 209 object_id=str(self.app.pk), 210 ) 211 rule.reviewer_groups.add(self.reviewer_group) 212 213 response = self.client.get(reverse("authentik_api:lifecycleiteration-open-iterations")) 214 self.assertEqual(response.status_code, 200) 215 self.assertGreaterEqual(len(response.data["results"]), 1) 216 217 for iteration in response.data["results"]: 218 self.assertEqual(iteration["state"], ReviewState.PENDING)
220 def test_open_iterations_filter_user_is_reviewer(self): 221 rule = LifecycleRule.objects.create( 222 name=generate_id(), 223 content_type=self.content_type, 224 object_id=str(self.app.pk), 225 ) 226 rule.reviewer_groups.add(self.reviewer_group) 227 228 response = self.client.get( 229 reverse("authentik_api:lifecycleiteration-open-iterations"), 230 {"user_is_reviewer": "true"}, 231 ) 232 self.assertEqual(response.status_code, 200) 233 # User is in reviewer_group, so should see the iteration 234 self.assertGreaterEqual(len(response.data["results"]), 1)
236 def test_latest_iteration(self): 237 rule = LifecycleRule.objects.create( 238 name=generate_id(), 239 content_type=self.content_type, 240 object_id=str(self.app.pk), 241 ) 242 rule.reviewer_groups.add(self.reviewer_group) 243 244 response = self.client.get( 245 reverse( 246 "authentik_api:lifecycleiteration-latest-iterations", 247 kwargs={ 248 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 249 "object_id": str(self.app.pk), 250 }, 251 ) 252 ) 253 self.assertEqual(response.status_code, 200) 254 self.assertEqual(len(response.data), 1) 255 self.assertEqual(response.data[0]["object_id"], str(self.app.pk))
257 def test_latest_iteration_not_found(self): 258 response = self.client.get( 259 reverse( 260 "authentik_api:lifecycleiteration-latest-iterations", 261 kwargs={ 262 "content_type": f"{self.content_type.app_label}.{self.content_type.model}", 263 "object_id": "00000000-0000-0000-0000-000000000000", 264 }, 265 ) 266 ) 267 self.assertEqual(response.data, [])
269 def test_iteration_includes_user_can_review(self): 270 rule = LifecycleRule.objects.create( 271 name=generate_id(), 272 content_type=self.content_type, 273 object_id=str(self.app.pk), 274 ) 275 rule.reviewer_groups.add(self.reviewer_group) 276 277 response = self.client.get(reverse("authentik_api:lifecycleiteration-open-iterations")) 278 self.assertEqual(response.status_code, 200) 279 self.assertGreaterEqual(len(response.data["results"]), 1) 280 # user_can_review should be present 281 self.assertIn("user_can_review", response.data["results"][0])
284@patch_license 285class TestReviewAPI(APITestCase): 286 287 def setUp(self): 288 self.user = create_test_admin_user() 289 self.client.force_login(self.user) 290 self.app = Application.objects.create(name=generate_id(), slug=generate_id()) 291 self.content_type = ContentType.objects.get_for_model(Application) 292 self.reviewer_group = Group.objects.create(name=generate_id()) 293 self.reviewer_group.users.add(self.user) 294 295 @classmethod 296 def setUpTestData(cls): 297 config = apps.get_app_config("authentik_tasks_schedules") 298 config._on_startup_callback(None) 299 300 def test_create_review(self): 301 rule = LifecycleRule.objects.create( 302 name=generate_id(), 303 content_type=self.content_type, 304 object_id=str(self.app.pk), 305 min_reviewers=1, 306 ) 307 rule.reviewer_groups.add(self.reviewer_group) 308 309 # Get the auto-created iteration 310 iteration = LifecycleIteration.objects.get( 311 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 312 ) 313 314 response = self.client.post( 315 reverse("authentik_api:review-list"), 316 { 317 "iteration": str(iteration.pk), 318 "note": "Reviewed and approved", 319 }, 320 ) 321 self.assertEqual(response.status_code, 201) 322 self.assertEqual(response.data["iteration"], iteration.pk) 323 self.assertEqual(response.data["note"], "Reviewed and approved") 324 self.assertEqual(response.data["reviewer"]["pk"], self.user.pk) 325 326 def test_create_review_completes_iteration(self): 327 rule = LifecycleRule.objects.create( 328 name=generate_id(), 329 content_type=self.content_type, 330 object_id=str(self.app.pk), 331 min_reviewers=1, 332 ) 333 rule.reviewer_groups.add(self.reviewer_group) 334 335 iteration = LifecycleIteration.objects.get( 336 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 337 ) 338 self.assertEqual(iteration.state, ReviewState.PENDING) 339 340 response = self.client.post( 341 reverse("authentik_api:review-list"), 342 { 343 "iteration": str(iteration.pk), 344 }, 345 ) 346 self.assertEqual(response.status_code, 201) 347 348 iteration.refresh_from_db() 349 self.assertEqual(iteration.state, ReviewState.REVIEWED) 350 351 def test_create_review_sets_reviewer_from_request(self): 352 rule = LifecycleRule.objects.create( 353 name=generate_id(), 354 content_type=self.content_type, 355 object_id=str(self.app.pk), 356 min_reviewers=1, 357 ) 358 rule.reviewer_groups.add(self.reviewer_group) 359 360 iteration = LifecycleIteration.objects.get( 361 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 362 ) 363 364 response = self.client.post( 365 reverse("authentik_api:review-list"), 366 { 367 "iteration": str(iteration.pk), 368 }, 369 ) 370 self.assertEqual(response.status_code, 201) 371 # Reviewer should be the logged-in user 372 self.assertEqual(response.data["reviewer"]["pk"], self.user.pk) 373 374 def test_non_reviewer_cannot_review(self): 375 other_group = Group.objects.create(name=generate_id()) 376 other_user = create_test_user() 377 other_group.users.add(other_user) 378 379 rule = LifecycleRule.objects.create( 380 name=generate_id(), 381 content_type=self.content_type, 382 object_id=str(self.app.pk), 383 min_reviewers=1, 384 ) 385 rule.reviewer_groups.add(other_group) 386 387 iteration = LifecycleIteration.objects.get( 388 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 389 ) 390 391 # Current user is not in the reviewer group 392 self.assertFalse(iteration.user_can_review(self.user)) 393 394 def test_non_reviewer_review_via_api_rejected(self): 395 other_group = Group.objects.create(name=generate_id()) 396 other_user = create_test_user() 397 other_group.users.add(other_user) 398 399 rule = LifecycleRule.objects.create( 400 name=generate_id(), 401 content_type=self.content_type, 402 object_id=str(self.app.pk), 403 min_reviewers=1, 404 ) 405 rule.reviewer_groups.add(other_group) 406 407 iteration = LifecycleIteration.objects.get( 408 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 409 ) 410 411 # Current user (self.user) is NOT in the reviewer group 412 response = self.client.post( 413 reverse("authentik_api:review-list"), 414 {"iteration": str(iteration.pk)}, 415 ) 416 self.assertEqual(response.status_code, 400) 417 418 def test_duplicate_review_via_api_rejected(self): 419 rule = LifecycleRule.objects.create( 420 name=generate_id(), 421 content_type=self.content_type, 422 object_id=str(self.app.pk), 423 min_reviewers=2, 424 ) 425 rule.reviewer_groups.add(self.reviewer_group) 426 427 iteration = LifecycleIteration.objects.get( 428 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 429 ) 430 431 # First review should succeed 432 response = self.client.post( 433 reverse("authentik_api:review-list"), 434 {"iteration": str(iteration.pk)}, 435 ) 436 self.assertEqual(response.status_code, 201) 437 438 # Second review by same user should be rejected 439 response = self.client.post( 440 reverse("authentik_api:review-list"), 441 {"iteration": str(iteration.pk)}, 442 ) 443 self.assertEqual(response.status_code, 400)
Similar to TransactionTestCase, but use transaction.atomic() to achieve
test isolation.
In most situations, TestCase should be preferred to TransactionTestCase as it allows faster execution. However, there are some situations where using TransactionTestCase might be necessary (e.g. testing some transactional behavior).
On database backends with no transaction support, TestCase behaves as TransactionTestCase.
287 def setUp(self): 288 self.user = create_test_admin_user() 289 self.client.force_login(self.user) 290 self.app = Application.objects.create(name=generate_id(), slug=generate_id()) 291 self.content_type = ContentType.objects.get_for_model(Application) 292 self.reviewer_group = Group.objects.create(name=generate_id()) 293 self.reviewer_group.users.add(self.user)
Hook method for setting up the test fixture before exercising it.
295 @classmethod 296 def setUpTestData(cls): 297 config = apps.get_app_config("authentik_tasks_schedules") 298 config._on_startup_callback(None)
Load initial data for the TestCase.
300 def test_create_review(self): 301 rule = LifecycleRule.objects.create( 302 name=generate_id(), 303 content_type=self.content_type, 304 object_id=str(self.app.pk), 305 min_reviewers=1, 306 ) 307 rule.reviewer_groups.add(self.reviewer_group) 308 309 # Get the auto-created iteration 310 iteration = LifecycleIteration.objects.get( 311 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 312 ) 313 314 response = self.client.post( 315 reverse("authentik_api:review-list"), 316 { 317 "iteration": str(iteration.pk), 318 "note": "Reviewed and approved", 319 }, 320 ) 321 self.assertEqual(response.status_code, 201) 322 self.assertEqual(response.data["iteration"], iteration.pk) 323 self.assertEqual(response.data["note"], "Reviewed and approved") 324 self.assertEqual(response.data["reviewer"]["pk"], self.user.pk)
326 def test_create_review_completes_iteration(self): 327 rule = LifecycleRule.objects.create( 328 name=generate_id(), 329 content_type=self.content_type, 330 object_id=str(self.app.pk), 331 min_reviewers=1, 332 ) 333 rule.reviewer_groups.add(self.reviewer_group) 334 335 iteration = LifecycleIteration.objects.get( 336 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 337 ) 338 self.assertEqual(iteration.state, ReviewState.PENDING) 339 340 response = self.client.post( 341 reverse("authentik_api:review-list"), 342 { 343 "iteration": str(iteration.pk), 344 }, 345 ) 346 self.assertEqual(response.status_code, 201) 347 348 iteration.refresh_from_db() 349 self.assertEqual(iteration.state, ReviewState.REVIEWED)
351 def test_create_review_sets_reviewer_from_request(self): 352 rule = LifecycleRule.objects.create( 353 name=generate_id(), 354 content_type=self.content_type, 355 object_id=str(self.app.pk), 356 min_reviewers=1, 357 ) 358 rule.reviewer_groups.add(self.reviewer_group) 359 360 iteration = LifecycleIteration.objects.get( 361 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 362 ) 363 364 response = self.client.post( 365 reverse("authentik_api:review-list"), 366 { 367 "iteration": str(iteration.pk), 368 }, 369 ) 370 self.assertEqual(response.status_code, 201) 371 # Reviewer should be the logged-in user 372 self.assertEqual(response.data["reviewer"]["pk"], self.user.pk)
374 def test_non_reviewer_cannot_review(self): 375 other_group = Group.objects.create(name=generate_id()) 376 other_user = create_test_user() 377 other_group.users.add(other_user) 378 379 rule = LifecycleRule.objects.create( 380 name=generate_id(), 381 content_type=self.content_type, 382 object_id=str(self.app.pk), 383 min_reviewers=1, 384 ) 385 rule.reviewer_groups.add(other_group) 386 387 iteration = LifecycleIteration.objects.get( 388 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 389 ) 390 391 # Current user is not in the reviewer group 392 self.assertFalse(iteration.user_can_review(self.user))
394 def test_non_reviewer_review_via_api_rejected(self): 395 other_group = Group.objects.create(name=generate_id()) 396 other_user = create_test_user() 397 other_group.users.add(other_user) 398 399 rule = LifecycleRule.objects.create( 400 name=generate_id(), 401 content_type=self.content_type, 402 object_id=str(self.app.pk), 403 min_reviewers=1, 404 ) 405 rule.reviewer_groups.add(other_group) 406 407 iteration = LifecycleIteration.objects.get( 408 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 409 ) 410 411 # Current user (self.user) is NOT in the reviewer group 412 response = self.client.post( 413 reverse("authentik_api:review-list"), 414 {"iteration": str(iteration.pk)}, 415 ) 416 self.assertEqual(response.status_code, 400)
418 def test_duplicate_review_via_api_rejected(self): 419 rule = LifecycleRule.objects.create( 420 name=generate_id(), 421 content_type=self.content_type, 422 object_id=str(self.app.pk), 423 min_reviewers=2, 424 ) 425 rule.reviewer_groups.add(self.reviewer_group) 426 427 iteration = LifecycleIteration.objects.get( 428 content_type=self.content_type, object_id=str(self.app.pk), rule=rule 429 ) 430 431 # First review should succeed 432 response = self.client.post( 433 reverse("authentik_api:review-list"), 434 {"iteration": str(iteration.pk)}, 435 ) 436 self.assertEqual(response.status_code, 201) 437 438 # Second review by same user should be rejected 439 response = self.client.post( 440 reverse("authentik_api:review-list"), 441 {"iteration": str(iteration.pk)}, 442 ) 443 self.assertEqual(response.status_code, 400)