Skip to content

Provide a view of all the access controls for a user within the current app #706

@philayres

Description

@philayres

Provide a view of all the access controls for a user within the current app

Context & Background

Currently administrators must navigate between three separate admin pages (Manage Users, User Roles, User Access Controls) to piece together a complete picture of a user's effective permissions. This is error-prone and time-consuming, especially when debugging why a user can or cannot access a specific resource.

The access control resolution in ReStructure is non-trivial: the evaluate_access_for method in Admin::UserAccessControl applies a first-match-wins strategy over records ordered by priority_order (app-type-specific before global defaults; user-specific → role-based → fallback). Multiple overlapping entries can exist, and the effective permission on a given resource depends on this ordering. Without a consolidated view, admins cannot easily see which rule "won" for each resource.

Requirements

Provide one or more new admin pages (or seeded admin reports) that present all access controls for a selected user within an app type, from four complementary perspectives.

Perspective Views

  1. User → Roles / "Direct" → User Access Controls

    • Show the user, then group by how access was granted (each role name, or "direct" for user_id-based UACs), then list the individual UAC entries (resource type, resource name, access level).
  2. User → User Access Controls → Roles / "Direct"

    • Show the user, then group by resource (resource type + resource name + access level), then show which role(s) or "direct" assignment granted that access.
  3. User → Resolved Access Controls

    • Show the effective (winning) UAC for each resource, after applying the standard priority_order resolution. For each resource, indicate which role or direct assignment was the source of the winning rule. This is the most useful view for debugging "what access does this user actually have?"
  4. User → Roles only

    • Show the user's assigned roles in the selected app type (from Admin::UserRole), with links to the role's UAC entries.

Filtering

All views must support filtering by:

Filter Source Notes
App type Admin::AppType Required; scopes all results
User Active User records Required; the user under inspection
Resource type UAC resource_type enum (table, report, general, activity_log_type, standalone_page, limited_access) Optional; narrows results
Resource name UAC resource_name values for the selected resource type Optional; narrows results

Linking & Navigation

  • Each item in the results must link back to the relevant admin page:
    • Useradmin_manage_users_path filtered to that user
    • User Access Control entryadmin_user_access_controls_path filtered by resource type / resource name / app type
    • User Roleadmin_user_roles_path filtered by user / app type / role name
  • The new page(s) should be accessible from the Users & Access section of the admin index page (app/views/pages/_index_admin.html.erb), gated by current_admin.can_admin?(:user_access_controls).

Ordering

Results within each view must follow the standard UAC priority_order:

app_type_id ASC NULLS LAST,
CASE
  WHEN user_id IS NOT NULL THEN user_id::varchar
  WHEN (role_name IS NOT NULL AND role_name <> '') THEN role_name
  ELSE user_id::varchar
END

Within each grouping, secondary sort by resource_type ASC, resource_name ASC.

Acceptance Criteria

  • AC1: An admin can navigate to a new "User Access Overview" page from the admin index "Users & Access" section.
  • AC2: The page requires selection of an app type and a user before displaying results.
  • AC3: Perspective 1 (User → Roles → UACs) displays all UACs grouped by role name (and "direct"), with correct entries under each group.
  • AC4: Perspective 2 (User → UACs → Roles) displays all UACs grouped by resource, showing which role(s) or direct assignment granted each.
  • AC5: Perspective 3 (Resolved) displays exactly one effective UAC per resource (the winner per priority_order), clearly indicating the source (role name or "direct").
  • AC6: Perspective 4 (Roles only) lists the user's active UserRole entries for the selected app type, each linking to their associated UAC entries.
  • AC7: Optional filters for resource type and resource name correctly narrow results in all perspectives.
  • AC8: Every user, role, and UAC entry in the results links to the appropriate admin page with correct filter parameters.
  • AC9: Results ordering matches the standard priority_order defined in UserAndRoles#priority_order.
  • AC10: The page is only accessible to admins with can_admin?(:user_access_controls) permission.
  • AC11: RSpec specs cover each perspective view, filtering, ordering, and access control on the new page(s).

Technical Considerations

Implementation Approach — Options

Option A: Seeded Admin Reports (lower effort, leveraging existing infrastructure)

  • Define SQL-based reports seeded via db/seeds/ (similar to report_user_notifications.rb).
  • Use search_attrs with type: user and type: config_selector (or defined_selector) filters for app type, resource type, resource name.
  • Use the existing reports_tree.js tree view (data-tt-tree-cols) for hierarchical display in Perspectives 1 and 2.
  • Perspective 3 (Resolved) would require a more complex SQL query using DISTINCT ON or window functions to select the first-match per resource following priority_order.
  • Links in report results could use report column_options with hide_link or custom link formatting.
  • Pros: Reuses existing report infrastructure, filterable out of the box, no new controllers/views needed.
  • Cons: Complex SQL for resolved view; linking from report cells to admin pages may require custom column formatting; limited control over presentation.

Option B: Dedicated Controller & Views (more effort, full control)

  • New Admin::UserAccessOverviewController with a dedicated view.
  • Query Admin::UserAccessControl and Admin::UserRole using existing scopes (scope_user_and_role, where_user_and_role) and priority_order.
  • Render the tree hierarchy using a pattern similar to the protocol tree view (app/views/admin/protocols/_tree_list.html.erb) with collapsible <ul> lists.
  • Pros: Full control over presentation, linking, and UX; can reuse model methods directly.
  • Cons: More code to maintain; new controller, views, routes, and tests.

Option C: Hybrid — Report-backed with custom rendering

  • Use seeded report SQL for data retrieval, but render with a custom partial rather than the standard report table.
  • Pros: SQL-driven data, custom UX.
  • Cons: More complex wiring.

Key Models & Methods Involved

Component File Purpose
Admin::UserAccessControl app/models/admin/user_access_control.rb UAC records; evaluate_access_for, valid_resources
UserAndRoles concern app/models/concerns/user_and_roles.rb priority_order, scope_user_and_role, where_user_and_role
Admin::UserRole app/models/admin/user_role.rb Role assignments; active_app_roles
Admin::AppType app/models/admin/app_type.rb App type scoping; associated_* methods
Resources::Models app/models/resources/models.rb Resource name → class registry
Resources::UserAccessControl app/models/resources/user_access_control.rb resource_descriptions_for_* methods for resource name listings

Database Considerations

  • No schema changes required — all data exists in user_access_controls, user_roles, and users tables.
  • For the Resolved view (Perspective 3), a query like the following would select the winning UAC per resource:
    SELECT DISTINCT ON (resource_type, resource_name)
      resource_type, resource_name, access, role_name, user_id, app_type_id
    FROM ml_app.user_access_controls
    WHERE disabled IS NOT TRUE
      AND (user_id = :user_id
           OR (app_type_id = :app_type_id AND role_name IN (:role_names))
           OR (user_id IS NULL AND (role_name IS NULL OR role_name = '')))
      AND (app_type_id = :app_type_id OR app_type_id IS NULL)
    ORDER BY resource_type, resource_name,
      app_type_id ASC NULLS LAST,
      CASE WHEN user_id IS NOT NULL THEN 0
           WHEN role_name IS NOT NULL AND role_name <> '' THEN 1
           ELSE 2 END

UI Placement

  • Add link in app/views/pages/_index_admin.html.erb under the "Users & Access" section, after "User Access Controls".
  • Gate with current_admin.can_admin?(:user_access_controls).

Edge Cases & Risks

  1. Users with no roles or UACs: The page should display an informative "No access controls found" message rather than a blank or error page.
  2. Disabled UACs/roles: Disabled entries should be excluded by default but optionally shown (with visual distinction) if an "include disabled" filter is provided.
  3. Global (NULL app_type) UACs: These apply across all app types and must appear in results when viewing any specific app type, clearly marked as "global default".
  4. Large number of UAC entries: Some app types may have hundreds of UAC entries across many roles. Ensure the view remains performant — consider pagination or lazy-loading of sub-trees.
  5. Role with no UAC entries: A role assigned to a user that has no corresponding UAC entries should still appear in Perspective 4 (Roles only) but be flagged as "no access controls defined".
  6. Conflicting access levels: When multiple roles grant different access levels to the same resource, the Resolved view must clearly show which one won and why (i.e., which role/assignment had higher priority).
  7. Template users and roles (@template email users, Settings::AppTemplateRole): These should be excluded from normal user lists but could appear in UAC results if filtering by resource — consider filtering them out by default.

Non-Functional Requirements

  • Performance: Page load for any perspective should complete within 2 seconds for a user with up to 500 UAC entries across roles.
  • Accessibility: Follow existing admin page HTML structure and ARIA patterns; collapsible tree sections should be keyboard-navigable.
  • Security: Gated by can_admin?(:user_access_controls) — no user-facing access. UAC data is admin-only and should not leak to non-admin users.
  • Maintainability: If implemented as seeded reports, SQL must be versioned in db/seeds/ and idempotent (use find_or_initialize_by).
  • Consistency: The ordering and resolution logic must exactly match UserAndRoles#priority_order and evaluate_access_for — any divergence would produce misleading results.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions