-
Notifications
You must be signed in to change notification settings - Fork 2
Specs M5 Trust Center Document Request
trust_center.models.document_request.DocumentRequest
An external request for a gated Trust Center document. A visitor who wants a gated artifact (typically an NDA-restricted audit report or SOC 2 report) submits a short form; a curator reviews it; on approval the system issues a time-limited, signed download link so the requester can fetch the document without an account. This keeps sensitive artifacts off the open page while still letting prospects self-serve through an auditable approval step.
File: trust_center/models/document_request.py
BaseModel subclass : UUID PK, sequential reference (prefix DREQ, e.g. DREQ-1), django-simple-history audit trail, and the trust_center_document_request lifecycle workflow. Its workflow_state overrides the BaseModel default to start in pending.
| Field | Type | Constraints | Description |
|---|---|---|---|
id |
UUID | PK, auto-generated | Unique identifier |
reference |
string | auto DREQ-N, unique |
Business reference |
document |
relation | FK -> TrustCenterDocument, PROTECT, required |
The gated document being requested. related_name="requests". |
email |
required | Requester email (where the signed link is sent) | |
requester_name |
string | required, max 255 | Requester name |
company |
string | optional, max 255, blank default | Requester organization |
reason |
text | optional, blank default | Why access is requested (free text) |
nda_accepted |
boolean | required, default False
|
Whether the requester accepted the NDA terms. Required on submission when the document's requires_nda is on. |
nda_accepted_at |
datetime | optional | When the requester accepted the NDA (stamped on submission if nda_accepted). |
ip_address |
IP | optional | Requester IP captured at submission (audit / abuse tracing). |
user_agent |
text | optional, blank default | Requester user agent captured at submission (truncated). |
workflow_state |
string | indexed, default pending
|
Lifecycle state (trust_center_document_request) |
reviewed_by |
relation | FK -> User, SET_NULL, optional |
Curator who approved / rejected the request. related_name="reviewed_document_requests". |
reviewed_at |
datetime | optional | When the request was approved / rejected |
decision_note |
text | optional, blank default | Curator note recorded with the decision (the rejection / revoke comment). |
download_token_issued_at |
datetime | optional | When the signed download link was issued (set on approval). |
download_link_expires_at |
datetime | optional | Indicative expiry of the issued link (issue time + TRUST_CENTER_DOWNLOAD_TTL). The hard expiry is enforced by the signed-token max age, not this field. |
download_count |
int |
PositiveIntegerField, default 0
|
How many times the gated link has been used to stream the document. |
created_at / updated_at
|
datetime | auto | Timestamps |
Runs the dedicated trust_center_document_request workflow (declared from transition constants in trust_center/constants.py and registered from TrustCenterConfig.ready()):
pending ─► approved ─► rejected (revoke access)
│
└─► rejected (decline)
| State | In reports | Deletable | Branch | Description |
|---|---|---|---|---|
pending (initial) |
no | yes | no | Submitted, awaiting curator review |
approved |
yes | no | no | Approved; a signed, time-limited download link has been issued |
rejected (terminal) |
no | no | yes | Declined by a curator, or a previously approved request whose access was revoked |
There is no separate expired or revoked state. Revoking a granted request reuses the approved -> rejected transition, and link expiry is enforced purely by the signed-token max age (TRUST_CENTER_DOWNLOAD_TTL), not a workflow state. Badge tones are warning (pending), success (approved), danger (rejected).
| Verb | Transition | Permission action | Comment |
|---|---|---|---|
| Approve | pending -> approved |
approve |
optional |
| Reject | pending -> rejected |
approve |
required (requires_comment) |
| Revoke access | approved -> rejected |
approve |
required (requires_comment) |
is_granted is a convenience property returning True only while the request is in the approved state; the gated download view uses it so a revoke (which moves the request back to rejected) kills the link immediately, even before the token's TTL elapses.
The token is a django.core.signing.TimestampSigner signature (salt "trust_center.document_request.download") over the request UUID. It is never stored : it is recomputed on approval and verified statelessly on each fetch. Helpers on the model:
-
make_download_token(): sign the request PK and return the token. -
resolve_token(token, max_age)(classmethod) : verify signature and age, returning the matching request (orNone). It propagatessigning.SignatureExpired/signing.BadSignatureso the caller can distinguish an expired link from a tampered one. -
issue_download_link(ttl_seconds): stampdownload_token_issued_at/download_link_expires_atand return a fresh signed token.
On approval (via the management stepper or MCP), issue_download_link(TRUST_CENTER_DOWNLOAD_TTL) is called and the resulting URL is emailed to the requester, so:
- the document bytes are still streamed through a view (never exposed under
/media/), - the link stops working after the TTL (
resolve_tokenraisesSignatureExpired, and the gated download view renders a 410 "link expired" page), - a curator can revoke access before expiry (the
approved -> rejectedtransition flipsis_grantedto false, and the view 404s the link).
- A visitor opens the public request form at
/trust/documents/<uuid>/request/(DocumentRequestCreateView+PublicDocumentRequestForm). The form has a honeypot field, requires NDA acceptance when the document'srequires_ndais on, and the view applies a cache-based per-IP rate limit and de-duplicates pending requests per(email, document)(returning the same confirmation either way, so it never leaks which emails already requested access). - On submit, a
pendingDocumentRequestis created, the IP and user agent are captured, andaccounts.notifications.notify_document_requestedfires an in-app notification plus an email (NotificationType.TRUST_DOCUMENT_REQUESTED) to the holders oftrust_center.document_request.approve. - A curator reviews the request on the 2-column detail page at
/trust-center/manage/requests/<uuid>/and acts through the generic workflow stepper. The bespoke transition endpoint (trust_center_manage:request-transition) stampsreviewed_by/reviewed_at(anddecision_notefrom the comment); on approve it callsissue_download_linkand emails the requester viatrust_center/notifications.pysend_gated_link_email. - The requester fetches the document at
/trust/documents/download/<token>/(TrustCenterGatedDownloadView): the view validates theTimestampSignertoken (404 onBadSignature, a 410 "link expired" page onSignatureExpired), requires the request to still be in theapprovedstate (so a revoke kills the link even before expiry), incrementsdownload_count, and streams the bytes.
| ID | Rule |
|---|---|
| RG-TC-22 | A DocumentRequest may only target a document whose access = gated. Public documents are downloaded directly and need no request (the public request form 404s a non-gated document). |
| RG-TC-23 | When the target document's requires_nda is on, NDA acceptance is required to submit the request (nda_accepted is stamped with nda_accepted_at). |
| RG-TC-24 | Approval issues a signed, time-limited link (TRUST_CENTER_DOWNLOAD_TTL); the bytes are streamed through a view, never via /media/. Expiry is enforced by the signed-token max age; revocation reuses approved -> rejected. |
| RG-TC-25 | A new request submission notifies the holders of trust_center.document_request.approve (in-app + email); an approval emails the requester the signed link. |
-
GET/POST /trust/documents/<uuid>/request/: the public request form for a gated document (DocumentRequestCreateView, unauthenticated, honeypot + per-IP rate limiting + pending dedupe; 404s when the global switch is off or the document is not gated). -
GET /trust/documents/download/<token>/: fetch an approved gated document via the signed link (TrustCenterGatedDownloadView; signature + TTL verified,approvedstate required,download_countincremented).
There is no management REST viewset for requests : review happens through the curation UI (the workflow stepper) and through MCP. The detail / transition surface lives under /trust-center/manage/requests/<uuid>/ (web UI).
-
list_trust_center_document_requests: list requests (optionalworkflow_statefilter). Requirestrust_center.document_request.read. -
get_trust_center_document_request: read one request. Requirestrust_center.document_request.read. -
approve_trust_center_document_request: approve a request : issues the time-limited signed link and emails it to the requester. Requirestrust_center.document_request.approve. -
reject_trust_center_document_request: reject a pending request, or revoke access for an approved one (comment required). Requirestrust_center.document_request.approve.
There is no create / update / delete via MCP : requests originate from the public form, not from authenticated clients.
| Codename | Description |
|---|---|
trust_center.document_request.read |
List / read gated-document requests |
trust_center.document_request.approve |
Approve / reject / revoke a request |
trust_center.document_request.delete |
Delete a request |
These three actions are present in PERMISSION_REGISTRY and are assigned to Super Admin / Admin (all), RSSI/DPO and Contributeur (read; approve for RSSI/DPO), Auditeur / Lecteur (read only), per accounts/migrations/0042_add_trust_center_permissions.py. There is no create action : requests are created by anonymous visitors through the public form, not by authenticated users.
-
TrustCenterDocument : the gated source of a request (
access = gated,requires_nda). -
README.md : §6.3 (no raw file exposure), §8 (notifications), §9.2 (
TRUST_CENTER_DOWNLOAD_TTL). - governance/workflow.md : the lifecycle framework this workflow plugs into.
Built from docs/ at v0.36.0. Edits made here are overwritten by the next release : open a pull request against the source instead.
- Administration
- Ask Cairn
- Assets and suppliers
- Compliance
- The dashboard
- Finding your way
- Getting started
- Incidents
- How records move
- Organisational context
- Reports and management review
- Risks
- Trust Center
- Architecture
- Configuration
- Contributing
- The documentation system
- Installation
- Internationalisation
- Operations
- Release process
- Security
- Testing
- Adding an assistant provider
- Adding a dashboard widget
- Adding a domain entity
- Declaring a lifecycle
- Adding an MCP tool
- Adding a REST endpoint
- Adding a report
- Interface conventions
- Dashboard widgets
- Lifecycles
- MCP tools
- MCP tool parameters : Assets
- MCP tool parameters : Compliance
- MCP tool parameters : Governance and context
- MCP tool parameters : General
- MCP tool parameters : Incidents
- MCP tool parameters : Reports and management review
- MCP tool parameters : Risks
- MCP tool parameters : System and administration
- MCP tool parameters : Trust Center
- Management commands
- Models
- Permissions
- REST endpoints
- Environment variables
- MCP server
- REST API
- Assistant module (Ask Cairn)
- Module 0: User Management and Access Control
- Module 1: Context and Organization
- Module 2: Asset Management
- Module 3: Compliance
- Module 4: Risk Management
- Module 4 bis - EBIOS Risk Manager
- Module 5 : Trust Center
- Module 6 : Security Incident Management
- Management review : ISO 27001:2022 compliance (clause 9.3)