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

Copy Markdown View Source

Holds an action to the kind of caller it was written for: a master, or a site.

A MASTER is a user whose site_id is nil (MishkaCmsCore.Accounts.Helper.master_user?/1). Nothing else makes one: not a missing actor, not an actor with no site_id, not a permission (* included), not the absence of a tenant.

Options

  • :require_master - Required boolean.
    • true: the action is a master's. The actor must be a master and the tenant must be nil.
    • false: the action is a site's. A tenant must be given. A site user may give only its own site as the tenant; a master may give any site; a visitor with no actor acts on the site it is visiting.

A call made with authorize?: false is a system call (the installer, seeds, workers, internal reads) and is not checked.

The check reads the actor the changeset, query or action input was BUILT with. Build it with the actor that runs it: one built with no actor is refused a master action, whoever runs it later. Who may run an action is still the resource's policies' question; this only keeps each action to its own kind of caller. The policies ask the actor the action RUNS with, and MishkaCmsCore.Accounts.Checks.AccessPermissions gives a site user none of its permissions under another site's tenant or for a master's action (requires_master?/2), so an input built with another actor, or with none, still keeps a site user to its own site wherever a policy authorizes through that check.

Usage

alias MishkaCmsCore.Runtime.Resources.Validations.TenantContext

create :master_create do
  validate {TenantContext, require_master: true}
end

create :create do
  validate {TenantContext, require_master: false}
end

action :master_update, :struct do
  validate {TenantContext, require_master: true}
end

Error messages

  • A master action given a tenant: "Tenant context must be nil for this action. Use the tenant action instead."
  • A master action run by anyone but a master: "This action requires a master user, an account that belongs to no site. Site users use the tenant action instead." For a site user it is an invalid-input error; for nobody (no actor, or one that is not a user) it is a forbidden error, as a policy refusal is.
  • A tenant action given no tenant: Ash's own Ash.Error.Invalid.TenantRequired, the error Ash gives any query or change that needs a tenant and has none. The JSON:API shows both as 400 tenant_required: "Tenant context is required for this action. Use the master action instead."
  • A tenant action run by a site user for another site: "A site user can act only on their own site.", a forbidden error.

Summary

Functions

Initializes the validation with provided options.

Whether action of resource is a master's: it declares this validation with require_master: true. Takes the action or its name; nil is no action.

Validates tenant context based on the action requirements.

Functions

init(opts)

Initializes the validation with provided options.

Parameters

  • opts - Keyword list containing:
    • :require_master - Boolean indicating if master context is required

Returns

  • {:ok, opts} with validated options

requires_master?(resource, name)

@spec requires_master?(Ash.Resource.t(), atom() | struct() | nil) :: boolean()

Whether action of resource is a master's: it declares this validation with require_master: true. Takes the action or its name; nil is no action.

validate(subject, opts, context)

Validates tenant context based on the action requirements.

Parameters

  • subject - The Ash changeset or action input being validated
  • opts - Validation options containing :require_master flag
  • context - Context containing tenant and authorization information

Returns

  • :ok if validation passes
  • {:error, message: String.t()} if validation fails