Mediate (mediate v0.1.0)

Copy Markdown View Source

The port: the one place an application asks whether a subject can perform an operation on an object. It is also the contract every adapter implements. The words are NIST SP 800-162's: subject, object, operation, and environment.

  • authorize/4 when the call reaches the repo: it answers the decision the seam accepts under mediate:.
  • check/4 when a branch needs a yes or no and reaches no repo.
  • scope/4 when a query covers a whole type: it answers a dynamic the query carries, which can only narrow, and a decision whose object id is nil.
  • review/5 when a reviewer asks who can do what today: one scope per subject under the reviewer's operation id, with nil ids alike.

This module is the top-layer boundary. Everything under Mediate that is not Mediate.Test belongs to it. It can reach Ecto and NimbleOptions. It reaches nothing from ecto_sql or postgrex, because the port decides and does not query.

Summary

Types

Under what conditions the subject asks: the facts only the caller knows, by name, with now from the configured clock beside them. The port stamps now, so a decider reads the moment of the request from the environment rather than from a clock of its own.

What the subject asks about: an object type and an id. A decision over a whole type, which scope/4 makes, carries nil for the id.

Who asks: a kind and an account id. The kind travels with every decision record. The port refuses a kind it does not know.

A person, software that acts alone, or a person who can change the system.

Functions

Decide for one object: the decision to hand the seam, or why not.

The verdict alone for one object.

The review a reviewer asks: a rule and a decision per subject over an object type.

The rule a row must satisfy and the decision the query carries.

Types

environment()

@type environment() :: %{:now => DateTime.t(), optional(atom()) => term()}

Under what conditions the subject asks: the facts only the caller knows, by name, with now from the configured clock beside them. The port stamps now, so a decider reads the moment of the request from the environment rather than from a clock of its own.

object()

@type object() :: {atom(), Mediate.Id.t() | nil}

What the subject asks about: an object type and an id. A decision over a whole type, which scope/4 makes, carries nil for the id.

subject()

@type subject() :: {subject_kind(), Mediate.Id.t()}

Who asks: a kind and an account id. The kind travels with every decision record. The port refuses a kind it does not know.

subject_kind()

@type subject_kind() :: :user | :non_person_entity | :privileged

A person, software that acts alone, or a person who can change the system.

Functions

authorize(subject, operation, object, opts \\ [])

@spec authorize(subject(), atom(), object(), Mediate.Port.options()) ::
  {:ok, Mediate.Decision.t()} | {:error, Mediate.Error.t()}

Decide for one object: the decision to hand the seam, or why not.

check(subject, operation, object, opts \\ [])

@spec check(subject(), atom(), object(), Mediate.Port.options()) :: boolean()

The verdict alone for one object.

review(reviewer, subjects, operation, object_type, opts \\ [])

@spec review(subject(), [subject()], atom(), atom(), Mediate.Port.options()) ::
  Mediate.Port.reviewed()

The review a reviewer asks: a rule and a decision per subject over an object type.

scope(subject, operation, object_type, opts \\ [])

The rule a row must satisfy and the decision the query carries.