Skip to Content
AuthorizationExplore a document you have

Explore a document you have

@evanion/acl 0.1.0 and its patch releases up to 0.1.2, the version on npm. Values on this page were produced by running the source at 40b8f84, a later commit than the 0.1.0 tag, and are not re-executed.

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 unevaluable with missing naming every path its rules read.
  • A row missing a key the rules read answers unevaluable as well, and missing names only what is short. { "sellerId": "u_31" } against the sample leaves object.status.
  • A key holding an empty value is a value. { "sellerId": "", "status": "live" } answers no-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 shownWhat it holds
classUnknownFieldError
messagepermission “listing.update”: condition rules[0].when[0] on “object.seller_id” names a field the schema does not declare
keylisting.update
fieldobject.seller_id
whererules[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

Last updated on