Skip to content

Allow anonymous access to welcome page #1129

Description

@IanMayo

By default, the welcome page (and app in general) requires login/auth. But, we could allow anonymous access - this could help analysts learn about new data/records.

So, all resources would be publicly visible, but only auth users would have action buttons, or edit/save/cancel buttons.

image

We'd obvs have to enforce this in the backend. I suspect we will have to introduce a zero role_id, that is used for users that are not logged in . This role will have read access to all tables, plus write access to the requests table.

Update: it's possible to do this with minimal change:

  1. Admin creates user via VAL UI:
    • username: browse-only
    • Assign read-only role
    • Set temp password
  2. Login as browse-only (within 60 min):
    • Use temp password
    • Self-change to permanent password (e.g., browse2024!)
  3. Manually edit database:
  UPDATE _users
  SET lastUpdatedAt = '9999-12-31T00:00:00Z',
      updateBefore = ''
  WHERE username = 'browse-only';

Also, modify the _user_role for that user to be the default role (this isn't currently possible via the UI).

  1. Frontend Login button:
    <Button onClick={() => login({
    username: 'browse-only',
    password: 'browse2024!'
    })}>
    Browse as Guest

VAL Browse-Only Access - Permission Implementation Analysis

Current Permission Implementation

1. Resource Routes (App.tsx:479+)

  • protectedRoutes() function removes edit/create routes when write: false
  • Already works - browse-only users won't have edit/create routes available

2. List Actions - CreateButtons

All protected with useCanAccess:

  • BatchList.tsx:35-38
{hasAccess(R_BATCHES, {write: true}) ? <CreateButton label='ADD NEW BATCH' /> : <></>}
  • UserList.tsx:40-46
{hasWriteAccess ? <CreateButton to={`${basePath}/create`} /> : null}
// where: hasWriteAccess = hasAccess(R_USERS, {write: true})
  • ReferenceDataList.tsx:45-47
{hasAccess('reference-data', {write: true}) ? <CreateButton to={'create'} /> : null}
  • BatchShow.tsx:68-74 (nested CreateButton for items within batch)
{hasAccess(R_ITEMS, {write: true}) ? (
  <CreateButton label='ADD ITEM' to={`/${R_RICH_ITEMS}/create?batch=${batch}`} />
) : <></>}

3. Show Actions - EditButtons

All Show components protected:

  • ItemShow.tsx:376-378
{hasAccess(R_ITEMS, {write: true}) && <EditButton to={`/${R_RICH_ITEMS}/${record.id}`} />}
  • BatchShow.tsx:44
{hasAccess(R_BATCHES, {write: true}) && <EditButton />}
  • UserShow.tsx:531
{hasWriteAccess && <EditButton />}
// where: hasWriteAccess = hasAccess(R_USERS, {write: true})
  • DispatchShow.tsx:59-60
{hasAccess(R_DISPATCH, {write: true}) && !dispatched && <EditButton />}
  • DestructionShow.tsx:62-63
{hasAccess(R_DESTRUCTION, {write: true}) && !finalised && <EditButton />}
  • ProjectShow.tsx:53-55
{hasAccess(R_PROJECTS, {write: true}) ? <><EditButton /><CreateButton /></> : null}
  • ReferenceDataShow.tsx:35
{hasAccess(resource, {write: true}) ? <EditButton /> : null}

4. Bulk Actions

ItemList.tsx - Comprehensive protection:

  • Line 492 - Loan buttons:
const disableLoanItemsBulkActions = !(
  canBeLoaned &&
  hasAccess(R_ITEMS, {write: true}) &&
  isSelected
)
  • Lines 498-504 - Dispatch/Destroy/Change Location:
const normalWithWriteAccess =
  isItemNormal && hasAccess(R_ITEMS, {write: true}) && isSelected

const { disableDispatch, disableDestroy, disableChangeLocation } = {
  disableDispatch: !(normalWithWriteAccess && dispatch),
  disableDestroy: !(normalWithWriteAccess && destroy),
  disableChangeLocation: !(normalWithWriteAccess && location)
}

Lists with bulkActionButtons disabled:

  • BatchList.tsx:177 - bulkActionButtons={false}
  • Recent.tsx:169 - bulkActionButtons={false}
  • ReferenceDataList.tsx:66 - bulkActionButtons={false}

Custom bulk actions (read-only):

  • UserList.tsx:77 - bulkActionButtons={<UserActions />}
    • Shows "User Muster List" button (read-only report generation)

5. Other Protected Actions

UserShow.tsx:

  • Line 435 - "Depart Organisation" button:
disabled={cannotDepart()}
// cannotDepart checks: !hasWriteAccess || hasLoanedItems || alreadyDeparted
  • Line 533 - "Edit Password" button (role-based):
{userDetails && rolesThatCanEditPassword.includes(userDetails.userRole) ? (
  <Button onClick={handleEditPasswordOpen}>Edit Password</Button>
) : null}

DispatchShow.tsx:

  • Line 94 - Footer mutation buttons check:
const hasWritePermission = hasAccess(R_ITEMS, {write: true})
  • Disables: Dispatch, Print Receipt, Print Hastener, Record Hastener Sent, Receipt Note Received

DestructionShow.tsx:

  • Line 89 - Footer mutation buttons check:
const hasWritePermission = hasAccess(R_ITEMS, {write: true})
  • Disables: Destroy, Destruction Certificate

6. Core Permission Hook

useCanAccess.ts:36-64 - protectedRoutes() function:

export const protectedRoutes = (
  permissions: ResourcePermissions,
  resource: string,
  routes: ResourceRoutes
): ResourceRoutes => {
  const permission = permissions[resource] ?? { read: false, write: false }
  const { write, read } = permission

  const hasReadPermission = !!(read)
  const hasWritePermission = !!(hasReadPermission && write)

  return {
    create: hasWritePermission ? routes.create : undefined,
    edit: hasWritePermission ? routes.edit : undefined,
    list: hasReadPermission ? routes.list : undefined,
    show: hasReadPermission ? routes.show : undefined
  }
}

Used throughout App.tsx to control route availability based on permissions.


Summary

✅ Complete Protection Coverage

The codebase has comprehensive permission checking already in place:

  1. Route-level protection - protectedRoutes() removes edit/create routes for read-only users
  2. Component-level protection - All CreateButtons and EditButtons check hasAccess()
  3. Action-level protection - All bulk actions and mutation buttons check permissions
  4. Consistent pattern - Uses useCanAccess() hook uniformly across all components

🎯 Implications for Browse-Only Feature

No frontend code changes required for permission enforcement!

The browse-only feature will work automatically once:

  1. Browse-only user exists in database
  2. User has read-only role assigned (all resources: read: true, write: false, delete: false)
  3. Login page has "Browse as Guest" button

All UI elements (EditButtons, CreateButtons, BulkActions, mutation operations) will automatically hide/disable based on the user's permissions returned by the auth provider.


Resources Covered

  • ✅ Items (R_ITEMS, R_RICH_ITEMS, R_ALL_ITEMS)
  • ✅ Batches (R_BATCHES)
  • ✅ Projects (R_PROJECTS)
  • ✅ Dispatch (R_DISPATCH)
  • ✅ Destruction (R_DESTRUCTION)
  • ✅ Users (R_USERS)
  • ✅ Vault Locations (R_VAULT_LOCATION)
  • ✅ Addresses (R_ADDRESSES)
  • ✅ Audit (R_AUDIT)
  • ✅ All Reference Data (Protective Marking, Cat Codes, etc.)

Implementation Quality Notes

Strengths:

  • Consistent use of useCanAccess hook across all components
  • Multi-layer protection (routes, components, actions)
  • Write permission implies read permission (proper hierarchy)
  • Disabled states for buttons (better UX than hiding)

Architecture:

  • Central permission system in authProvider/permissions.ts
  • Reusable useCanAccess() hook
  • protectedRoutes() helper for route-level control
  • Permission data flows from backend through auth provider

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    Ready

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions