MishkaCmsCore.Accounts.Permission (Mishka CMS Core v0.0.1)

Copy Markdown View Source

Defines system permissions for role-based access control.

This resource represents individual permissions that can be granted to users, forming the basis of the authorization system. An admin permission is named admin:<surface>:<area> (e.g. admin:dashboard:runtime:page, admin:api:blog:post), written as a plain string where it is used. A * as the whole last segment covers everything below it (admin:dashboard:*, admin:*), and * alone covers everything. See MishkaCmsCore.Accounts.Checks.AccessPermissions.

Each permission has a unique name and optional description explaining its purpose. Permissions are assigned to users through the UserPermission join table, creating a many-to-many relationship that allows users to have multiple permissions and permissions to be shared across multiple users.

Access to manage permissions is restricted through policies to users who already have administrative privileges, ensuring that only authorized actors can modify the permission structure. The unique constraint on permission names prevents duplicate permissions and maintains consistency in the authorization system.

Access Control

  • Masters (site_id nil) holding admin:<surface>:permission: full CRUD access
  • Site users holding admin:<surface>:permission: read-only access
  • Anyone else: no access, except a user's own permissions read through its account

Summary

Functions

Apply the full GuardedStruct pipeline (sanitize → validate → derive → main_validator) to attrs and return either {:ok, transformed_attrs} or {:error, errors}. Wire this into an Ash.Resource.Change to plug guardedstruct rules into Ash's changeset pipeline.

O(1) lookup of a field's compile-time metadata by name. Returns nil for unknown fields.

Compile-time-baked MapSet of every field name owned by the guardedstruct block. Used by GuardedStruct.AshResource.Change to decide, in O(1), whether a key in changeset.atomics belongs to the pipeline.

Validates that the keys in the provided input are valid for at least one action on the resource.

Same as input/1, except restricts the keys to values accepted by the action provided.

Types

t()

@type t() :: %MishkaCmsCore.Accounts.Permission{
  __lateral_join_source__: term(),
  __meta__: term(),
  __metadata__: term(),
  __order__: term(),
  aggregates: term(),
  calculations: term(),
  created_at: term(),
  description: term(),
  id: term(),
  name: term(),
  updated_at: term(),
  users: term(),
  users_join_assoc: term()
}

Functions

__guarded_change__(attrs, error? \\ false)

Apply the full GuardedStruct pipeline (sanitize → validate → derive → main_validator) to attrs and return either {:ok, transformed_attrs} or {:error, errors}. Wire this into an Ash.Resource.Change to plug guardedstruct rules into Ash's changeset pipeline.

The function is named __guarded_change__ because it does more than validate — it can also transform values (trim, downcase, slugify, auto-fill, etc.) before they reach the data layer.

__guarded_field_meta__(name)

O(1) lookup of a field's compile-time metadata by name. Returns nil for unknown fields.

__guarded_field_name_set__()

Compile-time-baked MapSet of every field name owned by the guardedstruct block. Used by GuardedStruct.AshResource.Change to decide, in O(1), whether a key in changeset.atomics belongs to the pipeline.

default_short_name()

input(opts)

@spec input(values :: map() | Keyword.t()) :: map() | no_return()

Validates that the keys in the provided input are valid for at least one action on the resource.

Raises a KeyError error at compile time if not. This exists because generally a struct should only ever be created by Ash as a result of a successful action. You should not be creating records manually in code, e.g %MyResource{value: 1, value: 2}. Generally that is fine, but often with embedded resources it is nice to be able to validate the keys that are being provided, e.g

Resource
|> Ash.Changeset.for_create(:create, %{embedded: EmbeddedResource.input(foo: 1, bar: 2)})
|> Ash.create()

input(opts, action)

@spec input(values :: map() | Keyword.t(), action :: atom()) :: map() | no_return()

Same as input/1, except restricts the keys to values accepted by the action provided.

json_api_match_route(_, _)

primary_key_matches?(left, right)