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.

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:
- Admin creates user via VAL UI:
- username: browse-only
- Assign read-only role
- Set temp password
- Login as browse-only (within 60 min):
- Use temp password
- Self-change to permanent password (e.g., browse2024!)
- 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).
- 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:
{hasAccess(R_BATCHES, {write: true}) ? <CreateButton label='ADD NEW BATCH' /> : <></>}
{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:
{hasAccess(R_ITEMS, {write: true}) && <EditButton to={`/${R_RICH_ITEMS}/${record.id}`} />}
{hasAccess(R_BATCHES, {write: true}) && <EditButton />}
{hasWriteAccess && <EditButton />}
// where: hasWriteAccess = hasAccess(R_USERS, {write: true})
{hasAccess(R_DISPATCH, {write: true}) && !dispatched && <EditButton />}
- DestructionShow.tsx:62-63
{hasAccess(R_DESTRUCTION, {write: true}) && !finalised && <EditButton />}
{hasAccess(R_PROJECTS, {write: true}) ? <><EditButton /><CreateButton /></> : null}
{hasAccess(resource, {write: true}) ? <EditButton /> : null}
4. Bulk Actions
ItemList.tsx - Comprehensive protection:
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:
- Route-level protection -
protectedRoutes() removes edit/create routes for read-only users
- Component-level protection - All CreateButtons and EditButtons check
hasAccess()
- Action-level protection - All bulk actions and mutation buttons check permissions
- 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:
- Browse-only user exists in database
- User has read-only role assigned (all resources:
read: true, write: false, delete: false)
- 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
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.
We'd obvs have to enforce this in the backend. I suspect we will have to introduce a
zerorole_id, that is used for users that are not logged in . This role will have read access to all tables, plus write access to therequeststable.Update: it's possible to do this with minimal change:
Also, modify the _user_role for that user to be the default role (this isn't currently possible via the UI).
<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 removesedit/createroutes whenwrite: false2. List Actions - CreateButtons
All protected with
useCanAccess:3. Show Actions - EditButtons
All Show components protected:
4. Bulk Actions
ItemList.tsx - Comprehensive protection:
Lists with bulkActionButtons disabled:
bulkActionButtons={false}bulkActionButtons={false}bulkActionButtons={false}Custom bulk actions (read-only):
bulkActionButtons={<UserActions />}5. Other Protected Actions
UserShow.tsx:
DispatchShow.tsx:
DestructionShow.tsx:
6. Core Permission Hook
useCanAccess.ts:36-64 -
protectedRoutes()function:Used throughout App.tsx to control route availability based on permissions.
Summary
✅ Complete Protection Coverage
The codebase has comprehensive permission checking already in place:
protectedRoutes()removes edit/create routes for read-only usershasAccess()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:
read: true, write: false, delete: false)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
R_ITEMS,R_RICH_ITEMS,R_ALL_ITEMS)R_BATCHES)R_PROJECTS)R_DISPATCH)R_DESTRUCTION)R_USERS)R_VAULT_LOCATION)R_ADDRESSES)R_AUDIT)Implementation Quality Notes
Strengths:
useCanAccesshook across all componentsArchitecture:
authProvider/permissions.tsuseCanAccess()hookprotectedRoutes()helper for route-level control