Repository navigation
users: no tenant management, UserCreate, UserUpdate and create_admin have no tenant_id #360
Description
Summary
users_user.tenant_id exists and is nullable, but nothing in the users module lets an operator set it. UserCreate/UserUpdate (the API schemas) have no tenant_id field, create_admin/create_standard_user (the CLI/bootstrap path) take no tenant parameter, and there is no admin UI field either. On a host that turns on multi-tenancy, every existing user — including the bootstrap admin — is stuck at tenant_id=None with no supported way to fix it short of a raw UPDATE statement.
Observed
users/contracts/schemas.py:35 and :44:
class UserCreate(CreateUpdateDictModel, SQLModel):
email: EmailStr
password: str
is_active: bool | None = True
is_superuser: bool | None = False
is_verified: bool | None = False
full_name: str | None = None
# no tenant_id
class UserUpdate(CreateUpdateDictModel, SQLModel):
password: str | None = None
email: EmailStr | None = None
is_active: bool | None = None
is_superuser: bool | None = None
is_verified: bool | None = None
full_name: str | None = None
# no tenant_idusers/bootstrap.py:51:
async def create_admin(
db: AsyncSession,
*,
email: str,
password: str,
full_name: str | None = None,
force: bool = False,
) -> CreateAdminResult:No tenant_id parameter. (UserRead, the response schema at schemas.py:20-32, does already expose tenant_id: str | None read-only — it's only the write paths that are missing it.)
Minimal reproduction (no DB needed):
import inspect
from users.contracts.schemas import UserCreate, UserUpdate
from users.bootstrap import create_admin
assert "tenant_id" not in UserCreate.model_fields
assert "tenant_id" not in UserUpdate.model_fields
assert "tenant_id" not in inspect.signature(create_admin).parameters
print("confirmed: no write path sets tenant_id")Confirmed on the real stack in the spike (FACT 3, test_spike_app_tenancy.py::test_3_multi_tenant_host_resolution): users.bootstrap.create_admin -> users_user.tenant_id=None (no tenant parameter). The only way the spike found to assign a tenant was a raw UPDATE users_user SET tenant_id=... WHERE id=....
Expected
An operator running a multi-tenant host should be able to create a user (or the bootstrap admin) with a tenant, and update an existing user's tenant, through the same UserCreate/UserUpdate schemas and admin UI used for everything else about the user.
Why a module cannot work around it
records doesn't own the users table or its schemas; it can only refuse tenant-less users at its own admin routes (fail-closed), not give an operator a way to fix the underlying user.
Proposed API
tenant_id: str | None = None on UserCreate/UserUpdate (and the admin create/edit forms), plus create_admin(..., tenant_id: str | None = None) and create_standard_user(..., tenant_id: str | None = None).
Context
Found while making the records module (antosubash/smpy_modules#37) multi-tenant; design doc docs/plans/2026-09-23-records-multitenancy.md there.
Activity
antosubash commentedon Oct 1, 2026
Closing: filed before #370 (fail-closed tenancy + tenants module) landed, so the premise is stale. Module tenancy adoption is being re-tracked with fresh per-module issues in the post-#370 format (see #371–#377); any remaining gap here gets a new issue and lands in the tenancy adoption PR stack.