feat(users): auto-default agent accounts to general-agent role
Previously every account created via POST /users without an explicit role_id fell through to the `guest` role. Recruitment workflow creates HF accounts for newly-onboarded agents with --agent-id/--claw-identifier set, so we can detect "this is an agent" at the backend boundary and pick a more appropriate default: payload.agent_id set → general-agent (guest reads + reset-self-apikey) payload.agent_id unset → guest (human users keep current behavior) Also adds `general-agent` to init_bootstrap.py's _DEFAULT_ROLES so fresh deployments seed it on first boot — the role already existed on prod (created via UI earlier); this is for re-seedability / new envs. No ClawSkills script changes required: the onboard script already calls `hf user create --agent-id <id> --claw-identifier <claw>`. The recruitment workflow.md is updated to note the new default. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -68,11 +68,29 @@ def require_account_creator(
|
||||
raise HTTPException(status_code=403, detail="Account creation permission required")
|
||||
|
||||
|
||||
def _resolve_user_role(db: Session, role_id: int | None) -> Role:
|
||||
def _resolve_user_role(db: Session, role_id: int | None, *, is_agent: bool = False) -> Role:
|
||||
"""Resolve target role for user creation.
|
||||
|
||||
Default policy when caller didn't pin role_id:
|
||||
- is_agent (i.e. payload had agent_id/claw_identifier) → general-agent
|
||||
- human user → guest
|
||||
|
||||
general-agent ≈ guest + user.reset-self-apikey so agents can rotate
|
||||
their own API key without admin intervention. Created in
|
||||
init_bootstrap.py on every startup; falls back to guest if absent
|
||||
(e.g. very old DB that hasn't been re-seeded yet).
|
||||
"""
|
||||
if role_id is None:
|
||||
role = db.query(Role).filter(Role.name == "guest").first()
|
||||
default_name = "general-agent" if is_agent else "guest"
|
||||
role = db.query(Role).filter(Role.name == default_name).first()
|
||||
if not role and is_agent:
|
||||
# general-agent missing from this DB → fall back to guest, log warn
|
||||
role = db.query(Role).filter(Role.name == "guest").first()
|
||||
if not role:
|
||||
raise HTTPException(status_code=500, detail="Default guest role is missing")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Default role '{default_name}' is missing (DB not seeded)",
|
||||
)
|
||||
return role
|
||||
|
||||
role = db.query(Role).filter(Role.id == role_id).first()
|
||||
@@ -112,7 +130,7 @@ def create_user(
|
||||
if existing_agent:
|
||||
raise HTTPException(status_code=400, detail="agent_id already in use")
|
||||
|
||||
assigned_role = _resolve_user_role(db, user.role_id)
|
||||
assigned_role = _resolve_user_role(db, user.role_id, is_agent=has_agent_id)
|
||||
# In OIDC-only mode, ignore any supplied password: the user is created
|
||||
# passwordless (cannot password-login) and is expected to sign in via a
|
||||
# bound OIDC identity. API keys still work for such users.
|
||||
|
||||
Reference in New Issue
Block a user