# `MishkaCmsCore.Accounts.Validations.GrantWithinActorScope`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/accounts/__validations/grant_within_actor_scope.ex#L5)

Refuses a permission grant that would hand out more than the granting actor holds.

What is handed out is what the account does not hold yet: a permission it already holds, sent
back as the account form does, is no grant, whoever holds it.

This is what lets a site run itself. A tenant admin holding `admin:dashboard:permission` and
`admin:dashboard:blog:*` can make a colleague a blog editor without a master being involved — which
is the point, because a CMS with many sites and many managers cannot route every routine grant
through one person. What it cannot do is reach past its own ceiling: it does not hold `*` or
`admin:dashboard:document:*`, so it cannot grant them.

The ceiling is the same wildcard rule every gate uses
(`MishkaCmsCore.Accounts.Checks.AccessPermissions.covers?/2`), so `admin:dashboard:blog:*` covers
`admin:dashboard:blog:post`, and nothing but `*` covers `*`. A tenant holding `*` — a site owner —
may grant anything, and that is still contained: the row policy keeps them to their own site's
users, and `require_master` gates master actions on `site_id`, never on permissions. Their `*` is
site-wide, not system-wide.

A MASTER (a user whose `site_id` is nil) has a ceiling too: what it holds. A master holding `*`
grants anything; one holding only `admin:dashboard:permission` grants nothing it does not hold,
itself included. A call made with `authorize?: false` (seeds, the installer) is a system call and
has none. Anything else with no actor is refused: a missing actor is nobody, not a master.

Declare it with `before_action?: true`, on an update, a create or a generic action taking
`permission_ids`. The ceiling is then asked right before the write, of the actor the action RUNS
with — the actor the policies ask — and not of the one the changeset was built with. A grant built
by a master, by a system call or with no actor, and run by a site's admin, is held to that admin's
ceiling.

    update :assign_permissions do
      validate {MishkaCmsCore.Accounts.Validations.GrantWithinActorScope, []},
        before_action?: true
    end

Escalation is closed from both directions only in combination with the policy that forbids editing
your own permissions — otherwise two colleagues could raise each other. See
`MishkaCmsCore.Accounts.User`'s `assign_permissions` policy.

---

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