Skip to content

Consult the host's mail policy on preview and attachment routes - #87

Open
MACscr wants to merge 1 commit into
backstagephp:mainfrom
MACscr:per-record-mail-authorization
Open

Consult the host's mail policy on preview and attachment routes#87
MACscr wants to merge 1 commit into
backstagephp:mainfrom
MACscr:per-record-mail-authorization

Conversation

@MACscr

@MACscr MACscr commented Aug 26, 2026

Copy link
Copy Markdown

Problem

#84 / #86 put the preview and attachment routes behind Authenticate and canManageMails(). That answers "may this user use the mail log at all?", which is the same question every Resource, Page and Widget asks — but the controllers still resolve the mail by id and serve it:

$mail = $mailModel::findOrFail($request->route('mail'));

return response($mail->html, …);

Hosts that need a second answer — "may this user see this mail?" — have no hook. In a multi-tenant application every user who may open the mail log can read any tenant's mail, and download its attachments, by changing the id in the URL. Wrapping the routes in extra middleware from the host is possible but means re-declaring Mails::routes() and its middleware by hand, which is exactly the class of drift #84 was written to prevent.

Fix

When the host has registered a policy for the mail model, both controllers authorize the view ability against the requested mail before serving it:

if (Gate::getPolicyFor($mail) !== null) {
    Gate::authorize('view', $mail);
}
  • Hosts without a policy are unaffected — Gate::getPolicyFor() returns null and nothing changes.
  • Hosts with a policy get the same per-record rule on the raw routes that they already apply in their own resources.
  • canManageMails() keeps its role as the coarse gate; this runs after it, inside the same middleware stack.

The README section on canManageMails() now documents the policy hook with a tenant example.

Tests

Two new cases in tests/MailRouteSecurityTest.php using a DenyOddMailPolicy fixture: the preview is forbidden for a denied mail and served for an allowed one; an attachment download is forbidden for a denied mail. Full suite: 16 passed (39 assertions).

canManageMails() answers whether a user may use the mail log at all. Hosts
that need a per-mail answer as well - a multi-tenant application where a
user may only open mails belonging to their own tenant - have no hook: both
controllers resolve the mail by id and serve it.

When the host has registered a policy for the mail model, authorize the
view ability against the requested mail before serving the preview or an
attachment. Hosts without a policy are unaffected.
@Casmo

Casmo commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

What is the DenyOddMailPolicy class testing exactly? There is no Gate check for the $user.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants