could-could is yet another authorization library built atop JsonLogic, a JSON-based schema and library that allows for conditional statements to be defined and evaluated against a context. This allows for more complex rules based on who the principal (e.g. user/service) is, what state a resource is in (e.g. block an action if the resource is in a closed
state), or a combination of factors. The same policy can then be consumed in frontend apps as well as backend apps, potentially in any language supported by JsonLogic.
- Generates the set of policies ahead of time for predictable performance
- Provides for both
allow
anddeny
constraints.deny
constraints will always take precedence - Allows for a context to be provided when evaluating the policy, allowing for more complex conditional logic
- Uses a portable JSONSchema for validating the shape of the policies
- Can extend JsonLogic with custom functions if needed (not recommended as you will need to implement the same functions in each consumer of the policy)
action
: patterns to match for action(s) that could be requested (e.g.create
,kitties:PetKitty
)- Patterns can be specified as a single string or an array of strings
- Patterns can be exact matches, match all actions (
'*'
), or starts with/ends with using'*'
as a glob operator
effect
: what the result should be if theconstraint
is trueconstraint
: the rules that will be evaluated to determine if the effect should applycontext
: the extra data that can be provided to make decisions, such as a principal (e.g. user/service object) or the resource in question
You will need a few pieces of information up front when creating a collection of policies:
- a list of actions that are permitted for each resource type. All other actions will evaluate to
false
- TIP: use a namespacing scheme such as
documents:deleteDocument
to group actions by domain
- what information will be available in the context when evaluating an action
Next, in the consuming application create a resolver and make it available for use:
import { PolicyResolver } from '@freakyfelt/could-could'
const resolver = PolicyResolver.fromDocuments(policies)
// ...then at runtime
function petKitty(kitty: Kitty, { subject }: RequestContext) {
if (!resolver.can('kitty:pet', { kitty, subject })) {
throw new NotAuthorizedError()
}
}
JsonLogic supports adding custom boolean evaluators by using JsonLogic.add_operation()
. Unfortunately the library is a singleton, meaning you cannot create isolated instances of JsonLogic with their own operations.
This library is mostly a proof of concept, but if you find it useful definitely fork and submit a PR against this repository. The project uses prettier
and jest
for linting and testing respectively. You can run tests, linting, and build by using npm run release
.
The package is broken into four major areas: the validator, the parser, the store, and the resolver.
- the validator checks that the provided policy document matches the schema and has an evaluatable set of constraints
- the parser does the heavy lifting of turning a policy statement into the internal shape ("ParsedPolicyStatement")
- the store handles storing, indexing, and finding parsed statements matching a given action
- the resolver handles the runtime evaluation logic based on the provided action and context