@@ -469,3 +469,274 @@ def test_sort_by_date_joined(self):
469
469
body = loads (response .content )
470
470
self .assertEqual (len (body ["results" ]), 2 )
471
471
self .assertEqual (body ["results" ][0 ]["pk" ], user .pk )
472
+
473
+ def test_service_account_validation_empty_username (self ):
474
+ """Test service account creation with empty/blank username validation"""
475
+ self .client .force_login (self .admin )
476
+
477
+ # Test with empty string
478
+ response = self .client .post (
479
+ reverse ("authentik_api:user-service-account" ),
480
+ data = {
481
+ "name" : "" ,
482
+ "create_group" : True ,
483
+ },
484
+ )
485
+ self .assertEqual (response .status_code , 400 )
486
+ self .assertJSONEqual (
487
+ response .content ,
488
+ {"name" : ["This field may not be blank." ]},
489
+ )
490
+
491
+ # Test with only whitespace
492
+ response = self .client .post (
493
+ reverse ("authentik_api:user-service-account" ),
494
+ data = {
495
+ "name" : " " ,
496
+ "create_group" : True ,
497
+ },
498
+ )
499
+ self .assertEqual (response .status_code , 400 )
500
+ self .assertJSONEqual (
501
+ response .content ,
502
+ {"name" : ["This field may not be blank." ]},
503
+ )
504
+
505
+ # Test with tab and newline characters
506
+ response = self .client .post (
507
+ reverse ("authentik_api:user-service-account" ),
508
+ data = {
509
+ "name" : "\t \n " ,
510
+ "create_group" : True ,
511
+ },
512
+ )
513
+ self .assertEqual (response .status_code , 400 )
514
+ self .assertJSONEqual (
515
+ response .content ,
516
+ {"name" : ["This field may not be blank." ]},
517
+ )
518
+
519
+ def test_service_account_validation_valid_username (self ):
520
+ """Test service account creation with valid username"""
521
+ self .client .force_login (self .admin )
522
+
523
+ # Test with valid username
524
+ response = self .client .post (
525
+ reverse ("authentik_api:user-service-account" ),
526
+ data = {
527
+ "name" : "valid-service-account" ,
528
+ "create_group" : True ,
529
+ },
530
+ )
531
+ self .assertEqual (response .status_code , 200 )
532
+
533
+ # Verify response structure
534
+ body = loads (response .content )
535
+ self .assertIn ("username" , body )
536
+ self .assertIn ("user_uid" , body )
537
+ self .assertIn ("user_pk" , body )
538
+ self .assertIn ("group_pk" , body ) # Should exist since create_group=True
539
+ self .assertIn ("token" , body )
540
+
541
+ # Verify field types
542
+ self .assertEqual (body ["username" ], "valid-service-account" )
543
+ self .assertIsInstance (body ["user_pk" ], int )
544
+ self .assertIsInstance (body ["user_uid" ], str )
545
+ self .assertIsInstance (body ["token" ], str )
546
+ self .assertIsInstance (body ["group_pk" ], str )
547
+
548
+ def test_service_account_validation_without_group (self ):
549
+ """Test service account creation without creating a group"""
550
+ self .client .force_login (self .admin )
551
+
552
+ response = self .client .post (
553
+ reverse ("authentik_api:user-service-account" ),
554
+ data = {
555
+ "name" : "no-group-service-account" ,
556
+ "create_group" : False ,
557
+ },
558
+ )
559
+ self .assertEqual (response .status_code , 200 )
560
+
561
+ body = loads (response .content )
562
+ self .assertIn ("username" , body )
563
+ self .assertIn ("user_uid" , body )
564
+ self .assertIn ("user_pk" , body )
565
+ self .assertIn ("token" , body )
566
+ # Should NOT have group_pk when create_group=False
567
+ self .assertNotIn ("group_pk" , body )
568
+
569
+ def test_service_account_validation_duplicate_username (self ):
570
+ """Test service account creation with duplicate username"""
571
+ self .client .force_login (self .admin )
572
+
573
+ # Create first service account
574
+ response = self .client .post (
575
+ reverse ("authentik_api:user-service-account" ),
576
+ data = {
577
+ "name" : "duplicate-test" ,
578
+ "create_group" : True ,
579
+ },
580
+ )
581
+ self .assertEqual (response .status_code , 200 )
582
+
583
+ # Attempt to create second with same username
584
+ response = self .client .post (
585
+ reverse ("authentik_api:user-service-account" ),
586
+ data = {
587
+ "name" : "duplicate-test" ,
588
+ "create_group" : True ,
589
+ },
590
+ )
591
+ self .assertEqual (response .status_code , 400 )
592
+ self .assertJSONEqual (
593
+ response .content ,
594
+ {"name" : ["This field must be unique." ]},
595
+ )
596
+
597
+ def test_service_account_validation_invalid_create_group (self ):
598
+ """Test service account creation with invalid create_group field"""
599
+ self .client .force_login (self .admin )
600
+
601
+ # Test with string instead of boolean
602
+ response = self .client .post (
603
+ reverse ("authentik_api:user-service-account" ),
604
+ data = {
605
+ "name" : "test-sa" ,
606
+ "create_group" : "invalid" ,
607
+ },
608
+ )
609
+ self .assertEqual (response .status_code , 400 )
610
+ self .assertJSONEqual (
611
+ response .content ,
612
+ {"create_group" : ["Must be a valid boolean." ]},
613
+ )
614
+
615
+ # Test with number instead of boolean
616
+ response = self .client .post (
617
+ reverse ("authentik_api:user-service-account" ),
618
+ data = {
619
+ "name" : "test-sa" ,
620
+ "create_group" : 123 ,
621
+ },
622
+ )
623
+ self .assertEqual (response .status_code , 400 )
624
+ self .assertJSONEqual (
625
+ response .content ,
626
+ {"create_group" : ["Must be a valid boolean." ]},
627
+ )
628
+
629
+ def test_service_account_validation_invalid_expiring (self ):
630
+ """Test service account creation with invalid expiring field"""
631
+ self .client .force_login (self .admin )
632
+
633
+ # Test with string instead of boolean
634
+ response = self .client .post (
635
+ reverse ("authentik_api:user-service-account" ),
636
+ data = {
637
+ "name" : "test-sa" ,
638
+ "expiring" : "invalid" ,
639
+ },
640
+ )
641
+ self .assertEqual (response .status_code , 400 )
642
+ self .assertJSONEqual (
643
+ response .content ,
644
+ {"expiring" : ["Must be a valid boolean." ]},
645
+ )
646
+
647
+ def test_service_account_validation_invalid_expires (self ):
648
+ """Test service account creation with invalid expires field"""
649
+ self .client .force_login (self .admin )
650
+
651
+ # Test with invalid datetime string
652
+ response = self .client .post (
653
+ reverse ("authentik_api:user-service-account" ),
654
+ data = {
655
+ "name" : "test-sa" ,
656
+ "expires" : "invalid-datetime" ,
657
+ },
658
+ )
659
+ self .assertEqual (response .status_code , 400 )
660
+ self .assertJSONEqual (
661
+ response .content ,
662
+ {
663
+ "expires" : [
664
+ "Datetime has wrong format. Use one of these formats instead: "
665
+ "YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]."
666
+ ]
667
+ },
668
+ )
669
+
670
+ # Test with invalid format
671
+ response = self .client .post (
672
+ reverse ("authentik_api:user-service-account" ),
673
+ data = {
674
+ "name" : "test-sa" ,
675
+ "expires" : "2024-13-45" , # Invalid month/day
676
+ },
677
+ )
678
+ self .assertEqual (response .status_code , 400 )
679
+ self .assertJSONEqual (
680
+ response .content ,
681
+ {
682
+ "expires" : [
683
+ "Datetime has wrong format. Use one of these formats instead: "
684
+ "YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]."
685
+ ]
686
+ },
687
+ )
688
+
689
+ def test_service_account_validation_multiple_errors (self ):
690
+ """Test service account creation with multiple validation errors"""
691
+ self .client .force_login (self .admin )
692
+
693
+ response = self .client .post (
694
+ reverse ("authentik_api:user-service-account" ),
695
+ data = {
696
+ "name" : "" , # Empty username
697
+ "create_group" : "invalid" , # Invalid boolean
698
+ "expiring" : 123 , # Invalid boolean
699
+ "expires" : "not-a-date" , # Invalid datetime
700
+ },
701
+ )
702
+ self .assertEqual (response .status_code , 400 )
703
+ self .assertJSONEqual (
704
+ response .content ,
705
+ {
706
+ "name" : ["This field may not be blank." ],
707
+ "create_group" : ["Must be a valid boolean." ],
708
+ "expiring" : ["Must be a valid boolean." ],
709
+ "expires" : [
710
+ "Datetime has wrong format. Use one of these formats instead: "
711
+ "YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]."
712
+ ],
713
+ },
714
+ )
715
+
716
+ def test_service_account_validation_user_friendly_duplicate_error (self ):
717
+ """Test that duplicate username returns user-friendly error, not database error"""
718
+ self .client .force_login (self .admin )
719
+
720
+ # Create first service account
721
+ response = self .client .post (
722
+ reverse ("authentik_api:user-service-account" ),
723
+ data = {
724
+ "name" : "duplicate-username-test" ,
725
+ "create_group" : True ,
726
+ },
727
+ )
728
+ self .assertEqual (response .status_code , 200 )
729
+
730
+ # Attempt to create second with same username
731
+ response = self .client .post (
732
+ reverse ("authentik_api:user-service-account" ),
733
+ data = {
734
+ "name" : "duplicate-username-test" ,
735
+ "create_group" : True ,
736
+ },
737
+ )
738
+ self .assertEqual (response .status_code , 400 )
739
+ self .assertJSONEqual (
740
+ response .content ,
741
+ {"name" : ["This field must be unique." ]},
742
+ )
0 commit comments