OpenHands / OpenHands/enterprise

Allow superadmin to seed a new org via a normal (invitation-based) flow, without user-provisioning

Open
#63 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Python
Stars
4
Forks
2
Avg merge
1d 22h
Merged PRs (30d)
101

Description

Summary

Provide a way for an instance-level superadmin to seed a newly-created organization with an initial owner/admin through a normal, invitation-based flow — without requiring the privileged POST /api/organizations/provision-user endpoint.

Background

Two recently merged PRs established the super-admin org-management model:

  • OpenHands/OpenHands#14864 — adds POST /api/organizations/provision-user (gated by USER_PROVISIONING_ENABLED).
  • OpenHands/OpenHands#14937 — adds instance-level super roles. A superadmin (user.role_id -> admin role row) holds SUPER_ROLE_PERMISSIONS = {CREATE_ORGANIZATION, PROVISION_USER}. POST /api/organizations now creates the org with add_creator_as_owner=False, so the creating superadmin is not a member of the org it just created.

Problem

After a superadmin creates an organization, there is currently no non-privileged path to add the first user to it. Tracing every caller of OrgMemberStore.add_user_to_org:

  1. Provisioning (server/routes/user_provisioning.py) — only mounted when USER_PROVISIONING_ENABLED is on. It mints the user, sets a server-side non-temporary password, and returns a per-user API key. This is a specialized OEM flow, not appropriate as the only seeding mechanism.
  2. Invitation accept (server/services/org_invitation_service.py) — requires a pending invitation. Creating an invitation needs Permission.INVITE_USER_TO_ORGANIZATION, which is held only by org-scoped owner/admin (existing members). A superadmin is neither granted invite in SUPER_ROLE_PERMISSIONS nor a member of the freshly-created org, so it cannot create an invite. A brand-new org has zero members, so there is no one to bootstrap from.
  3. Default-org auto-add (storage/default_org_service.py) — only targets the single globally-configured default org bootstrap; cannot target an arbitrary superadmin-created org.

Net result: if USER_PROVISIONING_ENABLED is off, a superadmin can create organizations but they are empty shells that can never be populated. The org-creation capability is effectively inert without the OEM provisioning endpoint.

Proposed solution

Allow a superadmin to seed an org through the existing invitation subsystem (normal auth: invitee logs in via Keycloak/SSO, sets their own password, accepts — no server-side credential or API-key minting). Options, roughly in order of preference:

  1. Grant superadmin the ability to invite into any org. Extend require_permission(Permission.INVITE_USER_TO_ORGANIZATION) handling so a super-role satisfies it for any target org (the super-role permission check already falls back after org-scoped checks). This lets a superadmin send a normal invite to the org they created.
  2. Optionally seed on create. Let POST /api/organizations accept an initial-owner email and emit an invitation (or add-on-first-login) for that user as owner, without the superadmin itself becoming a member.
  3. Some combination — e.g. superadmin can invite the first owner, after which the normal org-scoped owner/admin invitation flow takes over.

Whatever the approach, the goal is a seeding path that does not depend on the credential-minting provision-user endpoint, so the superadmin org-creation feature is usable on installs where OEM provisioning is intentionally disabled.

Acceptance criteria

  • A superadmin can, immediately after creating an org, cause an initial owner/admin to join that org via the standard invitation flow (self-service auth; no server-set password, no minted API key).
  • Works with USER_PROVISIONING_ENABLED=false.
  • The superadmin does not need to become a member of the org to do this.
  • Existing org-scoped permission boundaries for non-super users are unchanged.

Related

  • OpenHands/OpenHands#14864
  • OpenHands/OpenHands#14937
  • OpenHands-Cloud PR wiring the OEM provisioning toggle for Replicated installs: OpenHands/OpenHands-Cloud#914

This issue was filed by an AI agent (OpenHands) on behalf of the user.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing permission handling around POST /api/organizations and the invitation flow, then read server/routes/user_provisioning.py, server/services/org_invitation_service.py, and storage/default_org_service.py. Compare how super-role and org-scoped permissions are checked. Done means a newly created org can receive its first owner or admin through the normal invitation flow with provisioning disabled, while non-super-user boundaries remain unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, authorization, backend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.