diff --git a/api/controllers/console/auth/activate.py b/api/controllers/console/auth/activate.py index eedb2854345..65e278edb54 100644 --- a/api/controllers/console/auth/activate.py +++ b/api/controllers/console/auth/activate.py @@ -2,15 +2,17 @@ from flask import request from flask_restx import Resource from pydantic import BaseModel, Field, field_validator +from configs import dify_config from constants.languages import supported_language from controllers.common.schema import register_schema_models from controllers.console import console_ns -from controllers.console.error import AlreadyActivateError +from controllers.console.error import AccountInFreezeError, AlreadyActivateError from extensions.ext_database import db from libs.datetime_utils import naive_utc_now from libs.helper import EmailStr, timezone from models import AccountStatus from services.account_service import RegisterService +from services.billing_service import BillingService class ActivateCheckQuery(BaseModel): @@ -120,9 +122,12 @@ class ActivateApi(Resource): if invitation is None: raise AlreadyActivateError() + account = invitation["account"] + if dify_config.BILLING_ENABLED and BillingService.is_email_in_freeze(account.email): + raise AccountInFreezeError() + RegisterService.revoke_token(args.workspace_id, normalized_request_email, args.token) - account = invitation["account"] account.name = args.name account.interface_language = args.interface_language diff --git a/api/tests/unit_tests/controllers/console/auth/test_account_activation.py b/api/tests/unit_tests/controllers/console/auth/test_account_activation.py index 0fb0ebc330a..79169cfce7e 100644 --- a/api/tests/unit_tests/controllers/console/auth/test_account_activation.py +++ b/api/tests/unit_tests/controllers/console/auth/test_account_activation.py @@ -14,7 +14,7 @@ import pytest from flask import Flask from controllers.console.auth.activate import ActivateApi, ActivateCheckApi -from controllers.console.error import AlreadyActivateError +from controllers.console.error import AccountInFreezeError, AlreadyActivateError from models.account import AccountStatus @@ -255,6 +255,47 @@ class TestActivateApi: with pytest.raises(AlreadyActivateError): api.post() + @patch("controllers.console.auth.activate.dify_config.BILLING_ENABLED", True) + @patch("controllers.console.auth.activate.BillingService.is_email_in_freeze") + @patch("controllers.console.auth.activate.RegisterService.get_invitation_if_token_valid") + @patch("controllers.console.auth.activate.RegisterService.revoke_token") + @patch("controllers.console.auth.activate.db") + def test_activation_rejects_account_in_billing_freeze( + self, + mock_db, + mock_revoke_token, + mock_get_invitation, + mock_is_email_in_freeze, + app: Flask, + mock_invitation, + mock_account, + ): + """Frozen deleted-account emails cannot be reactivated through invitation links.""" + mock_account.email = "Invitee@Example.com" + mock_get_invitation.return_value = mock_invitation + mock_is_email_in_freeze.return_value = True + + with app.test_request_context( + "/activate", + method="POST", + json={ + "workspace_id": "workspace-123", + "email": "invitee@example.com", + "token": "valid_token", + "name": "John Doe", + "interface_language": "en-US", + "timezone": "UTC", + }, + ): + api = ActivateApi() + with pytest.raises(AccountInFreezeError): + api.post() + + mock_is_email_in_freeze.assert_called_once_with("Invitee@Example.com") + mock_revoke_token.assert_not_called() + mock_db.session.commit.assert_not_called() + assert mock_account.status == AccountStatus.PENDING + @patch("controllers.console.auth.activate.RegisterService.get_invitation_with_case_fallback") @patch("controllers.console.auth.activate.RegisterService.revoke_token") @patch("controllers.console.auth.activate.db")