MishkaCmsCore.Runtime.Resources.Validations.TenantIsolation (Mishka CMS Core v0.0.1)

Copy Markdown View Source

Refuses a write that names a record of another site.

A row that points at a site's record — a bookmark at a post, a follow at a blogger, a comment at an issue — may only point at a record of the site the write is made for: the tenant, or the actor's own site when there is no tenant. A resource with no multitenancy of its own, such as MishkaBlog.PostBookmark, has nothing else that says so.

A site user writes for its own site only. Naming another site as the tenant is refused, the same way, whatever record the write names: the tenant is not the site user's to choose. A master (a user whose site_id is nil) writes for whichever site it names as the tenant, and a visitor with no actor for the site it is visiting.

A record of another site and a record that does not exist are refused with the same error, so the answer does not tell a caller which ids exist elsewhere. Each refusal is announced on runtime:tenant:cross_site_refused with %{resource: resource, field: field, id: id, tenant: tenant, actor_id: actor_id}.

A write with neither a tenant nor an actor's site passes only for a master admin: a master who is an admin of the surface the write came from (MishkaCmsCore.Accounts.Helper.master_admin?/2, with MishkaCmsCore.Accounts.Checks.AccessPermissions.surface/1). Anyone else is refused the same way — a master with no admin permission of that surface, a visitor, an actor with no site_id. Nothing is checked for a call made with authorize?: false.

Usage

validate {MishkaCmsCore.Runtime.Resources.Validations.TenantIsolation,
          field: :section_id,
          resource: MishkaDocument.Section,
          message: "Section not found or does not belong to your site."}

Options

  • :field - Required. The attribute holding the id of the record.
  • :resource - Required. The Ash resource that record belongs to. It must have a site_id.
  • :message - Optional. The error message. Defaults to "{Resource} not found or does not belong to your site (field: {field})".
  • :optional - Optional. When true, a nil id passes. Defaults to true.

The record is read with the first of :read_any, :master_read and :read the resource has, so a resource that is multitenant needs one of the first two.