Replace single test user with three seeded dev users; add permissions TODO

- scripts/seed.py: seed three fixed dev users on every startup:
    test_admin@example.com / Secure_Dev1!  (admin)
    test_1@example.com     / Secure_Dev2!  (user)
    test_2@example.com     / Secure_Dev3!  (user)
  Upsert logic: missing users are created; existing users have their admin
  flag corrected if it drifted; all passwords pass the strength policy
- TODO.md: add permissions registry item (user_app_permissions table,
  admin UI to grant/revoke per-app access per user)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-04-13 18:50:02 +02:00
parent 87c7cc193a
commit 212c663a4c
2 changed files with 59 additions and 24 deletions
+4
View File
@@ -1,5 +1,9 @@
# TODO # TODO
## App permissions
- [ ] **Permissions registry** — admin-managed table that controls which apps each user can access. Schema: `user_app_permissions (user_id FK, app_key)`. Admin UI lets the admin grant/revoke per-app access per user. The Apps page only shows apps the current user has been granted access to.
## Frontend features ## Frontend features
- [x] **Logout button** — visible when logged in, clears token and redirects to `/login` - [x] **Logout button** — visible when logged in, clears token and redirects to `/login`
+48 -17
View File
@@ -1,4 +1,8 @@
"""Create a test user for the dev environment if it doesn't exist yet.""" """Seed the dev environment with a fixed set of test users.
Users are upserted on every startup — missing ones are created, existing ones
are left untouched except for the admin flag which is always enforced.
"""
import asyncio import asyncio
@@ -8,35 +12,62 @@ from app.core.security import hash_password
from app.database import AsyncSessionLocal from app.database import AsyncSessionLocal
from app.models.user import User from app.models.user import User
TEST_EMAIL = "test@example.com" # ── Dev seed users ────────────────────────────────────────────────────────────
TEST_PASSWORD = "Test123!" # Passwords satisfy the strength policy (upper, lower, digit, special char,
TEST_NAME = "Test User" # no forbidden words) so they can also be used via the API if needed.
SEED_USERS = [
{
"email": "test_admin@example.com",
"password": "Secure_Dev1!",
"full_name": "Test Admin",
"is_superuser": True,
},
{
"email": "test_1@example.com",
"password": "Secure_Dev2!",
"full_name": "Test User One",
"is_superuser": False,
},
{
"email": "test_2@example.com",
"password": "Secure_Dev3!",
"full_name": "Test User Two",
"is_superuser": False,
},
]
async def seed() -> None: async def seed() -> None:
async with AsyncSessionLocal() as db: async with AsyncSessionLocal() as db:
result = await db.execute(select(User).where(User.email == TEST_EMAIL)) for spec in SEED_USERS:
result = await db.execute(
select(User).where(User.email == spec["email"])
)
existing = result.scalar_one_or_none() existing = result.scalar_one_or_none()
if existing: if existing:
# Ensure the dev test user is always an admin # Always enforce the correct admin flag in case it drifted
if not existing.is_superuser: if existing.is_superuser != spec["is_superuser"]:
existing.is_superuser = True existing.is_superuser = spec["is_superuser"]
await db.commit() await db.commit()
print(f"[seed] promoted test user to admin: {TEST_EMAIL}") flag = "admin" if spec["is_superuser"] else "user"
print(f"[seed] updated role → {flag}: {spec['email']}")
else:
print(f"[seed] already exists: {spec['email']}")
else: else:
print(f"[seed] test user already exists: {TEST_EMAIL}")
return
user = User( user = User(
email=TEST_EMAIL, email=spec["email"],
hashed_password=hash_password(TEST_PASSWORD), hashed_password=hash_password(spec["password"]),
full_name=TEST_NAME, full_name=spec["full_name"],
is_superuser=True, is_superuser=spec["is_superuser"],
) )
db.add(user) db.add(user)
await db.commit() await db.commit()
print(f"[seed] created test admin — email: {TEST_EMAIL} pwd: {TEST_PASSWORD}") role = "admin" if spec["is_superuser"] else "user"
print(
f"[seed] created {role}: {spec['email']} pwd: {spec['password']}"
)
if __name__ == "__main__": if __name__ == "__main__":