apache-airflow-providers-fab 3.1.1rc1__py3-none-any.whl → 3.2.0rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. airflow/providers/fab/__init__.py +1 -1
  2. airflow/providers/fab/auth_manager/api_endpoints/user_endpoint.py +3 -1
  3. airflow/providers/fab/auth_manager/api_fastapi/datamodels/roles.py +13 -7
  4. airflow/providers/fab/auth_manager/api_fastapi/datamodels/users.py +68 -0
  5. airflow/providers/fab/auth_manager/api_fastapi/openapi/v2-fab-auth-manager-generated.yaml +485 -18
  6. airflow/providers/fab/auth_manager/api_fastapi/routes/login.py +2 -4
  7. airflow/providers/fab/auth_manager/api_fastapi/routes/users.py +133 -0
  8. airflow/providers/fab/auth_manager/api_fastapi/services/login.py +1 -2
  9. airflow/providers/fab/auth_manager/api_fastapi/services/users.py +219 -0
  10. airflow/providers/fab/auth_manager/cli_commands/db_command.py +2 -2
  11. airflow/providers/fab/auth_manager/cli_commands/user_command.py +3 -3
  12. airflow/providers/fab/auth_manager/fab_auth_manager.py +18 -51
  13. airflow/providers/fab/auth_manager/models/__init__.py +6 -6
  14. airflow/providers/fab/auth_manager/security_manager/override.py +90 -77
  15. airflow/providers/fab/auth_manager/views/user.py +12 -0
  16. airflow/providers/fab/cli/__init__.py +18 -0
  17. airflow/providers/fab/{auth_manager/cli_commands → cli}/definition.py +50 -2
  18. airflow/providers/fab/get_provider_info.py +8 -0
  19. airflow/providers/fab/www/app.py +2 -7
  20. airflow/providers/fab/www/extensions/init_appbuilder.py +3 -2
  21. airflow/providers/fab/www/package-lock.json +669 -531
  22. airflow/providers/fab/www/package.json +9 -9
  23. airflow/providers/fab/www/static/dist/{743.0c0bf201ae17e66a9a3f.js → 743.8fb7d21632ed892227fe.js} +2 -2
  24. airflow/providers/fab/www/static/dist/{airflowDefaultTheme.ef6fc04c9b6920cd75c9.js → airflowDefaultTheme.51e5d14856ee1ebc83ca.js} +1 -1
  25. airflow/providers/fab/www/static/dist/{flash.eaaf777ec1b3628cf7be.js → flash.865b6940c00b2a9041b3.js} +1 -1
  26. airflow/providers/fab/www/static/dist/{loadingDots.76f4332c0a932c3dc08f.js → loadingDots.07f5b9805847242736e1.js} +1 -1
  27. airflow/providers/fab/www/static/dist/main.8cffe40bcf7cca998f4e.js +2 -0
  28. airflow/providers/fab/www/static/dist/manifest.json +13 -13
  29. airflow/providers/fab/www/static/dist/{materialIcons.ad07a489b2f0fc1a96bf.js → materialIcons.4fe84ae36604d84dec78.js} +1 -1
  30. airflow/providers/fab/www/static/dist/moment.0ec3ee3fb60dc999b1fd.js +1 -0
  31. airflow/providers/fab/www/static/js/main.js +11 -0
  32. airflow/providers/fab/www/templates/airflow/main.html +1 -0
  33. {apache_airflow_providers_fab-3.1.1rc1.dist-info → apache_airflow_providers_fab-3.2.0rc1.dist-info}/METADATA +10 -10
  34. {apache_airflow_providers_fab-3.1.1rc1.dist-info → apache_airflow_providers_fab-3.2.0rc1.dist-info}/RECORD +47 -43
  35. {apache_airflow_providers_fab-3.1.1rc1.dist-info → apache_airflow_providers_fab-3.2.0rc1.dist-info}/licenses/3rd-party-licenses/LICENSES-ui.txt +1 -1
  36. {apache_airflow_providers_fab-3.1.1rc1.dist-info → apache_airflow_providers_fab-3.2.0rc1.dist-info}/licenses/NOTICE +1 -1
  37. airflow/providers/fab/www/static/dist/main.bc1f701c3d133e2a3bab.js +0 -2
  38. airflow/providers/fab/www/static/dist/moment.5b85b4f6be2fe9c405ac.js +0 -1
  39. /airflow/providers/fab/www/static/dist/{743.0c0bf201ae17e66a9a3f.js.LICENSE.txt → 743.8fb7d21632ed892227fe.js.LICENSE.txt} +0 -0
  40. /airflow/providers/fab/www/static/dist/{airflowDefaultTheme.ef6fc04c9b6920cd75c9.css → airflowDefaultTheme.51e5d14856ee1ebc83ca.css} +0 -0
  41. /airflow/providers/fab/www/static/dist/{flash.eaaf777ec1b3628cf7be.css → flash.865b6940c00b2a9041b3.css} +0 -0
  42. /airflow/providers/fab/www/static/dist/{loadingDots.76f4332c0a932c3dc08f.css → loadingDots.07f5b9805847242736e1.css} +0 -0
  43. /airflow/providers/fab/www/static/dist/{main.bc1f701c3d133e2a3bab.css → main.8cffe40bcf7cca998f4e.css} +0 -0
  44. /airflow/providers/fab/www/static/dist/{main.bc1f701c3d133e2a3bab.js.LICENSE.txt → main.8cffe40bcf7cca998f4e.js.LICENSE.txt} +0 -0
  45. /airflow/providers/fab/www/static/dist/{materialIcons.ad07a489b2f0fc1a96bf.css → materialIcons.4fe84ae36604d84dec78.css} +0 -0
  46. /airflow/providers/fab/www/static/dist/{runtime.254c277d91ce3ac79c64.js → runtime.45b36fb8335446865b53.js} +0 -0
  47. {apache_airflow_providers_fab-3.1.1rc1.dist-info → apache_airflow_providers_fab-3.2.0rc1.dist-info}/WHEEL +0 -0
  48. {apache_airflow_providers_fab-3.1.1rc1.dist-info → apache_airflow_providers_fab-3.2.0rc1.dist-info}/entry_points.txt +0 -0
  49. {apache_airflow_providers_fab-3.1.1rc1.dist-info → apache_airflow_providers_fab-3.2.0rc1.dist-info}/licenses/LICENSE +0 -0
@@ -29,7 +29,7 @@ from airflow import __version__ as airflow_version
29
29
 
30
30
  __all__ = ["__version__"]
31
31
 
32
- __version__ = "3.1.1"
32
+ __version__ = "3.2.0"
33
33
 
34
34
  if packaging.version.parse(packaging.version.parse(airflow_version).base_version) < packaging.version.parse(
35
35
  "3.0.2"
@@ -120,7 +120,9 @@ def post_user() -> APIResponse:
120
120
  raise BadRequest(detail=detail)
121
121
 
122
122
  if not roles_to_add: # No roles provided, use the F.A.B's default registered user role.
123
- roles_to_add.append(security_manager.find_role(security_manager.auth_user_registration_role))
123
+ r = security_manager.find_role(security_manager.auth_user_registration_role)
124
+ if r:
125
+ roles_to_add.append(r)
124
126
 
125
127
  user = security_manager.add_user(role=roles_to_add, **data)
126
128
  if not user:
@@ -21,30 +21,36 @@ from pydantic import Field
21
21
  from airflow.api_fastapi.core_api.base import BaseModel, StrictBaseModel
22
22
 
23
23
 
24
- class ActionResponse(BaseModel):
24
+ class Action(BaseModel):
25
25
  """Outgoing representation of an action (permission name)."""
26
26
 
27
27
  name: str
28
28
 
29
29
 
30
- class ResourceResponse(BaseModel):
30
+ class Resource(BaseModel):
31
31
  """Outgoing representation of a resource."""
32
32
 
33
33
  name: str
34
34
 
35
35
 
36
- class ActionResourceResponse(BaseModel):
36
+ class ActionResource(BaseModel):
37
37
  """Pairing of an action with a resource."""
38
38
 
39
- action: ActionResponse
40
- resource: ResourceResponse
39
+ action: Action
40
+ resource: Resource
41
+
42
+
43
+ class Role(BaseModel):
44
+ """Lightweight role reference used by /users schemas."""
45
+
46
+ name: str
41
47
 
42
48
 
43
49
  class RoleBody(StrictBaseModel):
44
50
  """Incoming payload for creating/updating a role."""
45
51
 
46
52
  name: str = Field(min_length=1)
47
- permissions: list[ActionResourceResponse] = Field(
53
+ permissions: list[ActionResource] = Field(
48
54
  default_factory=list, alias="actions", validation_alias="actions"
49
55
  )
50
56
 
@@ -53,7 +59,7 @@ class RoleResponse(BaseModel):
53
59
  """Outgoing representation of a role and its permissions."""
54
60
 
55
61
  name: str
56
- permissions: list[ActionResourceResponse] = Field(default_factory=list, serialization_alias="actions")
62
+ permissions: list[ActionResource] = Field(default_factory=list, serialization_alias="actions")
57
63
 
58
64
 
59
65
  class RoleCollectionResponse(BaseModel):
@@ -0,0 +1,68 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+ from __future__ import annotations
18
+
19
+ from pydantic import Field, SecretStr
20
+
21
+ from airflow.api_fastapi.common.types import UtcDateTime
22
+ from airflow.api_fastapi.core_api.base import BaseModel, StrictBaseModel
23
+ from airflow.providers.fab.auth_manager.api_fastapi.datamodels.roles import Role
24
+
25
+
26
+ class UserBody(StrictBaseModel):
27
+ """Incoming payload for creating a user."""
28
+
29
+ username: str = Field(min_length=1)
30
+ email: str = Field(min_length=1)
31
+ first_name: str = Field(min_length=1)
32
+ last_name: str = Field(min_length=1)
33
+ roles: list[Role] | None = None
34
+ password: SecretStr
35
+
36
+
37
+ class UserPatchBody(StrictBaseModel):
38
+ """Incoming payload for updating a user (all fields optional)."""
39
+
40
+ username: str | None = Field(default=None, min_length=1)
41
+ email: str | None = Field(default=None, min_length=1)
42
+ first_name: str | None = Field(default=None, min_length=1)
43
+ last_name: str | None = Field(default=None, min_length=1)
44
+ roles: list[Role] | None = None
45
+ password: SecretStr | None = None
46
+
47
+
48
+ class UserResponse(BaseModel):
49
+ """Outgoing representation of a user (no password)."""
50
+
51
+ username: str
52
+ email: str
53
+ first_name: str
54
+ last_name: str
55
+ roles: list[Role] | None = None
56
+ active: bool | None = None
57
+ last_login: UtcDateTime | None = None
58
+ login_count: int | None = None
59
+ fail_login_count: int | None = None
60
+ created_on: UtcDateTime | None = None
61
+ changed_on: UtcDateTime | None = None
62
+
63
+
64
+ class UserCollectionResponse(BaseModel):
65
+ """Response model for a collection of users."""
66
+
67
+ users: list[UserResponse]
68
+ total_entries: int
@@ -398,21 +398,304 @@ paths:
398
398
  application/json:
399
399
  schema:
400
400
  $ref: '#/components/schemas/HTTPValidationError'
401
+ /auth/fab/v1/users:
402
+ post:
403
+ tags:
404
+ - FabAuthManager
405
+ summary: Create User
406
+ operationId: create_user
407
+ security:
408
+ - OAuth2PasswordBearer: []
409
+ - HTTPBearer: []
410
+ requestBody:
411
+ required: true
412
+ content:
413
+ application/json:
414
+ schema:
415
+ $ref: '#/components/schemas/UserBody'
416
+ responses:
417
+ '200':
418
+ description: Successful Response
419
+ content:
420
+ application/json:
421
+ schema:
422
+ $ref: '#/components/schemas/UserResponse'
423
+ '400':
424
+ content:
425
+ application/json:
426
+ schema:
427
+ $ref: '#/components/schemas/HTTPExceptionResponse'
428
+ description: Bad Request
429
+ '401':
430
+ content:
431
+ application/json:
432
+ schema:
433
+ $ref: '#/components/schemas/HTTPExceptionResponse'
434
+ description: Unauthorized
435
+ '403':
436
+ content:
437
+ application/json:
438
+ schema:
439
+ $ref: '#/components/schemas/HTTPExceptionResponse'
440
+ description: Forbidden
441
+ '409':
442
+ content:
443
+ application/json:
444
+ schema:
445
+ $ref: '#/components/schemas/HTTPExceptionResponse'
446
+ description: Conflict
447
+ '500':
448
+ content:
449
+ application/json:
450
+ schema:
451
+ $ref: '#/components/schemas/HTTPExceptionResponse'
452
+ description: Internal Server Error
453
+ '422':
454
+ description: Validation Error
455
+ content:
456
+ application/json:
457
+ schema:
458
+ $ref: '#/components/schemas/HTTPValidationError'
459
+ get:
460
+ tags:
461
+ - FabAuthManager
462
+ summary: Get Users
463
+ description: List users with pagination and ordering.
464
+ operationId: get_users
465
+ security:
466
+ - OAuth2PasswordBearer: []
467
+ - HTTPBearer: []
468
+ parameters:
469
+ - name: order_by
470
+ in: query
471
+ required: false
472
+ schema:
473
+ type: string
474
+ description: Field to order by. Prefix with '-' for descending.
475
+ default: id
476
+ title: Order By
477
+ description: Field to order by. Prefix with '-' for descending.
478
+ - name: offset
479
+ in: query
480
+ required: false
481
+ schema:
482
+ type: integer
483
+ minimum: 0
484
+ description: Number of items to skip before starting to collect results.
485
+ default: 0
486
+ title: Offset
487
+ description: Number of items to skip before starting to collect results.
488
+ - name: limit
489
+ in: query
490
+ required: false
491
+ schema:
492
+ type: integer
493
+ minimum: 0
494
+ default: 100
495
+ title: Limit
496
+ responses:
497
+ '200':
498
+ description: Successful Response
499
+ content:
500
+ application/json:
501
+ schema:
502
+ $ref: '#/components/schemas/UserCollectionResponse'
503
+ '400':
504
+ content:
505
+ application/json:
506
+ schema:
507
+ $ref: '#/components/schemas/HTTPExceptionResponse'
508
+ description: Bad Request
509
+ '401':
510
+ content:
511
+ application/json:
512
+ schema:
513
+ $ref: '#/components/schemas/HTTPExceptionResponse'
514
+ description: Unauthorized
515
+ '403':
516
+ content:
517
+ application/json:
518
+ schema:
519
+ $ref: '#/components/schemas/HTTPExceptionResponse'
520
+ description: Forbidden
521
+ '422':
522
+ description: Validation Error
523
+ content:
524
+ application/json:
525
+ schema:
526
+ $ref: '#/components/schemas/HTTPValidationError'
527
+ /auth/fab/v1/users/{username}:
528
+ get:
529
+ tags:
530
+ - FabAuthManager
531
+ summary: Get User
532
+ description: Get a user by username.
533
+ operationId: get_user
534
+ security:
535
+ - OAuth2PasswordBearer: []
536
+ - HTTPBearer: []
537
+ parameters:
538
+ - name: username
539
+ in: path
540
+ required: true
541
+ schema:
542
+ type: string
543
+ minLength: 1
544
+ title: Username
545
+ responses:
546
+ '200':
547
+ description: Successful Response
548
+ content:
549
+ application/json:
550
+ schema:
551
+ $ref: '#/components/schemas/UserResponse'
552
+ '401':
553
+ content:
554
+ application/json:
555
+ schema:
556
+ $ref: '#/components/schemas/HTTPExceptionResponse'
557
+ description: Unauthorized
558
+ '403':
559
+ content:
560
+ application/json:
561
+ schema:
562
+ $ref: '#/components/schemas/HTTPExceptionResponse'
563
+ description: Forbidden
564
+ '404':
565
+ content:
566
+ application/json:
567
+ schema:
568
+ $ref: '#/components/schemas/HTTPExceptionResponse'
569
+ description: Not Found
570
+ '422':
571
+ description: Validation Error
572
+ content:
573
+ application/json:
574
+ schema:
575
+ $ref: '#/components/schemas/HTTPValidationError'
576
+ patch:
577
+ tags:
578
+ - FabAuthManager
579
+ summary: Update User
580
+ description: Update an existing user.
581
+ operationId: update_user
582
+ security:
583
+ - OAuth2PasswordBearer: []
584
+ - HTTPBearer: []
585
+ parameters:
586
+ - name: username
587
+ in: path
588
+ required: true
589
+ schema:
590
+ type: string
591
+ minLength: 1
592
+ title: Username
593
+ - name: update_mask
594
+ in: query
595
+ required: false
596
+ schema:
597
+ anyOf:
598
+ - type: string
599
+ - type: 'null'
600
+ description: Comma-separated list of fields to update
601
+ title: Update Mask
602
+ description: Comma-separated list of fields to update
603
+ requestBody:
604
+ required: true
605
+ content:
606
+ application/json:
607
+ schema:
608
+ $ref: '#/components/schemas/UserPatchBody'
609
+ responses:
610
+ '200':
611
+ description: Successful Response
612
+ content:
613
+ application/json:
614
+ schema:
615
+ $ref: '#/components/schemas/UserResponse'
616
+ '400':
617
+ content:
618
+ application/json:
619
+ schema:
620
+ $ref: '#/components/schemas/HTTPExceptionResponse'
621
+ description: Bad Request
622
+ '401':
623
+ content:
624
+ application/json:
625
+ schema:
626
+ $ref: '#/components/schemas/HTTPExceptionResponse'
627
+ description: Unauthorized
628
+ '403':
629
+ content:
630
+ application/json:
631
+ schema:
632
+ $ref: '#/components/schemas/HTTPExceptionResponse'
633
+ description: Forbidden
634
+ '404':
635
+ content:
636
+ application/json:
637
+ schema:
638
+ $ref: '#/components/schemas/HTTPExceptionResponse'
639
+ description: Not Found
640
+ '409':
641
+ content:
642
+ application/json:
643
+ schema:
644
+ $ref: '#/components/schemas/HTTPExceptionResponse'
645
+ description: Conflict
646
+ '422':
647
+ description: Validation Error
648
+ content:
649
+ application/json:
650
+ schema:
651
+ $ref: '#/components/schemas/HTTPValidationError'
652
+ delete:
653
+ tags:
654
+ - FabAuthManager
655
+ summary: Delete User
656
+ description: Delete a user by username.
657
+ operationId: delete_user
658
+ security:
659
+ - OAuth2PasswordBearer: []
660
+ - HTTPBearer: []
661
+ parameters:
662
+ - name: username
663
+ in: path
664
+ required: true
665
+ schema:
666
+ type: string
667
+ minLength: 1
668
+ title: Username
669
+ responses:
670
+ '204':
671
+ description: Successful Response
672
+ '401':
673
+ content:
674
+ application/json:
675
+ schema:
676
+ $ref: '#/components/schemas/HTTPExceptionResponse'
677
+ description: Unauthorized
678
+ '403':
679
+ content:
680
+ application/json:
681
+ schema:
682
+ $ref: '#/components/schemas/HTTPExceptionResponse'
683
+ description: Forbidden
684
+ '404':
685
+ content:
686
+ application/json:
687
+ schema:
688
+ $ref: '#/components/schemas/HTTPExceptionResponse'
689
+ description: Not Found
690
+ '422':
691
+ description: Validation Error
692
+ content:
693
+ application/json:
694
+ schema:
695
+ $ref: '#/components/schemas/HTTPValidationError'
401
696
  components:
402
697
  schemas:
403
- ActionResourceResponse:
404
- properties:
405
- action:
406
- $ref: '#/components/schemas/ActionResponse'
407
- resource:
408
- $ref: '#/components/schemas/ResourceResponse'
409
- type: object
410
- required:
411
- - action
412
- - resource
413
- title: ActionResourceResponse
414
- description: Pairing of an action with a resource.
415
- ActionResponse:
698
+ Action:
416
699
  properties:
417
700
  name:
418
701
  type: string
@@ -420,8 +703,20 @@ components:
420
703
  type: object
421
704
  required:
422
705
  - name
423
- title: ActionResponse
706
+ title: Action
424
707
  description: Outgoing representation of an action (permission name).
708
+ ActionResource:
709
+ properties:
710
+ action:
711
+ $ref: '#/components/schemas/Action'
712
+ resource:
713
+ $ref: '#/components/schemas/Resource'
714
+ type: object
715
+ required:
716
+ - action
717
+ - resource
718
+ title: ActionResource
719
+ description: Pairing of an action with a resource.
425
720
  HTTPExceptionResponse:
426
721
  properties:
427
722
  detail:
@@ -454,7 +749,7 @@ components:
454
749
  - access_token
455
750
  title: LoginResponse
456
751
  description: API Token serializer for responses.
457
- ResourceResponse:
752
+ Resource:
458
753
  properties:
459
754
  name:
460
755
  type: string
@@ -462,8 +757,18 @@ components:
462
757
  type: object
463
758
  required:
464
759
  - name
465
- title: ResourceResponse
760
+ title: Resource
466
761
  description: Outgoing representation of a resource.
762
+ Role:
763
+ properties:
764
+ name:
765
+ type: string
766
+ title: Name
767
+ type: object
768
+ required:
769
+ - name
770
+ title: Role
771
+ description: Lightweight role reference used by /users schemas.
467
772
  RoleBody:
468
773
  properties:
469
774
  name:
@@ -472,7 +777,7 @@ components:
472
777
  title: Name
473
778
  actions:
474
779
  items:
475
- $ref: '#/components/schemas/ActionResourceResponse'
780
+ $ref: '#/components/schemas/ActionResource'
476
781
  type: array
477
782
  title: Actions
478
783
  additionalProperties: false
@@ -504,7 +809,7 @@ components:
504
809
  title: Name
505
810
  actions:
506
811
  items:
507
- $ref: '#/components/schemas/ActionResourceResponse'
812
+ $ref: '#/components/schemas/ActionResource'
508
813
  type: array
509
814
  title: Actions
510
815
  type: object
@@ -512,6 +817,168 @@ components:
512
817
  - name
513
818
  title: RoleResponse
514
819
  description: Outgoing representation of a role and its permissions.
820
+ UserBody:
821
+ properties:
822
+ username:
823
+ type: string
824
+ minLength: 1
825
+ title: Username
826
+ email:
827
+ type: string
828
+ minLength: 1
829
+ title: Email
830
+ first_name:
831
+ type: string
832
+ minLength: 1
833
+ title: First Name
834
+ last_name:
835
+ type: string
836
+ minLength: 1
837
+ title: Last Name
838
+ roles:
839
+ anyOf:
840
+ - items:
841
+ $ref: '#/components/schemas/Role'
842
+ type: array
843
+ - type: 'null'
844
+ title: Roles
845
+ password:
846
+ type: string
847
+ format: password
848
+ title: Password
849
+ writeOnly: true
850
+ additionalProperties: false
851
+ type: object
852
+ required:
853
+ - username
854
+ - email
855
+ - first_name
856
+ - last_name
857
+ - password
858
+ title: UserBody
859
+ description: Incoming payload for creating a user.
860
+ UserCollectionResponse:
861
+ properties:
862
+ users:
863
+ items:
864
+ $ref: '#/components/schemas/UserResponse'
865
+ type: array
866
+ title: Users
867
+ total_entries:
868
+ type: integer
869
+ title: Total Entries
870
+ type: object
871
+ required:
872
+ - users
873
+ - total_entries
874
+ title: UserCollectionResponse
875
+ description: Response model for a collection of users.
876
+ UserPatchBody:
877
+ properties:
878
+ username:
879
+ anyOf:
880
+ - type: string
881
+ minLength: 1
882
+ - type: 'null'
883
+ title: Username
884
+ email:
885
+ anyOf:
886
+ - type: string
887
+ minLength: 1
888
+ - type: 'null'
889
+ title: Email
890
+ first_name:
891
+ anyOf:
892
+ - type: string
893
+ minLength: 1
894
+ - type: 'null'
895
+ title: First Name
896
+ last_name:
897
+ anyOf:
898
+ - type: string
899
+ minLength: 1
900
+ - type: 'null'
901
+ title: Last Name
902
+ roles:
903
+ anyOf:
904
+ - items:
905
+ $ref: '#/components/schemas/Role'
906
+ type: array
907
+ - type: 'null'
908
+ title: Roles
909
+ password:
910
+ anyOf:
911
+ - type: string
912
+ format: password
913
+ writeOnly: true
914
+ - type: 'null'
915
+ title: Password
916
+ additionalProperties: false
917
+ type: object
918
+ title: UserPatchBody
919
+ description: Incoming payload for updating a user (all fields optional).
920
+ UserResponse:
921
+ properties:
922
+ username:
923
+ type: string
924
+ title: Username
925
+ email:
926
+ type: string
927
+ title: Email
928
+ first_name:
929
+ type: string
930
+ title: First Name
931
+ last_name:
932
+ type: string
933
+ title: Last Name
934
+ roles:
935
+ anyOf:
936
+ - items:
937
+ $ref: '#/components/schemas/Role'
938
+ type: array
939
+ - type: 'null'
940
+ title: Roles
941
+ active:
942
+ anyOf:
943
+ - type: boolean
944
+ - type: 'null'
945
+ title: Active
946
+ last_login:
947
+ anyOf:
948
+ - type: string
949
+ format: date-time
950
+ - type: 'null'
951
+ title: Last Login
952
+ login_count:
953
+ anyOf:
954
+ - type: integer
955
+ - type: 'null'
956
+ title: Login Count
957
+ fail_login_count:
958
+ anyOf:
959
+ - type: integer
960
+ - type: 'null'
961
+ title: Fail Login Count
962
+ created_on:
963
+ anyOf:
964
+ - type: string
965
+ format: date-time
966
+ - type: 'null'
967
+ title: Created On
968
+ changed_on:
969
+ anyOf:
970
+ - type: string
971
+ format: date-time
972
+ - type: 'null'
973
+ title: Changed On
974
+ type: object
975
+ required:
976
+ - username
977
+ - email
978
+ - first_name
979
+ - last_name
980
+ title: UserResponse
981
+ description: Outgoing representation of a user (no password).
515
982
  ValidationError:
516
983
  properties:
517
984
  loc:
@@ -18,10 +18,8 @@ from __future__ import annotations
18
18
 
19
19
  from typing import Any
20
20
 
21
- from fastapi import Body
22
- from starlette import status
23
- from starlette.requests import Request # noqa: TC002
24
- from starlette.responses import RedirectResponse
21
+ from fastapi import Body, Request, status
22
+ from fastapi.responses import RedirectResponse
25
23
 
26
24
  from airflow.api_fastapi.app import get_auth_manager
27
25
  from airflow.api_fastapi.auth.managers.base_auth_manager import COOKIE_NAME_JWT_TOKEN