zou 0.20.81__py3-none-any.whl → 0.20.83__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.
- zou/__init__.py +1 -1
- zou/app/blueprints/assets/resources.py +1060 -153
- zou/app/blueprints/auth/resources.py +374 -240
- zou/app/blueprints/breakdown/resources.py +584 -94
- zou/app/blueprints/chats/resources.py +176 -37
- zou/app/blueprints/comments/resources.py +387 -125
- zou/app/blueprints/concepts/resources.py +428 -63
- zou/app/blueprints/departments/resources.py +302 -68
- zou/app/blueprints/edits/resources.py +651 -81
- zou/app/blueprints/entities/resources.py +104 -39
- zou/app/blueprints/events/resources.py +96 -8
- zou/app/blueprints/index/resources.py +49 -42
- zou/app/blueprints/news/resources.py +45 -27
- zou/app/blueprints/user/resources.py +1808 -215
- zou/app/services/emails_service.py +2 -0
- zou/app/swagger.py +100 -27
- {zou-0.20.81.dist-info → zou-0.20.83.dist-info}/METADATA +1 -1
- {zou-0.20.81.dist-info → zou-0.20.83.dist-info}/RECORD +22 -22
- {zou-0.20.81.dist-info → zou-0.20.83.dist-info}/WHEEL +0 -0
- {zou-0.20.81.dist-info → zou-0.20.83.dist-info}/entry_points.txt +0 -0
- {zou-0.20.81.dist-info → zou-0.20.83.dist-info}/licenses/LICENSE +0 -0
- {zou-0.20.81.dist-info → zou-0.20.83.dist-info}/top_level.txt +0 -0
|
@@ -29,7 +29,7 @@ from zou.app.services import (
|
|
|
29
29
|
persons_service,
|
|
30
30
|
auth_service,
|
|
31
31
|
events_service,
|
|
32
|
-
templates_service
|
|
32
|
+
templates_service,
|
|
33
33
|
)
|
|
34
34
|
|
|
35
35
|
from zou.app.utils.flask import is_from_browser
|
|
@@ -58,20 +58,15 @@ from zou.app.services.exception import (
|
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
class AuthenticatedResource(Resource):
|
|
61
|
-
"""
|
|
62
|
-
Returns information if the user is authenticated else it returns a 401
|
|
63
|
-
response.
|
|
64
|
-
It can be used by third party tools, especially browser frontend, to know
|
|
65
|
-
if current user is still logged in.
|
|
66
|
-
"""
|
|
67
61
|
|
|
68
62
|
@jwt_required()
|
|
69
63
|
def get(self):
|
|
70
64
|
"""
|
|
71
|
-
|
|
72
|
-
response.
|
|
65
|
+
Check authentication status
|
|
73
66
|
---
|
|
74
|
-
description:
|
|
67
|
+
description: Returns information if the user is authenticated.
|
|
68
|
+
It can be used by third party tools, especially browser frontend,
|
|
69
|
+
to know if current user is still logged in.
|
|
75
70
|
tags:
|
|
76
71
|
- Authentication
|
|
77
72
|
responses:
|
|
@@ -92,25 +87,19 @@ class AuthenticatedResource(Resource):
|
|
|
92
87
|
|
|
93
88
|
|
|
94
89
|
class LogoutResource(Resource):
|
|
95
|
-
"""
|
|
96
|
-
Log user out by revoking his auth tokens. Once log out, current user
|
|
97
|
-
cannot access to API anymore.
|
|
98
|
-
"""
|
|
99
90
|
|
|
100
91
|
@jwt_required()
|
|
101
92
|
@permissions.require_person
|
|
102
93
|
def get(self):
|
|
103
94
|
"""
|
|
104
|
-
|
|
95
|
+
Logout user
|
|
105
96
|
---
|
|
106
|
-
description: Once logged out, current user cannot access the API anymore.
|
|
97
|
+
description: Log user out by revoking auth tokens. Once logged out, current user cannot access the API anymore.
|
|
107
98
|
tags:
|
|
108
99
|
- Authentication
|
|
109
100
|
responses:
|
|
110
101
|
200:
|
|
111
102
|
description: Logout successful
|
|
112
|
-
500:
|
|
113
|
-
description: Access token not found
|
|
114
103
|
"""
|
|
115
104
|
try:
|
|
116
105
|
auth_service.logout(get_jwt()["jti"])
|
|
@@ -131,49 +120,61 @@ class LogoutResource(Resource):
|
|
|
131
120
|
|
|
132
121
|
|
|
133
122
|
class LoginResource(Resource, ArgsMixin):
|
|
134
|
-
"""
|
|
135
|
-
Log in user by creating and registering auth tokens. Login is based
|
|
136
|
-
on email and password. If no user match given email and a destkop ID,
|
|
137
|
-
it looks in matching the desktop ID with the one stored in database. It is
|
|
138
|
-
useful for clients that run on desktop tools and that don't know user
|
|
139
|
-
email.
|
|
140
|
-
"""
|
|
141
123
|
|
|
142
124
|
def post(self):
|
|
143
125
|
"""
|
|
144
|
-
|
|
126
|
+
Login user
|
|
145
127
|
---
|
|
146
|
-
description:
|
|
147
|
-
|
|
148
|
-
|
|
128
|
+
description: Log in user by creating and registering auth tokens.
|
|
129
|
+
Login is based on email and password. If no user matches given email
|
|
130
|
+
It fallbacks to a desktop ID. It is useful for desktop tools that
|
|
131
|
+
don't know user email.
|
|
132
|
+
It is also possible to login with TOTP, Email OTP, FIDO and recovery
|
|
133
|
+
code.
|
|
149
134
|
tags:
|
|
150
135
|
- Authentication
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
136
|
+
requestBody:
|
|
137
|
+
required: true
|
|
138
|
+
content:
|
|
139
|
+
application/json:
|
|
140
|
+
schema:
|
|
141
|
+
type: object
|
|
142
|
+
properties:
|
|
143
|
+
email:
|
|
144
|
+
type: string
|
|
145
|
+
format: email
|
|
146
|
+
example: admin@example.com
|
|
147
|
+
description: User email address
|
|
148
|
+
password:
|
|
149
|
+
type: string
|
|
150
|
+
format: password
|
|
151
|
+
example: mysecretpassword
|
|
152
|
+
description: User password
|
|
153
|
+
required: true
|
|
154
|
+
totp:
|
|
155
|
+
type: string
|
|
156
|
+
example: 123456
|
|
157
|
+
description: TOTP verification code for two-factor authentication
|
|
158
|
+
required: false
|
|
159
|
+
email_otp:
|
|
160
|
+
type: string
|
|
161
|
+
example: 123456
|
|
162
|
+
description: Email OTP verification code for two-factor authentication
|
|
163
|
+
fido_authentication_response:
|
|
164
|
+
type: object
|
|
165
|
+
description: FIDO authentication response for WebAuth
|
|
166
|
+
recovery_code:
|
|
167
|
+
type: string
|
|
168
|
+
example: ABCD-EFGH-IJKL-MNOP
|
|
169
|
+
description: Recovery code for two-factor authentication
|
|
170
|
+
required:
|
|
171
|
+
- email
|
|
172
|
+
- password
|
|
170
173
|
responses:
|
|
171
174
|
200:
|
|
172
175
|
description: Login successful
|
|
173
176
|
400:
|
|
174
177
|
description: Login failed
|
|
175
|
-
500:
|
|
176
|
-
description: Database not reachable
|
|
177
178
|
"""
|
|
178
179
|
(
|
|
179
180
|
email,
|
|
@@ -372,9 +373,10 @@ class RefreshTokenResource(Resource):
|
|
|
372
373
|
@permissions.require_person
|
|
373
374
|
def get(self):
|
|
374
375
|
"""
|
|
375
|
-
|
|
376
|
+
Refresh access token
|
|
376
377
|
---
|
|
377
|
-
description:
|
|
378
|
+
description: Tokens are considered outdated every two weeks.
|
|
379
|
+
This route allows to extend their lifetime before they get outdated.
|
|
378
380
|
tags:
|
|
379
381
|
- Authentication
|
|
380
382
|
responses:
|
|
@@ -403,35 +405,43 @@ class RegistrationResource(Resource, ArgsMixin):
|
|
|
403
405
|
|
|
404
406
|
def post(self):
|
|
405
407
|
"""
|
|
406
|
-
|
|
408
|
+
Register new user
|
|
407
409
|
---
|
|
410
|
+
description: Allow a user to register himself to the service.
|
|
408
411
|
tags:
|
|
409
412
|
- Authentication
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
413
|
+
requestBody:
|
|
414
|
+
required: true
|
|
415
|
+
content:
|
|
416
|
+
application/json:
|
|
417
|
+
schema:
|
|
418
|
+
type: object
|
|
419
|
+
properties:
|
|
420
|
+
email:
|
|
421
|
+
type: string
|
|
422
|
+
format: email
|
|
423
|
+
example: admin@example.com
|
|
424
|
+
description: User email address
|
|
425
|
+
password:
|
|
426
|
+
type: string
|
|
427
|
+
format: password
|
|
428
|
+
description: User password
|
|
429
|
+
password_2:
|
|
430
|
+
type: string
|
|
431
|
+
format: password
|
|
432
|
+
description: Password confirmation
|
|
433
|
+
first_name:
|
|
434
|
+
type: string
|
|
435
|
+
description: User first name
|
|
436
|
+
last_name:
|
|
437
|
+
type: string
|
|
438
|
+
description: User last name
|
|
439
|
+
required:
|
|
440
|
+
- email
|
|
441
|
+
- password
|
|
442
|
+
- password_2
|
|
443
|
+
- first_name
|
|
444
|
+
- last_name
|
|
435
445
|
responses:
|
|
436
446
|
201:
|
|
437
447
|
description: Registration successful
|
|
@@ -508,42 +518,41 @@ class RegistrationResource(Resource, ArgsMixin):
|
|
|
508
518
|
|
|
509
519
|
|
|
510
520
|
class ChangePasswordResource(Resource, ArgsMixin):
|
|
511
|
-
"""
|
|
512
|
-
Allow the user to change his password. Prior to modify the password,
|
|
513
|
-
it requires to give the current password (to make sure the user changing
|
|
514
|
-
the password is not someone who stealed the session).
|
|
515
|
-
The new password requires a confirmation to ensure that the user didn't
|
|
516
|
-
make mistake by typing his new password.
|
|
517
|
-
"""
|
|
518
521
|
|
|
519
522
|
@jwt_required()
|
|
520
523
|
@permissions.require_person
|
|
521
524
|
def post(self):
|
|
522
525
|
"""
|
|
523
|
-
|
|
526
|
+
Change user password
|
|
524
527
|
---
|
|
525
|
-
description:
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
make a mistake by typing his new password.
|
|
528
|
+
description: Allow the user to change his password. Requires current
|
|
529
|
+
password for verification and password confirmation to ensure
|
|
530
|
+
accuracy.
|
|
529
531
|
tags:
|
|
530
532
|
- Authentication
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
533
|
+
requestBody:
|
|
534
|
+
required: true
|
|
535
|
+
content:
|
|
536
|
+
application/json:
|
|
537
|
+
schema:
|
|
538
|
+
type: object
|
|
539
|
+
properties:
|
|
540
|
+
old_password:
|
|
541
|
+
type: string
|
|
542
|
+
format: password
|
|
543
|
+
description: Current password
|
|
544
|
+
password:
|
|
545
|
+
type: string
|
|
546
|
+
format: password
|
|
547
|
+
description: New password
|
|
548
|
+
password_2:
|
|
549
|
+
type: string
|
|
550
|
+
format: password
|
|
551
|
+
description: New password confirmation
|
|
552
|
+
required:
|
|
553
|
+
- old_password
|
|
554
|
+
- password
|
|
555
|
+
- password_2
|
|
547
556
|
responses:
|
|
548
557
|
200:
|
|
549
558
|
description: Password changed
|
|
@@ -625,42 +634,45 @@ Your IP when you have changed your password is: {person_IP}.
|
|
|
625
634
|
|
|
626
635
|
|
|
627
636
|
class ResetPasswordResource(Resource, ArgsMixin):
|
|
628
|
-
"""
|
|
629
|
-
Resource to allow a user to change his password when he forgets it.
|
|
630
|
-
It uses a classic scheme: a token is sent by email to the user. Then
|
|
631
|
-
he can change his password.
|
|
632
|
-
"""
|
|
633
637
|
|
|
634
638
|
def put(self):
|
|
635
639
|
"""
|
|
636
|
-
|
|
640
|
+
Reset password with token
|
|
637
641
|
---
|
|
638
|
-
description:
|
|
639
|
-
|
|
642
|
+
description: Allow a user to change his password when he forgets it.
|
|
643
|
+
It uses a token sent by email to the user to verify it is the user
|
|
644
|
+
who requested the password reset.
|
|
640
645
|
tags:
|
|
641
646
|
- Authentication
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
647
|
+
requestBody:
|
|
648
|
+
required: true
|
|
649
|
+
content:
|
|
650
|
+
application/json:
|
|
651
|
+
schema:
|
|
652
|
+
type: object
|
|
653
|
+
properties:
|
|
654
|
+
email:
|
|
655
|
+
type: string
|
|
656
|
+
format: email
|
|
657
|
+
example: admin@example.com
|
|
658
|
+
description: User email address
|
|
659
|
+
token:
|
|
660
|
+
type: string
|
|
661
|
+
format: JWT token
|
|
662
|
+
description: Password reset token
|
|
663
|
+
password:
|
|
664
|
+
type: string
|
|
665
|
+
format: password
|
|
666
|
+
description: New password
|
|
667
|
+
password2:
|
|
668
|
+
type: string
|
|
669
|
+
format: password
|
|
670
|
+
description: New password confirmation
|
|
671
|
+
required:
|
|
672
|
+
- email
|
|
673
|
+
- token
|
|
674
|
+
- password
|
|
675
|
+
- password2
|
|
664
676
|
responses:
|
|
665
677
|
200:
|
|
666
678
|
description: Password reset
|
|
@@ -712,19 +724,26 @@ class ResetPasswordResource(Resource, ArgsMixin):
|
|
|
712
724
|
|
|
713
725
|
def post(self):
|
|
714
726
|
"""
|
|
715
|
-
|
|
727
|
+
Request password reset
|
|
716
728
|
---
|
|
717
|
-
description:
|
|
718
|
-
|
|
729
|
+
description: Send a password reset token by email to the user.
|
|
730
|
+
It uses a classic scheme where a token is sent by email.
|
|
719
731
|
tags:
|
|
720
732
|
- Authentication
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
733
|
+
requestBody:
|
|
734
|
+
required: true
|
|
735
|
+
content:
|
|
736
|
+
application/json:
|
|
737
|
+
schema:
|
|
738
|
+
type: object
|
|
739
|
+
properties:
|
|
740
|
+
email:
|
|
741
|
+
type: string
|
|
742
|
+
format: email
|
|
743
|
+
example: admin@example.com
|
|
744
|
+
description: User email address
|
|
745
|
+
required:
|
|
746
|
+
- email
|
|
728
747
|
responses:
|
|
729
748
|
200:
|
|
730
749
|
description: Reset token sent
|
|
@@ -767,8 +786,12 @@ class ResetPasswordResource(Resource, ArgsMixin):
|
|
|
767
786
|
html = f"""<p>Hello {user["first_name"]},</p>
|
|
768
787
|
|
|
769
788
|
<p>
|
|
770
|
-
You have requested for a password reset.
|
|
771
|
-
password:
|
|
789
|
+
You have requested for a password reset. Click on the following button
|
|
790
|
+
to change your password:
|
|
791
|
+
</p>
|
|
792
|
+
|
|
793
|
+
<p class="cta">
|
|
794
|
+
<a class="button" href="{reset_url}">Change your password</a>
|
|
772
795
|
</p>
|
|
773
796
|
|
|
774
797
|
<p>
|
|
@@ -785,26 +808,22 @@ The IP of the person who requested this is: {person_IP}.
|
|
|
785
808
|
|
|
786
809
|
|
|
787
810
|
class TOTPResource(Resource, ArgsMixin):
|
|
788
|
-
"""
|
|
789
|
-
Resource to allow a user to enable/disable TOTP.
|
|
790
|
-
"""
|
|
791
811
|
|
|
792
812
|
@jwt_required()
|
|
793
813
|
@permissions.require_person
|
|
794
814
|
def put(self):
|
|
795
815
|
"""
|
|
796
|
-
|
|
816
|
+
Pre-enable TOTP
|
|
797
817
|
---
|
|
798
|
-
description:
|
|
818
|
+
description: Prepare TOTP (Time-based One-Time Password) for enabling.
|
|
819
|
+
It returns provisioning URI and secret for authenticator app setup.
|
|
799
820
|
tags:
|
|
800
821
|
- Authentication
|
|
801
822
|
responses:
|
|
802
823
|
200:
|
|
803
|
-
description: TOTP enabled
|
|
824
|
+
description: TOTP pre-enabled
|
|
804
825
|
400:
|
|
805
|
-
description:
|
|
806
|
-
Wrong or expired token
|
|
807
|
-
Inactive user
|
|
826
|
+
description: TOTP already enabled
|
|
808
827
|
"""
|
|
809
828
|
try:
|
|
810
829
|
totp_provisionning_uri, totp_secret = auth_service.pre_enable_totp(
|
|
@@ -824,18 +843,29 @@ class TOTPResource(Resource, ArgsMixin):
|
|
|
824
843
|
@permissions.require_person
|
|
825
844
|
def post(self):
|
|
826
845
|
"""
|
|
827
|
-
|
|
846
|
+
Enable TOTP
|
|
828
847
|
---
|
|
829
|
-
description:
|
|
848
|
+
description: Enable TOTP (Time-based One-Time Password) authentication.
|
|
849
|
+
It requires verification code from authenticator app.
|
|
830
850
|
tags:
|
|
831
851
|
- Authentication
|
|
852
|
+
requestBody:
|
|
853
|
+
required: true
|
|
854
|
+
content:
|
|
855
|
+
application/json:
|
|
856
|
+
schema:
|
|
857
|
+
type: object
|
|
858
|
+
properties:
|
|
859
|
+
totp:
|
|
860
|
+
type: string
|
|
861
|
+
description: TOTP verification code from authenticator app
|
|
862
|
+
required:
|
|
863
|
+
- totp
|
|
832
864
|
responses:
|
|
833
865
|
200:
|
|
834
866
|
description: TOTP enabled
|
|
835
867
|
400:
|
|
836
|
-
description:
|
|
837
|
-
Wrong or expired token
|
|
838
|
-
Inactive user
|
|
868
|
+
description: TOTP already enabled or verification failed
|
|
839
869
|
"""
|
|
840
870
|
args = self.get_args([("totp", "", True)])
|
|
841
871
|
|
|
@@ -863,16 +893,36 @@ class TOTPResource(Resource, ArgsMixin):
|
|
|
863
893
|
@permissions.require_person
|
|
864
894
|
def delete(self):
|
|
865
895
|
"""
|
|
866
|
-
|
|
896
|
+
Disable TOTP
|
|
867
897
|
---
|
|
868
|
-
description:
|
|
898
|
+
description: Disable TOTP (Time-based One-Time Password) authentication.
|
|
899
|
+
It requires two-factor authentication verification.
|
|
869
900
|
tags:
|
|
870
901
|
- Authentication
|
|
902
|
+
requestBody:
|
|
903
|
+
required: true
|
|
904
|
+
content:
|
|
905
|
+
application/json:
|
|
906
|
+
schema:
|
|
907
|
+
type: object
|
|
908
|
+
properties:
|
|
909
|
+
totp:
|
|
910
|
+
type: string
|
|
911
|
+
description: TOTP verification code
|
|
912
|
+
email_otp:
|
|
913
|
+
type: string
|
|
914
|
+
description: Email OTP verification code
|
|
915
|
+
fido_authentication_response:
|
|
916
|
+
type: object
|
|
917
|
+
description: FIDO authentication response
|
|
918
|
+
recovery_code:
|
|
919
|
+
type: string
|
|
920
|
+
description: Recovery code for two-factor authentication
|
|
871
921
|
responses:
|
|
872
922
|
200:
|
|
873
923
|
description: TOTP disabled
|
|
874
924
|
400:
|
|
875
|
-
description: TOTP not enabled
|
|
925
|
+
description: TOTP not enabled or verification failed
|
|
876
926
|
"""
|
|
877
927
|
args = self.get_args(
|
|
878
928
|
[
|
|
@@ -916,25 +966,27 @@ class TOTPResource(Resource, ArgsMixin):
|
|
|
916
966
|
|
|
917
967
|
|
|
918
968
|
class EmailOTPResource(Resource, ArgsMixin):
|
|
919
|
-
"""
|
|
920
|
-
Resource to allow a user to enable/disable OTP by email or to send an OTP
|
|
921
|
-
by email.
|
|
922
|
-
"""
|
|
923
969
|
|
|
924
970
|
def get(self):
|
|
925
971
|
"""
|
|
926
|
-
|
|
972
|
+
Send email OTP
|
|
927
973
|
---
|
|
928
|
-
description:
|
|
974
|
+
description: Send a one-time password by email to the user for
|
|
975
|
+
authentication.
|
|
929
976
|
tags:
|
|
930
977
|
- Authentication
|
|
978
|
+
parameters:
|
|
979
|
+
- in: query
|
|
980
|
+
name: email
|
|
981
|
+
required: True
|
|
982
|
+
type: string
|
|
983
|
+
format: email
|
|
984
|
+
description: User email address
|
|
931
985
|
responses:
|
|
932
986
|
200:
|
|
933
987
|
description: OTP by email sent
|
|
934
988
|
400:
|
|
935
|
-
description:
|
|
936
|
-
Wrong or expired token
|
|
937
|
-
Inactive user
|
|
989
|
+
description: OTP by email not enabled
|
|
938
990
|
"""
|
|
939
991
|
args = self.get_args(
|
|
940
992
|
[
|
|
@@ -969,18 +1021,17 @@ class EmailOTPResource(Resource, ArgsMixin):
|
|
|
969
1021
|
@permissions.require_person
|
|
970
1022
|
def put(self):
|
|
971
1023
|
"""
|
|
972
|
-
|
|
1024
|
+
Pre-enable email OTP
|
|
973
1025
|
---
|
|
974
|
-
description:
|
|
1026
|
+
description: Prepare email OTP (One-Time Password) for enabling.
|
|
1027
|
+
It sets up email-based two-factor authentication.
|
|
975
1028
|
tags:
|
|
976
1029
|
- Authentication
|
|
977
1030
|
responses:
|
|
978
1031
|
200:
|
|
979
|
-
description: OTP
|
|
1032
|
+
description: Email OTP pre-enabled
|
|
980
1033
|
400:
|
|
981
|
-
description:
|
|
982
|
-
Wrong or expired token
|
|
983
|
-
Inactive user
|
|
1034
|
+
description: Email OTP already enabled
|
|
984
1035
|
"""
|
|
985
1036
|
try:
|
|
986
1037
|
auth_service.pre_enable_email_otp(
|
|
@@ -997,18 +1048,29 @@ class EmailOTPResource(Resource, ArgsMixin):
|
|
|
997
1048
|
@permissions.require_person
|
|
998
1049
|
def post(self):
|
|
999
1050
|
"""
|
|
1000
|
-
|
|
1051
|
+
Enable email OTP
|
|
1001
1052
|
---
|
|
1002
|
-
description:
|
|
1053
|
+
description: Enable email OTP (One-Time Password) authentication.
|
|
1054
|
+
It requires verification code sent to email.
|
|
1003
1055
|
tags:
|
|
1004
1056
|
- Authentication
|
|
1057
|
+
requestBody:
|
|
1058
|
+
required: true
|
|
1059
|
+
content:
|
|
1060
|
+
application/json:
|
|
1061
|
+
schema:
|
|
1062
|
+
type: object
|
|
1063
|
+
properties:
|
|
1064
|
+
email_otp:
|
|
1065
|
+
type: string
|
|
1066
|
+
description: Email OTP verification code
|
|
1067
|
+
required:
|
|
1068
|
+
- email_otp
|
|
1005
1069
|
responses:
|
|
1006
1070
|
200:
|
|
1007
|
-
description: OTP
|
|
1071
|
+
description: Email OTP enabled
|
|
1008
1072
|
400:
|
|
1009
|
-
description:
|
|
1010
|
-
Wrong or expired token
|
|
1011
|
-
Inactive user
|
|
1073
|
+
description: Email OTP already enabled or verification failed
|
|
1012
1074
|
"""
|
|
1013
1075
|
args = self.get_args([("email_otp", "", True)])
|
|
1014
1076
|
|
|
@@ -1037,19 +1099,36 @@ class EmailOTPResource(Resource, ArgsMixin):
|
|
|
1037
1099
|
@permissions.require_person
|
|
1038
1100
|
def delete(self):
|
|
1039
1101
|
"""
|
|
1040
|
-
|
|
1102
|
+
Disable email OTP
|
|
1041
1103
|
---
|
|
1042
|
-
description:
|
|
1104
|
+
description: Disable email OTP (One-Time Password) authentication.
|
|
1105
|
+
It requires two-factor authentication verification.
|
|
1043
1106
|
tags:
|
|
1044
1107
|
- Authentication
|
|
1108
|
+
requestBody:
|
|
1109
|
+
required: true
|
|
1110
|
+
content:
|
|
1111
|
+
application/json:
|
|
1112
|
+
schema:
|
|
1113
|
+
type: object
|
|
1114
|
+
properties:
|
|
1115
|
+
totp:
|
|
1116
|
+
type: string
|
|
1117
|
+
description: TOTP verification code
|
|
1118
|
+
email_otp:
|
|
1119
|
+
type: string
|
|
1120
|
+
description: Email OTP verification code
|
|
1121
|
+
fido_authentication_response:
|
|
1122
|
+
type: object
|
|
1123
|
+
description: FIDO authentication response
|
|
1124
|
+
recovery_code:
|
|
1125
|
+
type: string
|
|
1126
|
+
description: Recovery code for two-factor authentication
|
|
1045
1127
|
responses:
|
|
1046
1128
|
200:
|
|
1047
|
-
description: OTP
|
|
1129
|
+
description: Email OTP disabled
|
|
1048
1130
|
400:
|
|
1049
|
-
description:
|
|
1050
|
-
Wrong or expired token.
|
|
1051
|
-
Inactive user.
|
|
1052
|
-
Wrong 2FA.
|
|
1131
|
+
description: Email OTP not enabled or verification failed
|
|
1053
1132
|
"""
|
|
1054
1133
|
args = self.get_args(
|
|
1055
1134
|
[
|
|
@@ -1100,16 +1179,24 @@ class FIDOResource(Resource, ArgsMixin):
|
|
|
1100
1179
|
|
|
1101
1180
|
def get(self):
|
|
1102
1181
|
"""
|
|
1103
|
-
|
|
1182
|
+
Get FIDO challenge
|
|
1104
1183
|
---
|
|
1105
|
-
description:
|
|
1184
|
+
description: Get a challenge for FIDO device authentication.
|
|
1185
|
+
It is used for WebAuthn authentication flow.
|
|
1106
1186
|
tags:
|
|
1107
1187
|
- Authentication
|
|
1188
|
+
parameters:
|
|
1189
|
+
- in: query
|
|
1190
|
+
name: email
|
|
1191
|
+
required: True
|
|
1192
|
+
type: string
|
|
1193
|
+
format: email
|
|
1194
|
+
description: User email address
|
|
1108
1195
|
responses:
|
|
1109
1196
|
200:
|
|
1110
|
-
description:
|
|
1197
|
+
description: FIDO challenge generated
|
|
1111
1198
|
400:
|
|
1112
|
-
description:
|
|
1199
|
+
description: FIDO not enabled
|
|
1113
1200
|
"""
|
|
1114
1201
|
args = self.get_args(
|
|
1115
1202
|
[
|
|
@@ -1143,18 +1230,17 @@ class FIDOResource(Resource, ArgsMixin):
|
|
|
1143
1230
|
@permissions.require_person
|
|
1144
1231
|
def put(self):
|
|
1145
1232
|
"""
|
|
1146
|
-
|
|
1233
|
+
Pre-register FIDO device
|
|
1147
1234
|
---
|
|
1148
|
-
description:
|
|
1235
|
+
description: Prepare FIDO device for registration.
|
|
1236
|
+
It returns registration options for WebAuthn.
|
|
1149
1237
|
tags:
|
|
1150
1238
|
- Authentication
|
|
1151
1239
|
responses:
|
|
1152
1240
|
200:
|
|
1153
|
-
description: FIDO device pre-registered
|
|
1241
|
+
description: FIDO device pre-registered data
|
|
1154
1242
|
400:
|
|
1155
|
-
description: Invalid
|
|
1156
|
-
Wrong or expired token
|
|
1157
|
-
Inactive user
|
|
1243
|
+
description: Invalid request
|
|
1158
1244
|
"""
|
|
1159
1245
|
return auth_service.pre_register_fido(
|
|
1160
1246
|
persons_service.get_current_user()["id"]
|
|
@@ -1164,18 +1250,33 @@ class FIDOResource(Resource, ArgsMixin):
|
|
|
1164
1250
|
@jwt_required()
|
|
1165
1251
|
def post(self):
|
|
1166
1252
|
"""
|
|
1167
|
-
|
|
1253
|
+
Register FIDO device
|
|
1168
1254
|
---
|
|
1169
|
-
description:
|
|
1255
|
+
description: Register a FIDO device for WebAuthn authentication.
|
|
1256
|
+
It requires registration response from the device.
|
|
1170
1257
|
tags:
|
|
1171
1258
|
- Authentication
|
|
1259
|
+
requestBody:
|
|
1260
|
+
required: true
|
|
1261
|
+
content:
|
|
1262
|
+
application/json:
|
|
1263
|
+
schema:
|
|
1264
|
+
type: object
|
|
1265
|
+
properties:
|
|
1266
|
+
registration_response:
|
|
1267
|
+
type: object
|
|
1268
|
+
description: FIDO device registration response
|
|
1269
|
+
device_name:
|
|
1270
|
+
type: string
|
|
1271
|
+
description: Name for the FIDO device
|
|
1272
|
+
required:
|
|
1273
|
+
- registration_response
|
|
1274
|
+
- device_name
|
|
1172
1275
|
responses:
|
|
1173
1276
|
200:
|
|
1174
|
-
description: FIDO device registered
|
|
1277
|
+
description: FIDO device registered
|
|
1175
1278
|
400:
|
|
1176
|
-
description:
|
|
1177
|
-
Wrong or expired token
|
|
1178
|
-
Inactive user
|
|
1279
|
+
description: Registration failed or no preregistration
|
|
1179
1280
|
"""
|
|
1180
1281
|
try:
|
|
1181
1282
|
args = self.get_args(
|
|
@@ -1209,19 +1310,41 @@ class FIDOResource(Resource, ArgsMixin):
|
|
|
1209
1310
|
@permissions.require_person
|
|
1210
1311
|
def delete(self):
|
|
1211
1312
|
"""
|
|
1212
|
-
|
|
1313
|
+
Unregister FIDO device
|
|
1213
1314
|
---
|
|
1214
|
-
description:
|
|
1315
|
+
description: Unregister a FIDO device from WebAuthn authentication.
|
|
1316
|
+
It requires two-factor authentication verification.
|
|
1215
1317
|
tags:
|
|
1216
1318
|
- Authentication
|
|
1319
|
+
requestBody:
|
|
1320
|
+
required: true
|
|
1321
|
+
content:
|
|
1322
|
+
application/json:
|
|
1323
|
+
schema:
|
|
1324
|
+
type: object
|
|
1325
|
+
properties:
|
|
1326
|
+
totp:
|
|
1327
|
+
type: string
|
|
1328
|
+
description: TOTP verification code
|
|
1329
|
+
email_otp:
|
|
1330
|
+
type: string
|
|
1331
|
+
description: Email OTP verification code
|
|
1332
|
+
fido_authentication_response:
|
|
1333
|
+
type: object
|
|
1334
|
+
description: FIDO authentication response
|
|
1335
|
+
recovery_code:
|
|
1336
|
+
type: string
|
|
1337
|
+
description: Recovery code for two-factor authentication
|
|
1338
|
+
device_name:
|
|
1339
|
+
type: string
|
|
1340
|
+
description: Name of the FIDO device to unregister
|
|
1341
|
+
required:
|
|
1342
|
+
- device_name
|
|
1217
1343
|
responses:
|
|
1218
1344
|
200:
|
|
1219
|
-
description: FIDO device unregistered
|
|
1345
|
+
description: FIDO device unregistered
|
|
1220
1346
|
400:
|
|
1221
|
-
description:
|
|
1222
|
-
Wrong or expired token
|
|
1223
|
-
Inactive user
|
|
1224
|
-
Wrong 2FA
|
|
1347
|
+
description: FIDO not enabled or verification failed
|
|
1225
1348
|
"""
|
|
1226
1349
|
args = self.get_args(
|
|
1227
1350
|
[
|
|
@@ -1266,26 +1389,41 @@ class FIDOResource(Resource, ArgsMixin):
|
|
|
1266
1389
|
|
|
1267
1390
|
|
|
1268
1391
|
class RecoveryCodesResource(Resource, ArgsMixin):
|
|
1269
|
-
"""
|
|
1270
|
-
Resource to allow a user to generate new recovery codes.
|
|
1271
|
-
"""
|
|
1272
1392
|
|
|
1273
1393
|
@jwt_required()
|
|
1274
1394
|
@permissions.require_person
|
|
1275
1395
|
def put(self):
|
|
1276
1396
|
"""
|
|
1277
|
-
|
|
1397
|
+
Generate recovery codes
|
|
1278
1398
|
---
|
|
1279
|
-
description:
|
|
1399
|
+
description: Generate new recovery codes for two-factor authentication.
|
|
1400
|
+
It requires two-factor authentication verification.
|
|
1280
1401
|
tags:
|
|
1281
1402
|
- Authentication
|
|
1403
|
+
requestBody:
|
|
1404
|
+
required: true
|
|
1405
|
+
content:
|
|
1406
|
+
application/json:
|
|
1407
|
+
schema:
|
|
1408
|
+
type: object
|
|
1409
|
+
properties:
|
|
1410
|
+
totp:
|
|
1411
|
+
type: string
|
|
1412
|
+
description: TOTP verification code
|
|
1413
|
+
email_otp:
|
|
1414
|
+
type: string
|
|
1415
|
+
description: Email OTP verification code
|
|
1416
|
+
fido_authentication_response:
|
|
1417
|
+
type: object
|
|
1418
|
+
description: FIDO authentication response
|
|
1419
|
+
recovery_code:
|
|
1420
|
+
type: string
|
|
1421
|
+
description: Recovery code for two-factor authentication
|
|
1282
1422
|
responses:
|
|
1283
1423
|
200:
|
|
1284
|
-
description:
|
|
1424
|
+
description: New recovery codes generated
|
|
1285
1425
|
400:
|
|
1286
|
-
description:
|
|
1287
|
-
Wrong or expired token
|
|
1288
|
-
Inactive user
|
|
1426
|
+
description: No two-factor authentication enabled or verification failed
|
|
1289
1427
|
"""
|
|
1290
1428
|
args = self.get_args(
|
|
1291
1429
|
[
|
|
@@ -1334,22 +1472,20 @@ class RecoveryCodesResource(Resource, ArgsMixin):
|
|
|
1334
1472
|
|
|
1335
1473
|
|
|
1336
1474
|
class SAMLSSOResource(Resource, ArgsMixin):
|
|
1337
|
-
"""
|
|
1338
|
-
Resource to allow a user to login with SAML SSO.
|
|
1339
|
-
"""
|
|
1340
|
-
|
|
1341
1475
|
def post(self):
|
|
1342
1476
|
"""
|
|
1343
|
-
|
|
1477
|
+
SAML SSO login
|
|
1344
1478
|
---
|
|
1345
|
-
description:
|
|
1479
|
+
description: Handle SAML SSO login response. Processes authentication
|
|
1480
|
+
response from SAML identity provider and creates a new user if they
|
|
1481
|
+
don't exist.
|
|
1346
1482
|
tags:
|
|
1347
1483
|
- Authentication
|
|
1348
1484
|
responses:
|
|
1349
1485
|
302:
|
|
1350
|
-
description: Login successful, redirect to
|
|
1486
|
+
description: Login successful, redirect to home page
|
|
1351
1487
|
400:
|
|
1352
|
-
description:
|
|
1488
|
+
description: SAML not enabled or wrong parameter
|
|
1353
1489
|
"""
|
|
1354
1490
|
if not config.SAML_ENABLED:
|
|
1355
1491
|
return {"error": "SAML is not enabled."}, 400
|
|
@@ -1425,22 +1561,20 @@ class SAMLSSOResource(Resource, ArgsMixin):
|
|
|
1425
1561
|
|
|
1426
1562
|
|
|
1427
1563
|
class SAMLLoginResource(Resource, ArgsMixin):
|
|
1428
|
-
"""
|
|
1429
|
-
Resource to allow a user to login with SAML SSO.
|
|
1430
|
-
"""
|
|
1431
1564
|
|
|
1432
1565
|
def get(self):
|
|
1433
1566
|
"""
|
|
1434
|
-
|
|
1567
|
+
SAML SSO login redirect
|
|
1435
1568
|
---
|
|
1436
|
-
description:
|
|
1569
|
+
description: Initiate SAML SSO login by redirecting to SAML identity
|
|
1570
|
+
provider.
|
|
1437
1571
|
tags:
|
|
1438
1572
|
- Authentication
|
|
1439
1573
|
responses:
|
|
1440
1574
|
302:
|
|
1441
|
-
description: Redirect to
|
|
1575
|
+
description: Redirect to SAML identity provider
|
|
1442
1576
|
400:
|
|
1443
|
-
description:
|
|
1577
|
+
description: SAML not enabled or wrong parameter
|
|
1444
1578
|
"""
|
|
1445
1579
|
if not config.SAML_ENABLED:
|
|
1446
1580
|
return {"error": "SAML is not enabled."}, 400
|