Heratio Help Center article. Category: Security and Access Control.
Access Request Module
The Access Request module lets authenticated users ask for permission to view restricted or classified material, and gives administrators a queue to review, approve, or deny those requests with automatic email notifications. It is supplied by the ahg-access-request package and is distinct from the broader researcher-portal access workflow documented in the companion "Access Requests and Researcher Portal" guide.
Overview
Restricted records in Heratio can carry a security classification. When a user needs access to material above their current clearance, they submit an access request describing what they need and why. The request lands in a pending queue. Designated approvers (or any administrator) review the queue, then approve or deny each request, optionally attaching notes or a reason. The requester is notified by email at submission, on approval, and on denial; approvers are notified when a new request enters the queue.
The module covers three concerns:
- Request capture - a general request form and an object-specific request form.
- Review workflow - a pending queue, a per-request detail view, and approve/deny/cancel actions.
- Approver administration - a small admin screen for nominating which users may act as approvers.
The module is jurisdiction-neutral. Classification levels are read from the security_classification table and are entirely site-defined; the module does not assume any particular national classification scheme.
Key features
| Feature | Description |
|---|---|
| General access request | A form (subject, request_type, description, optional justification, urgency, requested classification level) for asking for clearance or research access. |
| Object access request | A reason-and-access-type form launched from a specific record, pre-bound to that record's slug. |
| My requests | A self-service list of the signed-in user's own requests. |
| Pending queue | An admin/approver list of all requests still awaiting review. |
| Browse all | An admin list of every request, newest first, paginated 25 per page. |
| Request detail view | A single-request page showing the requester, status, and review notes. |
| Approve / deny | Admin actions that set status, record the reviewer and timestamp, and store review notes or a denial reason. |
| Cancel | A requester action that withdraws their own pending or approved request. |
| Approver management | Add or deactivate the users who appear as designated approvers. |
| Email notifications | Submission acknowledgement to the requester, queue heads-up to approvers, and approval/denial notices, all gated by a single settings toggle. |
| Legacy URL redirects | Old /security/request* and /admin/accessRequests URLs redirect to the current routes (301) for backward compatibility. |
How to use
For requesters
Submit a general access request
- Sign in.
- Go to My Requests (
/access-request/my-requests) and start a new request, or go directly to New Access Request at/access-request/new. - Complete the form:
- Subject (required) - a short title for the request.
- Request Type (required) - one of: View restricted material, Request copies, Permission to publish, Research access, Other.
- Description (required) - what materials you need and the purpose.
- Justification (optional) - any supporting reasoning.
- Urgency (optional) - Low, Normal, High, or Urgent. Normal is the default and helps reviewers prioritise the queue.
- Requested classification level (optional) - shown only when classification levels are configured on the site. Choose the level you need, or leave on the default (lowest) level.
- Submit. You are returned to My Requests with a confirmation, and (if notifications are enabled) you receive an acknowledgement email.
Request access to a specific record
- From a restricted record, follow the request-access link, which opens
/access-request/request/{slug}. - The form shows which record you are requesting and asks for:
- Reason for Access (required).
- Access Type (optional) - View only, Download, or Physical copy.
- Submit. The request is created and you are returned to My Requests.
Track or cancel your requests
- View your requests at
/access-request/my-requests. - Open any request to see its current status and any reviewer notes.
- Cancel a request that is still pending or approved from its detail page; this sets the status to cancelled. (Requests already denied or cancelled cannot be cancelled again.)
For administrators and approvers
Review the pending queue
- Sign in as an administrator.
- Open the pending queue at
/access-request/pending. It lists every request with status pending, newest first, showing the requester's name (fromactor_i18n). - Open a request to review the full detail at
/access-request/{id}.
Approve or deny
- Approve: submit the approve action (
POST /access-request/{id}/approve). You may include notes. The request status becomes approved, the reviewer id and timestamp are recorded, and the requester is emailed. - Deny: submit the deny action (
POST /access-request/{id}/deny). You may include a reason. The request status becomes denied, the reviewer id and timestamp are recorded, and the requester is emailed the reason.
Both actions return you to the pending queue with a confirmation message.
Browse the full history
- The browse view at
/access-request/browselists every request regardless of status, paginated 25 per page, newest first.
Manage approvers
- Open Approvers at
/access-request/approvers. - Add an approver by supplying a user id; the user is inserted into
access_request_approverand marked active. - Remove an approver by submitting the delete action against their approver row; this sets
active = 0(a soft deactivate, not a hard delete).
Active approvers with an email address receive the new-request heads-up email each time a request is submitted.
Status lifecycle
| Status | Set by | Meaning |
|---|---|---|
pending |
created on submission | Awaiting review. Appears in the pending queue. |
approved |
approve action | Access granted. Reviewer and timestamp recorded. |
denied |
deny action | Access refused. Reviewer, timestamp, and reason recorded. |
cancelled |
requester cancel | Withdrawn by the requester (from pending or approved). |
expired |
(schema-supported) | Reserved status for lapsed requests; the column allows it. |
Routes
All routes are registered under the web middleware group. The auth middleware requires a signed-in user; admin additionally requires administrator rights.
Requester routes (auth)
| Method | URI | Action | Name |
|---|---|---|---|
| GET | /access-request/new |
create |
accessRequest.create |
| POST | /access-request/new |
store |
accessRequest.store |
| GET | /access-request/my-requests |
myRequests |
accessRequest.myRequests |
| GET | /access-request/request/{slug} |
requestObject |
accessRequest.requestObject |
| GET | /access-request/{id} |
view |
accessRequest.view |
| POST | /access-request/{id}/cancel |
cancel |
accessRequest.cancel |
| POST | /access-request/request-object/create |
storeObjectRequest |
accessRequest.storeObjectRequest |
Admin / approver routes (auth + admin)
| Method | URI | Action | Name |
|---|---|---|---|
| GET | /access-request/browse |
browse |
accessRequest.browse |
| GET | /access-request/pending |
pending |
accessRequest.pending |
| POST | /access-request/{id}/approve |
approve |
accessRequest.approve |
| POST | /access-request/{id}/deny |
deny |
accessRequest.deny |
| GET | /access-request/approvers |
approvers |
accessRequest.approvers |
| POST | /access-request/approvers/add |
addApprover |
accessRequest.addApprover |
| DELETE | /access-request/approvers/{id} |
removeApprover |
accessRequest.removeApprover |
Legacy redirects and aliases
For compatibility with the earlier plugin URLs, these paths are still served:
/admin/accessRequests-> redirects to/security/access-requests(301)./accessRequest,/security/request,/security/requests-> redirect to the pending queue (301)./security/request-access,/security/request-object-> redirect to the new-request form (301)./security/request-access/create,/security/request-object/create-> map tostoreandstoreObjectRequest./security/request/{id},/security/request/{id}/review-> map to the detail/review view./security/request/{id}/approve,/security/request/{id}/deny,/security/request/{id}/cancel-> map to the matching actions./security/approvers,/security/approvers/add,/security/approvers/{id}/remove-> map to the approver-management actions.
Views
| View | Used by | Purpose |
|---|---|---|
new.blade.php |
create |
General access-request form. |
request-object.blade.php |
requestObject |
Object-specific request form (shows the record slug). |
my-requests.blade.php |
myRequests |
The signed-in user's request list. |
pending.blade.php |
pending |
Approver queue of pending requests. |
browse.blade.php |
browse |
Full paginated list of all requests. |
view.blade.php |
view |
Single-request detail page. |
approvers.blade.php |
approvers |
Approver management screen. |
partials/_request-button.blade.php |
embedded | The request-access button shown on records. |
emails/submitted.blade.php |
submission | Acknowledgement to the requester. |
emails/pending.blade.php |
submission | Heads-up to each active approver. |
emails/approved.blade.php |
approval | Approval notice to the requester. |
emails/denied.blade.php |
denial | Denial notice (with reason) to the requester. |
The forms extend the central theme::layouts.1col layout and use Bootstrap 5 cards, badges, and the site theme colour (--ahg-primary), so they match the rest of the admin UI.
Data model
The module reads and writes the following tables (created by database/install.sql, all idempotent CREATE TABLE IF NOT EXISTS).
security_access_request
The table new requests are written to (createRequest). Key columns:
| Column | Notes |
|---|---|
user_id |
The requester. |
request_type |
e.g. view, download, print, clearance_upgrade, compartment_access, renewal. |
classification_id |
Requested classification (nullable). |
object_id |
Target object (nullable). |
justification |
Combined subject, description, and justification text. |
priority |
normal, urgent, immediate (mapped from the form's urgency). |
status |
pending, approved, denied, expired, cancelled. |
reviewed_by, reviewed_at, review_notes |
Review outcome. |
access_granted_until |
Optional expiry of granted access. |
access_request
The legacy request table. The review actions (approveRequest, denyRequest, cancelRequest) and the list/browse queries operate on this table. Columns include request_type, scope_type, requested_classification_id, current_classification_id, reason, justification, urgency, status, reviewed_by, reviewed_at, review_notes, and expires_at. New submissions are routed to security_access_request; access_request is retained for existing data and the review workflow.
access_request_approver
Designated approvers. Columns: user_id (unique), min_classification_level, max_classification_level, email_notifications, active. Removing an approver sets active = 0.
Supporting tables
access_request_log- audit trail of request actions (created, updated, approved, denied, cancelled, expired, escalated) with actor and IP.access_request_justification- justification text history, optionally tied to a template.access_request_scope- which objects a request covers (information_object, repository, actor) and whether descendants are included.
Classification levels themselves come from the security_classification table, which the new-request form reads (ordered by level) to populate its optional level selector.
Configuration
Email notifications
All four mailers are controlled by a single setting:
- Setting key:
access_request_email_notifications - Default: on (true) - a fresh install sends notifications without any operator action.
- Where: the email settings page at
/admin/ahgSettings/email.
When the toggle is off, every mailer no-ops silently. Mail delivery is wrapped so that a send failure is logged ([access-request] mail send failed) but never rolls back the request, approval, or denial that triggered it. Outbound mail is queued, so a queue worker must be running for messages to leave the server.
The four notifications:
| Trigger | Recipient | Mailable |
|---|---|---|
| Request submitted | Requester | AccessRequestSubmittedMail |
| Request submitted | Each active approver with an email | AccessRequestPendingMail |
| Request approved | Requester | AccessRequestApprovedMail |
| Request denied | Requester | AccessRequestDeniedMail |
The requester's display name on the approver heads-up is taken from actor_i18n.authorized_form_of_name (English culture), falling back to the user's email, then to User #id.
Classification levels
The optional classification selector on the new-request form only appears when the security_classification table contains rows. Define your site's levels there (code, name, level) to expose them in the form.
Access control
Requester routes require auth; review, browse, pending, and approver routes additionally require the admin middleware. There is no separate granular permission layer in this module beyond the admin gate and the designated-approver list.
Notes and current behaviour
- New submissions write to
security_access_request, while the review and listing actions read fromaccess_request. Operators integrating reporting should be aware these are two tables; the module retains both for backward compatibility with the earlier plugin's data. - Cancellation is restricted to the requester's own requests and only from the
pendingorapprovedstates. - Removing an approver is a soft deactivate (
active = 0), so historical attribution is preserved.
References
- Source package:
packages/ahg-access-request/ - Ported from the AtoM
ahgAccessRequestPluginon 2026-04-30 (Phase 1 standalone install). - Related guide:
docs/help/access-requests-user-guide.md(the broader researcher-portal access workflow). - Issue: GH #539