Skip to content

feat: Move user between organizations with optional resource migration #424

Description

@xezpeleta

Problem

Users created in the wrong organization (e.g., accidentally assigned to System instead of their intended org) cannot be moved. The only workaround is deleting and recreating the user, which loses all their data. With #249 (org creation without admin + promote/demote), the multi-org flow is almost complete — but there's no way to fix an incorrect org assignment after user creation.

There's also no way to add an existing user to an empty organization from the Members modal. The natural place people look (Orgs → Members → Add Member) doesn't exist; they must go to Users → Create User and select the target org.

Proposed solution

A Move User feature that allows system admins to transfer a user from one organization to another, with fine-grained control over what moves with them.

API

POST /admin/users/{user_id}/move  

Request body:

{
  target_organization_slug: repelega,
  conflict_strategy: rename,
  migrate_resources: {
    assistants: true,
    prompt_templates: true,
    kb_registry: true,
    collections: true,
    libraries: true,
    lti_activities: true,
    usage_logs: true
  }
}
  • migrate_resources: per-resource-type toggles (all default true). Admin can choose what to move.
  • conflict_strategy: rename, skip, or fail. Same as the existing org-level migration (migrate_organization_comprehensive).

What gets moved (when selected)

Resource Table Constraint Conflict handling
User record Creator_users Direct UPDATE organization_id
Org roles organization_roles Move to target org, remove from source
Assistants assistants UNIQUE(org_id, name, owner) Rename to {slug}_{name} on conflict
Prompt templates prompt_templates UNIQUE(org_id, owner_email, name) Rename on conflict
KB registry kb_registry FK to Creator_users.id Direct update
Collections collections UNIQUE(org_id, collection_name) Rename on conflict
Libraries + items libraries / library_items UNIQUE(org_id, name) Rename on conflict
LTI activities lti_activities Direct update
Usage logs usage_logs Direct update (history follows user)

What does NOT need migration

  • Lamb chats: no organization_id column, chats follow user naturally
  • Assistant shares: user-to-user (by ID), survive org change
  • Assistant publish: references assistant by ID, not org

OpenWebUI sync

User is removed from old OWI group and added to new OWI group (OwiGroupManager already has the methods).

Edge cases

  • System admin users cannot leave the System org
  • User is the last admin of the source org → warn, not block
  • Name collision with fail strategy → abort whole migration atomically
  • Name collision with skip → leave that resource in source org

Atomicity

Entire operation runs in a single SQLite transaction. If anything fails, everything rolls back.

UI

In the admin users table or Members modal: a Move User button that opens a modal with:

  1. Target organization dropdown
  2. Resource checkboxes (all checked by default) showing counts (e.g., "3 assistants, 2 KBs")
  3. Conflict strategy selector
  4. Confirmation with summary of what will be moved

Implementation notes

Rationale

This fills the last gap in the organization membership lifecycle:

  1. Create org (without admin, if desired) — feat: Allow organization creation without admin + allow system admin to promote any user (including LTI Creator) to org admin #249
  2. Create user in any org — already works
  3. Promote/demote members within org — feat: Allow organization creation without admin + allow system admin to promote any user (including LTI Creator) to org admin #249
  4. Move user between orgs — this issue ← the missing piece

Without this, a user created in the wrong org is stuck. The delete-and-recreate workaround loses all their assistants, templates, KBs, libraries, and usage history. That's a real problem for production deployments where users accumulate resources over time.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions