# `MishkaCmsCore.Runtime.Resources.Validations.TenantContext`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/resources/__validations/tenant_context.ex#L5)

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.

# `init`

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?`

```elixir
@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`

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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
