Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/move tenant id into db #2341

Merged
merged 7 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions api/controllers/console/auth/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from flask_restful import Resource, reqparse
from libs.helper import email
from libs.password import valid_password
from services.account_service import AccountService, TenantService
from services.account_service import AccountService


class LoginApi(Resource):
Expand All @@ -30,11 +30,6 @@ def post(self):
except services.errors.account.AccountLoginError:
return {'code': 'unauthorized', 'message': 'Invalid email or password'}, 401

try:
crazywoola marked this conversation as resolved.
Show resolved Hide resolved
TenantService.switch_tenant(account)
except Exception:
pass

AccountService.update_last_login(account, request)

# todo: return the user info
Expand All @@ -47,7 +42,6 @@ class LogoutApi(Resource):

@setup_required
def get(self):
flask.session.pop('workspace_id', None)
flask_login.logout_user()
return {'result': 'success'}

Expand Down
32 changes: 32 additions & 0 deletions api/migrations/versions/16830a790f0f_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""empty message

Revision ID: 16830a790f0f
Revises: 380c6aa5a70d
Create Date: 2024-02-01 08:21:31.111119

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '16830a790f0f'
down_revision = '380c6aa5a70d'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('tenant_account_joins', schema=None) as batch_op:
crazywoola marked this conversation as resolved.
Show resolved Hide resolved
batch_op.add_column(sa.Column('current', sa.Boolean(), server_default=sa.text('false'), nullable=False))

# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('tenant_account_joins', schema=None) as batch_op:
batch_op.drop_column('current')

# ### end Alembic commands ###
2 changes: 1 addition & 1 deletion api/models/account.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import enum
import json
from math import e
from typing import List

from extensions.ext_database import db
Expand Down Expand Up @@ -155,6 +154,7 @@ class TenantAccountJoin(db.Model):
id = db.Column(UUID, server_default=db.text('uuid_generate_v4()'))
tenant_id = db.Column(UUID, nullable=False)
account_id = db.Column(UUID, nullable=False)
current = db.Column(db.Boolean, nullable=False, server_default=db.text('false'))
role = db.Column(db.String(16), nullable=False, server_default='normal')
invited_by = db.Column(UUID, nullable=True)
created_at = db.Column(db.DateTime, nullable=False, server_default=db.text('CURRENT_TIMESTAMP(0)'))
Expand Down
82 changes: 31 additions & 51 deletions api/services/account_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from constants.languages import languages, language_timezone_mapping
from events.tenant_event import tenant_was_created
from extensions.ext_redis import redis_client
from flask import current_app, session
from flask import current_app
from libs.helper import get_remote_ip
from libs.passport import PassportService
from libs.password import compare_password, hash_password
Expand All @@ -23,7 +23,7 @@
NoPermissionError, RoleAlreadyAssignedError, TenantNotFound)
from sqlalchemy import func
from tasks.mail_invite_member_task import send_invite_member_mail_task
from werkzeug.exceptions import Forbidden, Unauthorized
from werkzeug.exceptions import Forbidden


def _create_tenant_for_account(account) -> Tenant:
Expand All @@ -39,54 +39,33 @@ class AccountService:

@staticmethod
def load_user(user_id: str) -> Account:
# todo: used by flask_login
if '.' in user_id:
tenant_id, account_id = user_id.split('.')
else:
account_id = user_id

account = db.session.query(Account).filter(Account.id == account_id).first()

if account:
if account.status == AccountStatus.BANNED.value or account.status == AccountStatus.CLOSED.value:
raise Forbidden('Account is banned or closed.')

workspace_id = session.get('workspace_id')
if workspace_id:
tenant_account_join = db.session.query(TenantAccountJoin).filter(
TenantAccountJoin.account_id == account.id,
TenantAccountJoin.tenant_id == workspace_id
).first()

if not tenant_account_join:
tenant_account_join = db.session.query(TenantAccountJoin).filter(
TenantAccountJoin.account_id == account.id).first()

if tenant_account_join:
account.current_tenant_id = tenant_account_join.tenant_id
else:
_create_tenant_for_account(account)
session['workspace_id'] = account.current_tenant_id
else:
account.current_tenant_id = workspace_id
else:
tenant_account_join = db.session.query(TenantAccountJoin).filter(
TenantAccountJoin.account_id == account.id).first()
if tenant_account_join:
account.current_tenant_id = tenant_account_join.tenant_id
else:
_create_tenant_for_account(account)
session['workspace_id'] = account.current_tenant_id

current_time = datetime.utcnow()
account = Account.query.filter_by(id=user_id).first()
if not account:
return None

# update last_active_at when last_active_at is more than 10 minutes ago
if current_time - account.last_active_at > timedelta(minutes=10):
account.last_active_at = current_time
db.session.commit()
if account.status in [AccountStatus.BANNED.value, AccountStatus.CLOSED.value]:
raise Forbidden('Account is banned or closed.')

# init owner's tenant
tenant_owner = TenantAccountJoin.query.filter_by(account_id=account.id, role='owner').first()
if not tenant_owner:
_create_tenant_for_account(account)

current_tenant = TenantAccountJoin.query.filter_by(account_id=account.id, current=True).first()
if current_tenant:
account.current_tenant_id = current_tenant.tenant_id
else:
account.current_tenant_id = tenant_owner.tenant_id
tenant_owner.current = True
db.session.commit()

if datetime.utcnow() - account.last_active_at > timedelta(minutes=10):
account.last_active_at = datetime.utcnow()
db.session.commit()

return account


@staticmethod
def get_account_jwt_token(account):
payload = {
Expand Down Expand Up @@ -277,18 +256,19 @@ def get_current_tenant_by_account(account: Account):
@staticmethod
def switch_tenant(account: Account, tenant_id: int = None) -> None:
"""Switch the current workspace for the account"""
if not tenant_id:
tenant_account_join = TenantAccountJoin.query.filter_by(account_id=account.id).first()
else:
tenant_account_join = TenantAccountJoin.query.filter_by(account_id=account.id, tenant_id=tenant_id).first()

tenant_account_join = TenantAccountJoin.query.filter_by(account_id=account.id, tenant_id=tenant_id).first()
TenantAccountJoin.query.filter_by(account_id=account.id).update({'current': False})

# Check if the tenant exists and the account is a member of the tenant
if not tenant_account_join:
raise AccountNotLinkTenantError("Tenant not found or account is not a member of the tenant.")
else:
tenant_account_join.current = True
db.session.commit()

# Set the current tenant for the account
account.current_tenant_id = tenant_account_join.tenant_id
session['workspace_id'] = account.current_tenant.id

@staticmethod
def get_tenant_members(tenant: Tenant) -> List[Account]:
Expand Down
Loading