feat(org): add organization limit per user
Add 10 organization limit per user to prevent abuse. Includes graceful fallback if count service is unavailable. - Add get_user_org_count method to OrganizationService - Check org count before allowing new organization creation - Improve invite email mismatch error message for logged-in users
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
"""Organization core CRUD endpoints."""
|
||||
import logging
|
||||
from flask import g, request
|
||||
from marshmallow import ValidationError
|
||||
from gatehouse_app.api.v1 import api_v1_bp
|
||||
@@ -12,6 +13,15 @@ from gatehouse_app.services.organization_service import OrganizationService
|
||||
@login_required
|
||||
@full_access_required
|
||||
def create_organization():
|
||||
try:
|
||||
org_count = OrganizationService.get_user_org_count(g.current_user.id)
|
||||
if org_count is not None and org_count >= 10:
|
||||
return api_response(success=False, message="You cannot belong to more than 10 organizations", status=400, error_type="ORG_LIMIT_REACHED")
|
||||
except Exception as e:
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.warning(f"[Org] Failed to check org count for user {g.current_user.id}: {e}")
|
||||
# Fail open to avoid blocking legitimate users when the count service is unavailable
|
||||
|
||||
try:
|
||||
schema = OrganizationCreateSchema()
|
||||
data = schema.load(request.json)
|
||||
|
||||
@@ -173,7 +173,7 @@ def accept_invite(token):
|
||||
if session_user.email.lower() != invite.email.lower():
|
||||
return api_response(
|
||||
success=False,
|
||||
message="This invite was sent to a different email address.",
|
||||
message="You are already logged in and this invite was sent to a different email address.",
|
||||
status=403,
|
||||
error_type="EMAIL_MISMATCH",
|
||||
)
|
||||
|
||||
@@ -70,6 +70,22 @@ class OrganizationService:
|
||||
|
||||
return org
|
||||
|
||||
@staticmethod
|
||||
def get_user_org_count(user_id):
|
||||
"""
|
||||
Get the count of organizations a user belongs to.
|
||||
|
||||
Args:
|
||||
user_id: User ID
|
||||
|
||||
Returns:
|
||||
Count of active memberships (deleted_at is NULL)
|
||||
"""
|
||||
return OrganizationMember.query.filter_by(
|
||||
user_id=user_id,
|
||||
deleted_at=None,
|
||||
).count()
|
||||
|
||||
@staticmethod
|
||||
def get_organization_by_id(org_id):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user