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
allowanddenyconstraints.denyconstraints 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 theconstraintis 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:deleteDocumentto 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