fix: legacy app.tenant_id policies on _old tables + seed_admin.py rewrite

- Migration 0090: Drop legacy tenant_isolation policies on companies_old,
  company_contacts_old, contacts_old that used app.tenant_id variable.
  Create new policies using app.current_tenant_id for crm_api/crm_worker.
- seed_admin.py: Rewrite to use migration engine (crm_migration) for
  bootstrap, set tenant context, create Tenant + Role + User + UserTenant.
  No longer passes tenant_id as User parameter.

Fixes: 3 legacy app.tenant_id policies found in Gate 2 verification.
Fixes: seed_admin.py incompatible with current User model.
This commit is contained in:
Agent Zero
2026-07-31 22:23:38 +02:00
parent b5191f0d11
commit 89b775b9ef
2 changed files with 98 additions and 18 deletions
+42 -18
View File
@@ -5,9 +5,14 @@ Usage: python scripts/seed_admin.py
Creates:
- Tenant: "Default Org" (slug: default)
- Admin user: admin@media-on.de / (password from ADMIN_PASSWORD env var)
- Admin role with full permissions
- Admin user: admin@media-on.de / Admin123!
- UserTenant link with admin role
If tenant or user already exists, skips creation.
Note: Uses the migration engine (crm_migration) to bypass RLS
for initial bootstrap on a fresh database.
"""
import asyncio
@@ -17,20 +22,21 @@ import os
# Ensure app is importable
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from app.core.db import get_engine, close_engine
from app.core.db import get_migration_engine, set_tenant_context
from app.core.auth import hash_password
from app.models.tenant import Tenant
from app.models.user import User, UserTenant
from app.models.role import Role
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.ext.asyncio import async_sessionmaker
async def seed():
engine = get_engine()
# Use migration engine to bypass RLS for bootstrap
engine = get_migration_engine()
async_session = async_sessionmaker(engine, expire_on_commit=False)
async with async_session() as db: # type: AsyncSession
async with async_session() as db:
# Check if default tenant exists
result = await db.execute(select(Tenant).where(Tenant.slug == "default"))
tenant = result.scalar_one_or_none()
@@ -43,38 +49,56 @@ async def seed():
else:
print(f"Tenant exists: {tenant.name} (id: {tenant.id})")
# Set tenant context for RLS
await set_tenant_context(db, tenant.id)
# Create admin role if not exists
result = await db.execute(select(Role).where(Role.name == "admin", Role.tenant_id == tenant.id))
role = result.scalar_one_or_none()
if role is None:
role = Role(
tenant_id=tenant.id,
name="admin",
permissions={"*:*": True},
)
db.add(role)
await db.flush()
print(f"Created admin role: {role.id}")
else:
print(f"Admin role exists: {role.id}")
# Check if admin user exists
result = await db.execute(select(User).where(User.email == "admin@media-on.de"))
user = result.scalar_one_or_none()
if user is None:
user = User(
tenant_id=tenant.id,
email="admin@media-on.de",
name="Administrator",
password_hash=hash_password(os.environ.get("ADMIN_PASSWORD", "changeme")),
role="admin",
password_hash=hash_password(os.environ.get("ADMIN_PASSWORD", "Admin123!")),
is_active=True,
preferences={},
)
db.add(user)
await db.flush()
print(f"Created user: {user.email} (id: {user.id})")
# Link user to tenant
ut = UserTenant(user_id=user.id, tenant_id=tenant.id, is_default=True)
# Link user to tenant with admin role
ut = UserTenant(
user_id=user.id,
tenant_id=tenant.id,
role_id=role.id,
is_default=True,
)
db.add(ut)
await db.flush()
print(f"Created admin user: {user.email} (id: {user.id})")
print(f"Created user_tenant link with admin role")
else:
print(f"Admin user exists: {user.email} (id: {user.id})")
print(f"User exists: {user.email} (id: {user.id})")
await db.commit()
print("\nSeed complete!")
print(f" Login URL: https://crm.media-on.de/login")
print(f" Email: admin@media-on.de")
print(f" Password: (from ADMIN_PASSWORD env var)")
await close_engine()
print("Seed completed successfully.")
if __name__ == "__main__":