From aa14568947e77dcba9913087ea22629d915ec2db Mon Sep 17 00:00:00 2001 From: fuwx Date: Mon, 17 Mar 2025 15:20:06 +0800 Subject: [PATCH] add anonymous user --- api/controllers/console/auth/login.py | 64 +++++++++++++++++++++++++++ api/services/account_service.py | 19 ++++++++ 2 files changed, 83 insertions(+) diff --git a/api/controllers/console/auth/login.py b/api/controllers/console/auth/login.py index f5fd5cd64a..48a7def8ce 100644 --- a/api/controllers/console/auth/login.py +++ b/api/controllers/console/auth/login.py @@ -157,6 +157,69 @@ class APOLoginApi(Resource): token_pair = AccountService.login(account=account, ip_address=extract_remote_ip(request)) # AccountService.reset_login_error_rate_limit(args["email"]) return {"result": "success", "data": token_pair.model_dump()} + +class APOAnonymousApi(Resource): + """Resource for apo anonymous login.""" + + @setup_required + def post(self): + """Authenticate user and login.""" + parser = reqparse.RequestParser() + parser.add_argument("email", type=email, required=True, location="json") + # 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") + parser.add_argument("language", type=str, required=False, default="en-US", location="json") + args = parser.parse_args() + + if dify_config.BILLING_ENABLED and BillingService.is_email_in_freeze(args["email"]): + raise AccountInFreezeError() + + # is_login_error_rate_limit = AccountService.is_login_error_rate_limit(args["email"]) + # if is_login_error_rate_limit: + # raise EmailPasswordLoginLimitError() + + invitation = args["invite_token"] + if invitation: + invitation = RegisterService.get_invitation_if_token_valid(None, args["email"], invitation) + + if args["language"] is not None and args["language"] == "zh-Hans": + language = "zh-Hans" + else: + language = "en-US" + + 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.anonymous(args["email"]) + # account = AccountService.authenticate(args["email"], args["password"], args["invite_token"]) + else: + account = AccountService.anonymous(args["email"]) + except services.errors.account.AccountLoginError: + raise AccountBannedError() + except services.errors.account.AccountPasswordError: + # AccountService.add_login_error_rate_limit(args["email"]) + raise EmailOrPasswordMismatchError() + except services.errors.account.AccountNotFoundError: + 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: + raise AccountNotFound() + # 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_pair = AccountService.login(account=account, ip_address=extract_remote_ip(request)) + # AccountService.reset_login_error_rate_limit(args["email"]) + return {"result": "success", "data": token_pair.model_dump()} class LogoutApi(Resource): @setup_required @@ -294,6 +357,7 @@ class RefreshTokenApi(Resource): api.add_resource(LoginApi, "/login") api.add_resource(APOLoginApi, "/login/apo") +api.add_resource(APOAnonymousApi, "/login/apo/anonymous") api.add_resource(LogoutApi, "/logout") api.add_resource(EmailCodeLoginSendEmailApi, "/email-code-login") api.add_resource(EmailCodeLoginApi, "/email-code-login/validity") diff --git a/api/services/account_service.py b/api/services/account_service.py index 664719c060..6554fa9869 100644 --- a/api/services/account_service.py +++ b/api/services/account_service.py @@ -173,6 +173,25 @@ class AccountService: db.session.commit() return cast(Account, account) + + @staticmethod + def anonymous(email: str) -> Account: + """authenticate account with email and password""" + + account = db.session.query(Account).filter_by(email=email).first() + if not account: + raise AccountNotFoundError() + + if account.status == AccountStatus.BANNED.value: + raise AccountLoginError("Account is banned.") + + if account.status == AccountStatus.PENDING.value: + account.status = AccountStatus.ACTIVE.value + account.initialized_at = datetime.now(UTC).replace(tzinfo=None) + + db.session.commit() + + return cast(Account, account) @staticmethod def update_account_password(account, password, new_password):