Skip to content

security: verify zero-group user cannot retain admin access (QA row 511 / step 15.1) #948

Closed

Description

@cristim

Source: QA sheet row 511 / step 15.1.

Action (repro as reported by QA)

Create an Admin user that is not added to any Group (zero-group user), then log in as that user.

Expected (acceptance)

A user must not be able to hold admin access without an explicit group grant. Either:

  • user-create rejects a zero-group user, or
  • the user is auto-assigned to the Administrators group,

and admin capability is derived solely from group membership (Administrators group 00000000-0000-5000-8000-000000000001). A user with no group can do nothing.

Actual (observed at QA time)

The user stayed unassigned to any group and still had admin access when logged in. Admin was not strictly group-derived. SECURITY gap (privilege without an explicit grant).

Verification finding (current base: feat/multicloud-web-frontend)

Already fixed by #907/#912 ("group-membership-only authorization, remove roles, require >=1 group"), reinforced by #924/#923 (Purchaser group) and #941 (dropped last session.Role shortcut). Both failure modes are now closed on the current production base:

  1. Zero-group creation is rejected. Service.validateCreateUserRequest (internal/auth/service_user.go:134) returns ErrNoGroups -> 400 when len(req.GroupIDs) == 0. UpdateUser/guardGroupChange (:328) enforces the same on edit. You cannot create or leave a user with zero groups via the API.
  2. Admin is strictly group-derived. GetUserPermissions (internal/auth/service_group.go:67) sources permissions "exclusively from group memberships"; a zero-group user gets an empty permission set. UserHasAdminCapability / HasPermission only grant admin on the {admin, *} permission, which comes only from the Administrators group. The admin middleware gate (internal/api/middleware.go:266) calls HasPermissionAPI(admin, *) and returns 403 otherwise (fail-closed). No live role-based admin path remains: RoleAdmin (internal/auth/types.go:303) is referenced only in its own definition and doc comments; the last session.Role shortcut was removed in fix(api/purchases): drop removed session.Role shortcut in execute-direct gate (closes #940) #941.

Regression coverage already present:

  • internal/auth/service_group_only_authz_test.go — TestGroupOnlyAuthz_ZeroGroupFailClosed asserts a zero-group user is denied everything and UserHasAdminCapability is false; plus tests asserting ErrNoGroups on create/update.
  • internal/auth/service_user_test.go:187,508 — assert ErrNoGroups on zero-group create.

Conclusion

Already-fixed-by-#907/#912. No code change or PR required. Filing for traceability and to mark the QA row resolved. Recommend re-testing on the current production build to close the sheet row.

Activity

  1. cristim commented on Jul 27, 2026

    @cristim
    MemberAuthor

    Verification sweep against main (101f099fb) on 2026-07-27 finds this already resolved.
    Authorization is now group-derived only, with ErrNoGroups on an empty group set and no live session.Role usage remaining.
    Evidence: #907/#912.
    Recommending close.

  2. cristim commented on Sep 2, 2026

    @cristim
    MemberAuthor

    Verified resolved at 3c0f8ac: user create and update reject an empty group set with ErrNoGroups, permissions come exclusively from group memberships, and no role-based admin shortcut remains, so a zero-group user cannot exist via the API and would hold no capability if one did. Evidence: internal/auth/service_user.go:136 and :371, internal/auth/service_group.go:87, internal/auth/service_group_only_authz_test.go:97 (#912). Residual axes checked: update path (guardGroupChange), session.Role reads in internal/ (none outside tests), RoleAdmin constant (definition only). Closing as completed; reopen if the behaviour recurs.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions