Unreleased changes
These pages document main, which has changes 0.1.2 does not. npm install @evanion/acl gives you 0.1.2, documented here. Values on these pages are produced by running the current source on every build.
Explore a document you have
The matrix explorer reads a matrix you paste, such as the
text JSON.stringify(access.matrix) prints for your own policy, and draws what
it declares and what it decides for one subject.
Open the matrix explorer
The document the acl matrix explorer reads
The explorer takes the envelope access.matrix holds: an optional version,
schema and maxStale, and the flat permissions list. It adopts the pasted
text with parseMatrix, the function a service calls on a matrix another
service published, and parseMatrix refuses a bare permission list as
InvalidMatrixError:
import { , } from '@evanion/acl';
type = { : string };
const = <{ : string }, { : }>({
: 3,
: { : { : { : { : 'string' } } } },
})
.('question', () =>
.('update', .('object.askedBy', 'subject.id')),
)
.();
const = .(.);
; // -> '{"version":3,"schema":{"objects":{"question":{"fields":{"askedBy":"string"}}}},"permissions":[{"key":"question.update","object":"question","action":"update","rules":[{"when":[{"field":"object.askedBy","op":"eq","path":"subject.id"}]}]}]}'
const = (.());
.({ : 's1' }, 'question', 'update', { : 's1' }).; // -> true
// The permission list on its own is no document.
let = '';
try {
(.().permissions);
} catch () {
= ( as Error).;
}
; // -> 'InvalidMatrixError'Where the acl matrix explorer runs
This site is a static export. There is no server behind these pages, and
@evanion/acl imports no framework and evaluates locally, so a document pasted
into the explorer is read by JavaScript in your own browser and has nowhere to
be uploaded to. The tool’s own toolbar says the same thing, because that is
where somebody who arrived at it directly is looking.
The tool takes a paste or a file you pick, and reads nothing from the URL,
because a document reachable at ?matrix=https://… is a link a third party can
send. Nobody can share a matrix by pasting a link, so to share one, send the
document.
A pasted document is also invisible to this site’s search. Search indexes the HTML the build emitted, and your document reaches the screen after that.
What the acl matrix explorer draws
The explorer loads the document with parseMatrix, the
function a service calls on a matrix another service published, because a
pasted document is foreign input: a key it does not carry answers
unknown-action rather than throwing. What the screen shows is what that
service would get from the same document.
The left pane holds what you brought: the document, the subject as JSON, and one row box per object kind the document reads. The right pane holds three reports, and each is either the document’s own words or one call into the package.
Decisions comes first, because it is what moves when an input changes. Its
asked column says which call answered each row: capabilities for the one
pass over the whole document, can for a permission that reads the object and
has a row to read. What it declares is version, maxStale, the schema’s
object kinds and how many permissions carry visibility: 'public', which is the
set serialize(access, 'reduced') would keep. What each
permission says is its allow rules, its deny rules and its field rules, with
needs the row on every permission access.readsObject answers true for.
What the acl explorer’s sample document answers
The sample the tool offers is a shop matrix over listing and order. Against
the seller { "id": "u_31", "roles": ["seller"] } and no row at all it answers
allow for listing.read, no-rule-matched for order.refund, and
unevaluable for listing.update with missing naming object.status and
object.sellerId. That third answer is the one to watch, because entering
{ "sellerId": "u_31", "status": "live" } in the listing box turns it into
allow, and { "sellerId": "u_31", "status": "archived" } turns it into
denied on the rule named archived.
How the acl matrix explorer asks about an object
A permission whose rules read no object.* path decides the same with a row and
without one, so the explorer asks for a row only where access.readsObject
answers true, and it asks once per object kind rather than offering one box and
a picker for which kind it holds. The screen’s answer is a table with every
permission in it, and a picker would leave half of it stale: listing.update
decided against the row in hand while order.refund still carries the answer it
gives with none.
Each box opens empty and carries a control that fills it. For the sample that is
a listing the subject above owns, which turns listing.update from
unevaluable into allow; from there, changing status to "archived"
reaches denied and changing sellerId reaches no-rule-matched. For a
document you brought it is one empty value per field
schema.objects[kind].fields declares. For a kind the schema declares no fields
for, which is every kind in a document with no schema, the control is disabled
and says so, because a row built from the paths a rule happens to read would be
the tool guessing at a shape the document declined to state. What lands in the
box is editable text, so nothing the control does is a state you cannot also
type.
The three states of an acl explorer row
A row is optional, and the three states it has are all worth reaching:
- An empty box is no row, and every permission that reads the object answers
unevaluablewithmissingnaming every path its rules read. - A row missing a key the rules read answers
unevaluableas well, andmissingnames only what is short.{ "sellerId": "u_31" }against the sample leavesobject.status. - A key holding an empty value is a value.
{ "sellerId": "", "status": "live" }answersno-rule-matched, because the comparison ran and failed.
An absent path and an empty string are different answers, and the box is where you tell them apart. Why was this refused? has the rest of that distinction.
Why the acl explorer reads no relations
The tool reads no relations. ObjectSchema carries it beside fields, and it
names the kinds one kind points at for the consumers that resolve them. A
condition compares one field of one scope, so no condition can name a relation,
and a box for one would take a value nothing evaluates.
What the acl matrix explorer does with a malformed document
Two failures, and your next move differs.
Text that is not JSON never reaches the package. The screen says so and quotes
what JSON.parse reported, and no rule has been read at that point.
A document that parsed and that the package refused raises an
AclConfigError, and the screen renders the class, the message
and the three properties that locate the value. A condition naming
object.seller_id against a schema declaring sellerId gives this:
| What is shown | What it holds |
|---|---|
| class | UnknownFieldError |
| message | permission “listing.update”: condition rules[0].when[0] on “object.seller_id” names a field the schema does not declare |
key | listing.update |
field | object.seller_id |
where | rules[0].when[0] |
where is a position inside the permission, so a document with forty rules
still names the one condition to change. What can throw? carries
every class with the input that raises each one.
A malformed row is reported the way a malformed document is, with the message
JSON.parse gave, and the row is not used: the permission stays on the answer
it gives with no row, rather than being decided against half of one.
What the acl matrix explorer leaves out
Two things you might expect, each left out for a reason.
The field axis. canFields needs a proposed write as well as a row, and it
answers a different question: which fields of this row may be written, rather
than whether the action is allowed at all. That is a third box for a question
Which fields may they write? teaches against a scenario built
for it.
A diff between two documents. diffMatrix is a second document, a second
adoption and a second refusal path on a screen that has one of each today.
What did editing my rules change about access? has it.
Where to go next
- The matrix explorer. The tool itself, on a screen of its own.
- How do I use rules another service published?. The same
parseMatrixadoption, in a service rather than a browser. - What may this user do at all?. The call the subject box makes, and what its answers mean.
- Why was this refused?. Reading
reasonandmissingoff a row of the decision table.