| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  | import base64 | 
					
						
							|  |  |  | import secrets | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-21 10:03:40 +08:00
										 |  |  | from flask import request | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  | from flask_restful import Resource, reqparse | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-21 10:03:40 +08:00
										 |  |  | from constants.languages import languages | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  | from controllers.console import api | 
					
						
							|  |  |  | from controllers.console.auth.error import ( | 
					
						
							| 
									
										
										
										
											2024-10-21 10:03:40 +08:00
										 |  |  |     EmailCodeError, | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  |     InvalidEmailError, | 
					
						
							|  |  |  |     InvalidTokenError, | 
					
						
							|  |  |  |     PasswordMismatchError, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2024-11-18 16:14:39 +08:00
										 |  |  | from controllers.console.error import AccountNotFound, EmailSendIpLimitError | 
					
						
							| 
									
										
										
										
											2024-11-01 15:51:22 +08:00
										 |  |  | from controllers.console.wraps import setup_required | 
					
						
							| 
									
										
										
										
											2024-10-21 10:03:40 +08:00
										 |  |  | from events.tenant_event import tenant_was_created | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  | from extensions.ext_database import db | 
					
						
							| 
									
										
										
										
											2024-10-21 10:03:40 +08:00
										 |  |  | from libs.helper import email, extract_remote_ip | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  | from libs.password import hash_password, valid_password | 
					
						
							|  |  |  | from models.account import Account | 
					
						
							| 
									
										
										
										
											2024-10-21 10:03:40 +08:00
										 |  |  | from services.account_service import AccountService, TenantService | 
					
						
							|  |  |  | from services.errors.workspace import WorkSpaceNotAllowedCreateError | 
					
						
							|  |  |  | from services.feature_service import FeatureService | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ForgotPasswordSendEmailApi(Resource): | 
					
						
							|  |  |  |     @setup_required | 
					
						
							|  |  |  |     def post(self): | 
					
						
							|  |  |  |         parser = reqparse.RequestParser() | 
					
						
							| 
									
										
										
										
											2024-10-21 10:03:40 +08:00
										 |  |  |         parser.add_argument("email", type=email, required=True, location="json") | 
					
						
							|  |  |  |         parser.add_argument("language", type=str, required=False, location="json") | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  |         args = parser.parse_args() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-21 10:03:40 +08:00
										 |  |  |         ip_address = extract_remote_ip(request) | 
					
						
							|  |  |  |         if AccountService.is_email_send_ip_limit(ip_address): | 
					
						
							|  |  |  |             raise EmailSendIpLimitError() | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-21 10:03:40 +08:00
										 |  |  |         if args["language"] is not None and args["language"] == "zh-Hans": | 
					
						
							|  |  |  |             language = "zh-Hans" | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             language = "en-US" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         account = Account.query.filter_by(email=args["email"]).first() | 
					
						
							|  |  |  |         token = None | 
					
						
							|  |  |  |         if account is None: | 
					
						
							|  |  |  |             if FeatureService.get_system_features().is_allow_register: | 
					
						
							|  |  |  |                 token = AccountService.send_reset_password_email(email=args["email"], language=language) | 
					
						
							|  |  |  |                 return {"result": "fail", "data": token, "code": "account_not_found"} | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2024-11-18 16:14:39 +08:00
										 |  |  |                 raise AccountNotFound() | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2024-10-21 10:03:40 +08:00
										 |  |  |             token = AccountService.send_reset_password_email(account=account, email=args["email"], language=language) | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-21 10:03:40 +08:00
										 |  |  |         return {"result": "success", "data": token} | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ForgotPasswordCheckApi(Resource): | 
					
						
							|  |  |  |     @setup_required | 
					
						
							|  |  |  |     def post(self): | 
					
						
							|  |  |  |         parser = reqparse.RequestParser() | 
					
						
							| 
									
										
										
										
											2024-10-21 10:03:40 +08:00
										 |  |  |         parser.add_argument("email", type=str, required=True, location="json") | 
					
						
							|  |  |  |         parser.add_argument("code", type=str, required=True, location="json") | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |         parser.add_argument("token", type=str, required=True, nullable=False, location="json") | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  |         args = parser.parse_args() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-21 10:03:40 +08:00
										 |  |  |         user_email = args["email"] | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-21 10:03:40 +08:00
										 |  |  |         token_data = AccountService.get_reset_password_data(args["token"]) | 
					
						
							|  |  |  |         if token_data is None: | 
					
						
							|  |  |  |             raise InvalidTokenError() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if user_email != token_data.get("email"): | 
					
						
							|  |  |  |             raise InvalidEmailError() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if args["code"] != token_data.get("code"): | 
					
						
							|  |  |  |             raise EmailCodeError() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return {"is_valid": True, "email": token_data.get("email")} | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ForgotPasswordResetApi(Resource): | 
					
						
							|  |  |  |     @setup_required | 
					
						
							|  |  |  |     def post(self): | 
					
						
							|  |  |  |         parser = reqparse.RequestParser() | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |         parser.add_argument("token", type=str, required=True, nullable=False, location="json") | 
					
						
							|  |  |  |         parser.add_argument("new_password", type=valid_password, required=True, nullable=False, location="json") | 
					
						
							|  |  |  |         parser.add_argument("password_confirm", type=valid_password, required=True, nullable=False, location="json") | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  |         args = parser.parse_args() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |         new_password = args["new_password"] | 
					
						
							|  |  |  |         password_confirm = args["password_confirm"] | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if str(new_password).strip() != str(password_confirm).strip(): | 
					
						
							|  |  |  |             raise PasswordMismatchError() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |         token = args["token"] | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  |         reset_data = AccountService.get_reset_password_data(token) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if reset_data is None: | 
					
						
							|  |  |  |             raise InvalidTokenError() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         AccountService.revoke_reset_password_token(token) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         salt = secrets.token_bytes(16) | 
					
						
							|  |  |  |         base64_salt = base64.b64encode(salt).decode() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         password_hashed = hash_password(new_password, salt) | 
					
						
							|  |  |  |         base64_password_hashed = base64.b64encode(password_hashed).decode() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |         account = Account.query.filter_by(email=reset_data.get("email")).first() | 
					
						
							| 
									
										
										
										
											2024-10-21 10:03:40 +08:00
										 |  |  |         if account: | 
					
						
							|  |  |  |             account.password = base64_password_hashed | 
					
						
							|  |  |  |             account.password_salt = base64_salt | 
					
						
							|  |  |  |             db.session.commit() | 
					
						
							|  |  |  |             tenant = TenantService.get_join_tenants(account) | 
					
						
							|  |  |  |             if not tenant and not FeatureService.get_system_features().is_allow_create_workspace: | 
					
						
							|  |  |  |                 tenant = TenantService.create_tenant(f"{account.name}'s Workspace") | 
					
						
							|  |  |  |                 TenantService.create_tenant_member(tenant, account, role="owner") | 
					
						
							|  |  |  |                 account.current_tenant = tenant | 
					
						
							|  |  |  |                 tenant_was_created.send(tenant) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 account = AccountService.create_account_and_tenant( | 
					
						
							|  |  |  |                     email=reset_data.get("email"), | 
					
						
							|  |  |  |                     name=reset_data.get("email"), | 
					
						
							|  |  |  |                     password=password_confirm, | 
					
						
							|  |  |  |                     interface_language=languages[0], | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             except WorkSpaceNotAllowedCreateError: | 
					
						
							|  |  |  |                 pass | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  |         return {"result": "success"} | 
					
						
							| 
									
										
										
										
											2024-07-05 13:38:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-26 15:29:10 +08:00
										 |  |  | api.add_resource(ForgotPasswordSendEmailApi, "/forgot-password") | 
					
						
							|  |  |  | api.add_resource(ForgotPasswordCheckApi, "/forgot-password/validity") | 
					
						
							|  |  |  | api.add_resource(ForgotPasswordResetApi, "/forgot-password/resets") |