|
|
|
|
@ -1,5 +1,6 @@
|
|
|
|
|
# -*- coding:utf-8 -*-
|
|
|
|
|
import base64
|
|
|
|
|
import json
|
|
|
|
|
import logging
|
|
|
|
|
import secrets
|
|
|
|
|
import uuid
|
|
|
|
|
@ -346,6 +347,10 @@ class TenantService:
|
|
|
|
|
|
|
|
|
|
class RegisterService:
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def _get_invitation_token_key(cls, token: str) -> str:
|
|
|
|
|
return f'member_invite:token:{token}'
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def register(cls, email, name, password: str = None, open_id: str = None, provider: str = None) -> Account:
|
|
|
|
|
db.session.begin_nested()
|
|
|
|
|
@ -401,7 +406,7 @@ class RegisterService:
|
|
|
|
|
# send email
|
|
|
|
|
send_invite_member_mail_task.delay(
|
|
|
|
|
to=email,
|
|
|
|
|
token=cls.generate_invite_token(tenant, account),
|
|
|
|
|
token=token,
|
|
|
|
|
inviter_name=inviter.name if inviter else 'Dify',
|
|
|
|
|
workspace_id=tenant.id,
|
|
|
|
|
workspace_name=tenant.name,
|
|
|
|
|
@ -412,21 +417,35 @@ class RegisterService:
|
|
|
|
|
@classmethod
|
|
|
|
|
def generate_invite_token(cls, tenant: Tenant, account: Account) -> str:
|
|
|
|
|
token = str(uuid.uuid4())
|
|
|
|
|
email_hash = sha256(account.email.encode()).hexdigest()
|
|
|
|
|
cache_key = 'member_invite_token:{}, {}:{}'.format(str(tenant.id), email_hash, token)
|
|
|
|
|
redis_client.setex(cache_key, 3600, str(account.id))
|
|
|
|
|
invitation_data = {
|
|
|
|
|
'account_id': account.id,
|
|
|
|
|
'email': account.email,
|
|
|
|
|
'workspace_id': tenant.id,
|
|
|
|
|
}
|
|
|
|
|
redis_client.setex(
|
|
|
|
|
cls._get_invitation_token_key(token),
|
|
|
|
|
3600,
|
|
|
|
|
json.dumps(invitation_data)
|
|
|
|
|
)
|
|
|
|
|
return token
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def revoke_token(cls, workspace_id: str, email: str, token: str):
|
|
|
|
|
email_hash = sha256(email.encode()).hexdigest()
|
|
|
|
|
cache_key = 'member_invite_token:{}, {}:{}'.format(workspace_id, email_hash, token)
|
|
|
|
|
redis_client.delete(cache_key)
|
|
|
|
|
if workspace_id and email:
|
|
|
|
|
email_hash = sha256(email.encode()).hexdigest()
|
|
|
|
|
cache_key = 'member_invite_token:{}, {}:{}'.format(workspace_id, email_hash, token)
|
|
|
|
|
redis_client.delete(cache_key)
|
|
|
|
|
else:
|
|
|
|
|
redis_client.delete(cls._get_invitation_token_key(token))
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def get_account_if_token_valid(cls, workspace_id: str, email: str, token: str) -> Optional[Account]:
|
|
|
|
|
def get_invitation_if_token_valid(cls, workspace_id: str, email: str, token: str) -> Optional[Account]:
|
|
|
|
|
invitation_data = cls._get_invitation_by_token(token, workspace_id, email)
|
|
|
|
|
if not invitation_data:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
tenant = db.session.query(Tenant).filter(
|
|
|
|
|
Tenant.id == workspace_id,
|
|
|
|
|
Tenant.id == invitation_data['workspace_id'],
|
|
|
|
|
Tenant.status == 'normal'
|
|
|
|
|
).first()
|
|
|
|
|
|
|
|
|
|
@ -435,30 +454,43 @@ class RegisterService:
|
|
|
|
|
|
|
|
|
|
tenant_account = db.session.query(Account, TenantAccountJoin.role).join(
|
|
|
|
|
TenantAccountJoin, Account.id == TenantAccountJoin.account_id
|
|
|
|
|
).filter(Account.email == email, TenantAccountJoin.tenant_id == tenant.id).first()
|
|
|
|
|
).filter(Account.email == invitation_data['email'], TenantAccountJoin.tenant_id == tenant.id).first()
|
|
|
|
|
|
|
|
|
|
if not tenant_account:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
account_id = cls._get_account_id_by_invite_token(workspace_id, email, token)
|
|
|
|
|
if not account_id:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
account = tenant_account[0]
|
|
|
|
|
if not account:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
if account_id != str(account.id):
|
|
|
|
|
if invitation_data['account_id'] != str(account.id):
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
return account
|
|
|
|
|
return {
|
|
|
|
|
'account': account,
|
|
|
|
|
'data': invitation_data,
|
|
|
|
|
'tenant': tenant,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def _get_account_id_by_invite_token(cls, workspace_id: str, email: str, token: str) -> Optional[str]:
|
|
|
|
|
email_hash = sha256(email.encode()).hexdigest()
|
|
|
|
|
cache_key = 'member_invite_token:{}, {}:{}'.format(workspace_id, email_hash, token)
|
|
|
|
|
account_id = redis_client.get(cache_key)
|
|
|
|
|
if not account_id:
|
|
|
|
|
return None
|
|
|
|
|
def _get_invitation_by_token(cls, token: str, workspace_id: str, email: str) -> Optional[str]:
|
|
|
|
|
if workspace_id is not None and email is not None:
|
|
|
|
|
email_hash = sha256(email.encode()).hexdigest()
|
|
|
|
|
cache_key = f'member_invite_token:{workspace_id}, {email_hash}:{token}'
|
|
|
|
|
account_id = redis_client.get(cache_key)
|
|
|
|
|
|
|
|
|
|
if not account_id:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
'account_id': account_id.decode('utf-8'),
|
|
|
|
|
'email': email,
|
|
|
|
|
'workspace_id': workspace_id,
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
data = redis_client.get(cls._get_invitation_token_key(token))
|
|
|
|
|
if not data:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
return account_id.decode('utf-8')
|
|
|
|
|
invitation = json.loads(data)
|
|
|
|
|
return invitation
|
|
|
|
|
|