Skip to content

Newly created project gets no project_member row — creator holds no role, sharing is impossible #659

Description

@silentsudo-io

Summary

add_project() never grants the creator a role on the new project. No row is written to
project_member at all, so the project ends up with no roles whatsoever.

Consequences:

  1. Sharing is broken. With no roles on the project, the web UI's Collaborators page cannot add
    anyone — including the owner adding themselves. This was the presenting symptom for us.
  2. With GLOBAL_ADMIN=True, the creator cannot see their own project.

Environment

Image lutraconsulting/merginmaps-backend:2025.7.3 (self-reports 2025.6.2, see #548)
Deployment self-hosted CE, Docker Compose, single global workspace
Config GLOBAL_READ / GLOBAL_WRITE / GLOBAL_ADMIN all unset (default False)

Verified still present in the current sourceadd_project() is structurally unchanged at
2025.7.3, 2025.8.0, 2025.8.1, 2025.8.2, 2026.4.2, 2026.5.0, 2026.6.0 and 2026.6.2.
None of them calls set_role.

Steps to reproduce

  1. Create a project — web UI or POST /v1/project/{namespace}; both reproduce it.
  2. Query the database:
SELECT p.name, u.username, pm.role
FROM project p
LEFT JOIN project_member pm ON pm.project_id = p.id
LEFT JOIN "user" u ON u.id = pm.user_id
WHERE p.name = '<new project>';

Expected: the creator holds owner.
Actual: no rows.

Observed on two projects created by two different users through two different paths:

schema-change-test | (NO MEMBERS)     <- created in the web UI by a superuser
list-refresh-test  | (NO MEMBERS)     <- created via the REST API by a non-superuser

Two older projects on the same server do have admin:owner rows — they were migrated in from
another host with their rows already present, which is why this went unnoticed at first.

Why the creator loses visibility

GlobalWorkspaceHandler.get_user_role() returns OWNER for is_admin users and GUEST for
everyone else when the GLOBAL_* settings are at their defaults. A GUEST reaches a project only
via project_member or Project.public — so a non-superuser who creates a project immediately
cannot see it.

Reproduced with a non-superuser that had been granted create rights:

GET /v1/project/paginated?flag=created   ->  count=0

flag=created filters on Project.creator_id == user.id and the user is the creator, but
projects_query(ProjectPermissions.Read) discards the project before the creator test is reached.

Cause

server/mergin/sync/public_api_controller.py, add_project() builds the project with
creator=current_user, adds a ProjectVersion, and commits — but never calls set_role.
Project.__init__ (sync/models.py) sets self.creator and does not call it either. The only
callers of set_role are the explicit collaborator endpoints in public_api_v2_controller.py.

Suggested fix

In add_project(), before db.session.commit():

p.set_role(current_user.id, ProjectRole.OWNER)

A data migration granting owner to creator_id for existing projects that have no
project_member rows would repair servers already affected.

I'm happy to open a PR for this if it would be useful.

Workaround

INSERT INTO project_member (project_id, user_id, role)
SELECT p.id, u.id, 'owner'::project_role
FROM project p JOIN "user" u ON u.username = '<owner>'
WHERE p.name = '<project>'
ON CONFLICT DO NOTHING;

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions