dify/api/controllers/console/auth/login.py

196 lines
7.9 KiB
Python
Raw Normal View History

from typing import cast
2023-05-15 08:51:32 +08:00
import flask_login
2024-09-10 11:36:20 +08:00
from flask import redirect, request
from flask_restful import Resource, reqparse
2023-05-15 08:51:32 +08:00
import services
2024-08-29 14:13:28 +08:00
from configs import dify_config
from constants.languages import languages
2023-05-15 08:51:32 +08:00
from controllers.console import api
2024-08-29 14:13:28 +08:00
from controllers.console.auth.error import (
2024-08-29 16:51:30 +08:00
EmailCodeError,
EmailOrPasswordMismatchError,
2024-09-23 11:20:35 +08:00
EmailPasswordLoginLimitError,
2024-08-29 14:13:28 +08:00
InvalidEmailError,
InvalidTokenError,
)
2024-09-10 11:51:40 +08:00
from controllers.console.error import NotAllowedCreateWorkspace, NotAllowedRegister
2023-05-15 08:51:32 +08:00
from controllers.console.setup import setup_required
2024-09-10 11:36:20 +08:00
from events.tenant_event import tenant_was_created
from libs.helper import email, get_remote_ip
2024-09-27 10:52:01 +08:00
from libs.password import valid_password
from models.account import Account
from services.account_service import AccountService, RegisterService, TenantService
2024-09-10 11:36:20 +08:00
from services.errors.workspace import WorkSpaceNotAllowedCreateError
2023-05-15 08:51:32 +08:00
class LoginApi(Resource):
"""Resource for user login."""
@setup_required
def post(self):
"""Authenticate user and login."""
parser = reqparse.RequestParser()
parser.add_argument("email", type=email, required=True, location="json")
2024-09-27 11:56:37 +08:00
parser.add_argument("password", type=valid_password, required=True, location="json")
parser.add_argument("remember_me", type=bool, required=False, default=False, location="json")
parser.add_argument("invite_token", type=str, required=False, default=None, location="json")
2023-05-15 08:51:32 +08:00
args = parser.parse_args()
2024-09-23 10:45:30 +08:00
is_login_error_rate_limit = AccountService.is_login_error_rate_limit(args["email"])
if is_login_error_rate_limit:
2024-09-23 11:20:35 +08:00
raise EmailPasswordLoginLimitError()
2024-09-23 10:45:30 +08:00
invitation = args["invite_token"]
if invitation:
invitation = RegisterService.get_invitation_if_token_valid(None, args["email"], invitation)
2023-05-15 08:51:32 +08:00
try:
if invitation:
data = invitation.get("data", {})
invitee_email = data.get("email") if data else None
if invitee_email != args["email"]:
raise InvalidEmailError()
account = AccountService.authenticate(args["email"], args["password"], args["invite_token"])
else:
account = AccountService.authenticate(args["email"], args["password"])
except services.errors.account.AccountLoginError:
raise NotAllowedRegister()
except services.errors.account.AccountPasswordError:
2024-09-23 10:45:30 +08:00
AccountService.add_login_error_rate_limit(args["email"])
raise EmailOrPasswordMismatchError()
except services.errors.account.AccountNotFoundError:
2024-09-02 11:08:36 +08:00
if not dify_config.ALLOW_REGISTER:
raise NotAllowedRegister()
2023-05-15 08:51:32 +08:00
2024-09-02 11:08:36 +08:00
token = AccountService.send_reset_password_email(email=args["email"])
return {"result": "fail", "data": token, "message": "account_not_found"}
2024-04-18 17:33:32 +08:00
# SELF_HOSTED only have one workspace
tenants = TenantService.get_join_tenants(account)
if len(tenants) == 0:
return {
"result": "fail",
"data": "workspace not found, please contact system admin to invite you to join in a workspace",
}
token = AccountService.login(account, ip_address=get_remote_ip(request))
2024-09-23 10:45:30 +08:00
AccountService.reset_login_error_rate_limit(args["email"])
return {"result": "success", "data": token}
2023-05-15 08:51:32 +08:00
class LogoutApi(Resource):
@setup_required
def get(self):
account = cast(Account, flask_login.current_user)
token = request.headers.get("Authorization", "").split(" ")[1]
AccountService.logout(account=account, token=token)
2023-05-15 08:51:32 +08:00
flask_login.logout_user()
return {"result": "success"}
2023-05-15 08:51:32 +08:00
2024-08-29 15:15:48 +08:00
class ResetPasswordSendEmailApi(Resource):
@setup_required
def post(self):
parser = reqparse.RequestParser()
parser.add_argument("email", type=email, required=True, location="json")
2024-09-26 14:30:06 +08:00
parser.add_argument("language", type=str, required=False, location="json")
2024-08-29 15:15:48 +08:00
args = parser.parse_args()
2024-09-26 16:15:23 +08:00
if args["language"] is not None and args["language"] == "zh-Hans":
language = "zh-Hans"
else:
language = "en-US"
2024-08-29 15:15:48 +08:00
account = AccountService.get_user_through_email(args["email"])
if account is None:
if dify_config.ALLOW_REGISTER:
2024-09-26 16:15:23 +08:00
token = AccountService.send_reset_password_email(email=args["email"], language=language)
2024-08-29 15:15:48 +08:00
else:
raise NotAllowedRegister()
2024-08-29 15:15:48 +08:00
else:
2024-09-26 16:15:23 +08:00
token = AccountService.send_reset_password_email(account=account, language=language)
2024-08-29 15:15:48 +08:00
return {"result": "success", "data": token}
2024-08-29 14:13:28 +08:00
class EmailCodeLoginSendEmailApi(Resource):
@setup_required
def post(self):
parser = reqparse.RequestParser()
2024-08-29 16:51:30 +08:00
parser.add_argument("email", type=email, required=True, location="json")
2024-09-26 14:30:06 +08:00
parser.add_argument("language", type=str, required=False, location="json")
2024-08-29 14:13:28 +08:00
args = parser.parse_args()
2024-09-26 16:15:23 +08:00
if args["language"] is not None and args["language"] == "zh-Hans":
language = "zh-Hans"
else:
language = "en-US"
2024-08-29 14:13:28 +08:00
account = AccountService.get_user_through_email(args["email"])
if account is None:
if dify_config.ALLOW_REGISTER:
2024-09-26 16:15:23 +08:00
token = AccountService.send_email_code_login_email(email=args["email"], language=language)
else:
raise NotAllowedRegister()
2024-08-29 15:15:48 +08:00
else:
2024-09-26 16:15:23 +08:00
token = AccountService.send_email_code_login_email(account=account, language=language)
2024-08-29 14:13:28 +08:00
return {"result": "success", "data": token}
class EmailCodeLoginApi(Resource):
@setup_required
def post(self):
parser = reqparse.RequestParser()
parser.add_argument("email", type=str, required=True, location="json")
parser.add_argument("code", type=str, required=True, location="json")
parser.add_argument("token", type=str, required=True, location="json")
args = parser.parse_args()
user_email = args["email"]
token_data = AccountService.get_email_code_login_data(args["token"])
if token_data is None:
raise InvalidTokenError()
if token_data["email"] != args["email"]:
raise InvalidEmailError()
if token_data["code"] != args["code"]:
2024-08-29 16:51:30 +08:00
raise EmailCodeError()
2024-08-29 14:13:28 +08:00
AccountService.revoke_email_code_login_token(args["token"])
account = AccountService.get_user_through_email(user_email)
2024-09-12 10:40:44 +08:00
if account:
tenant = TenantService.get_join_tenants(account)
if not tenant:
if not dify_config.ALLOW_CREATE_WORKSPACE:
raise NotAllowedCreateWorkspace()
else:
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)
2024-08-29 14:13:28 +08:00
2024-09-10 11:36:20 +08:00
if account is None:
try:
account = AccountService.create_account_and_tenant(
email=user_email, name=user_email, interface_language=languages[0]
)
except WorkSpaceNotAllowedCreateError:
return redirect(
f"{dify_config.CONSOLE_WEB_URL}/signin"
"?message=Workspace not found, please contact system admin to invite you to join in a workspace."
2024-09-10 11:36:20 +08:00
)
2024-08-29 15:15:48 +08:00
token = AccountService.login(account, ip_address=get_remote_ip(request))
2024-09-23 10:45:30 +08:00
AccountService.reset_login_error_rate_limit(args["email"])
2024-08-29 15:15:48 +08:00
return {"result": "success", "data": token}
2024-08-29 14:13:28 +08:00
api.add_resource(LoginApi, "/login")
api.add_resource(LogoutApi, "/logout")
2024-08-29 14:13:28 +08:00
api.add_resource(EmailCodeLoginSendEmailApi, "/email-code-login")
api.add_resource(EmailCodeLoginApi, "/email-code-login/validity")
2024-08-29 16:51:30 +08:00
api.add_resource(ResetPasswordSendEmailApi, "/reset-password")