raystack / raystack/frontier

SearchUserProjects diverges from ListProjectsByCurrentUser — missing inherited and group-expanded projects

Open
#1,650 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
344
Forks
47
Avg merge
4d 4h
Merged PRs (30d)
26

Description

Symptom

AdminService/SearchUserProjects and FrontierService/ListProjectsByCurrentUser answer effectively the same question — "which projects can this user see?" — but return different results for the same user, same org, same state.

Concrete repro

User Alice is org owner of OrgX (5 projects). She has no direct user→project policies; her visibility comes entirely from the app_organization_owner role on OrgX.

RPC Result
ListProjectsByCurrentUser(orgID=OrgX) 5 projects (org-inheritance expansion)
SearchUserProjects(userID=Alice, orgID=OrgX) 0 projects

The same gap appears for group-mediated access: a user in a group that holds a project policy is visible to ListProjectsByCurrentUser but invisible to SearchUserProjects.

Where it lives

internal/store/postgres/user_projects_repository.go::buildBaseQuery filters the inner subquery with:

WHERE pol.principal_id   = :userID
  AND pol.principal_type = 'app/user'        -- excludes group-mediated policies
  AND pol.resource_type  = 'app/project'     -- excludes org-level (inherited) policies

Both filters together restrict the result to direct user→project policy rows only.

project.Service.List(Filter{Principal}), used by ListProjectsByCurrentUser, delegates to membership.Service.ListProjectsByPrincipal, which unions three paths:

  1. Direct project policies (gated by schema.ProjectDirectVisibilityPerms).
  2. Group-expanded — principal's group memberships → policies on those groups.
  3. Org-inherited (skipped when NonInherited=true) — org-level policies gated by schema.OrganizationProjectInheritPerms.

The aggregate query in user_projects_repository only implements path 1.

Background

The divergence is pre-existing. It became visible after the four-PR membership listing migration (parent #1478) made the ListProjects* semantics explicit and policy-table-backed end-to-end. PR #1648 originally landed a TODO(fix) comment at the aggregate; the comment was dropped in favour of this tracked issue.

Options

  • A. Align the aggregate. Rewrite buildBaseQuery to UNION (a) direct user→project, (b) group-expanded — principal's groups joined into project policies, (c) org-inherited — Alice's org policies whose role grants any permission in OrganizationProjectInheritPerms, expanded to all projects in those orgs. Mirrors the membership method's logic in SQL.
  • B. Redefine the aggregate. Document SearchUserProjects as "projects on which the user has a direct policy" by product decision. Update the admin UI label if needed.
  • C. Document and leave both as-is. Keep the divergence; add a comment explaining the semantic difference.

A is the consistent fix; B is a smaller scope decision; C is the cheapest if the divergence is acceptable.

Acceptance

  • Decision recorded.
  • If A: aggregate returns the same set as project.Service.List(Filter{Principal}) for a given user/org. Regression test covering an org-owner-with-no-direct-policy case.
  • If B: admin UI / API docs reflect the narrowed semantic.
  • If C: comment near buildBaseQuery documenting the divergence and linking to this issue.

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 in internal/store/postgres/user_projects_repository.go at buildBaseQuery, then compare its direct-policy query with project.Service.List(Filter{Principal}) and membership.Service.ListProjectsByPrincipal. First resolve whether the project wants options A, B, or C; done means the recorded decision and its corresponding implementation, documentation, or comment, with the specified regression comparison if A is chosen.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, sql
Domain
authorization, backend, databases
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.