Skip to content
This repository has been archived by the owner on Jul 11, 2019. It is now read-only.
/ wahn Public archive

Policy Machine for Role Based Access Control

License

Notifications You must be signed in to change notification settings

ojkelly/wahn

Repository files navigation

Wahn

Policy Machine for Role Based Access Control

View on npm npm downloads Dependencies Build Status codecov NSP StatusKnown Vulnerabilities FOSSA Status

Designed for use with the Bunjil GraphQL server, Wahn is flexible policy based authorization engine. It is inpsired by other policy engines including AWS IAM.

Getting Started

yarn add wahn

npm install wahn

Usage

// Typescript
import { Wahn, Policy, PolicyAction, PolicyOperator } from "wahn";

// Define a policy
const policy: Policy = {
    // This is a simple policy with only one resource
    resources: ["test::resource"],
    // You can either Allow or Deny access
    action: policyAction.Allow,
    // This policy is then attached to the role `authenticated user`
    roles: ["authenticated user"],
};

// Now we create a Wahn
const wahn: Wahn = new Wahn({
    policies: [policy],
});
// javascript
import { Wahn } from "wahn";

// Define a policy
const policy = {
    // This is a simple policy with only one resource
    resources: ["test::resource"],
    // You can either `Allow` or `Deny` access
    action: "Allow",
    // This policy is then attached to the role `authenticated user`
    roles: ["authenticated user"],
};

// Now we create a Wahn
const wahn = new Wahn({
    policies: [policy],
});

Now you have an Wahn instance with policies, you can attempt to access something.

// typescript
import { RequestContext } from "wahn";

// In your request you will have information about the user, their roles,
// and possibly other information about their session, like their
// OS or IP
// You pass these to `wahn` during your evaluateAccess check via the
// context object
const context: RequestContext = {
    user: {
        id: "UserId",
        roles: ["authenticated user"],
    },
};

// Now lets check if our user has access to our test resource
const hasAccess: boolean = wahn.evaluateAccess({
    context,
    resource: "test::resource",
});
// hasAccess === true

// Now lets check if our user has access to a different resource
const hasAccess: boolean = wahn.evaluateAccess({
    context,
    resource: "AResourceTheUserCannotAccess",
});
// throws AuthorizationDeniedError
//javascript
import { RequestContext } from "whan";

// In your request you will have information about the user, their roles,
// and possibly other information about their session, like their
// OS or IP
// You pass these to `wahn` during your evaluateAccess check via the
// context object
const context = {
    user: {
        id: "UserId",
        roles: ["authenticated user"],
    },
};

// Now lets check if our user has access to our test resource
const hasAccess = wahn.evaluateAccess({
    context,
    resource: "test::resource",
});
// hasAccess === true

// Now lets check if our user has access to a different resource
const hasAccess = wahn.evaluateAccess({
    context,
    resource: "AResourceTheUserCannotAccess",
});
// throws AuthorizationDeniedError

Policy Object

You can view the Policy type detailed type information, below is the plain JSON version.

{
    // A action represents the object an action is being performed on.
    // In the example below we have two GraphQL paths.
    // The resource and verb are both combined here.
    actions: [
      "query::User:*",
      "query::Posts:*",
      "mutation::createPost"
    ]

    // There are only two types of effects on the policy Allow and Deny
    effect: "Allow",

    // Conditions allow futher refinement of the policy, and final outcome.

    conditions: [
      {
        // A dot path to the value on the context object
        field: "${user.id}",
        operator: "match", // or "notMatch"
        expected: ["user.id"],

      }
    ],

    // Resources are whats being actioned on
    resources: [
      // User and all it's fields
      "User:*",
      // Or a wildcard for everything
      "*"
    ]

    // Roles are what the policy is attached to
    roles: [
      "authenticated user",
      "subscriber"
    ]
}

Conditions

Conditions provide a powerful way to refine access on a Policy. wahn makes no assumptions about your implementation, and so the implementation of conditions is partially dependent on your impelmenation.

A condition scopes a Policy to values you provide in the context object.

When you define a Condition there are 3 parameters:

  • field: a dot path to the key on your context object.
  • expected: the value you expect to see (hard coded into the policy)
  • expectedOnContext: a dot path to the expected value on your context object. value on the context object
  • operator: match, notMatch, lessThan, greaterThan

DenyType

You can now add a denyType string to your deny policy to provide information to your client, about how to resolve the deny.

The following simple example shows a policy denying with a denyType of mfa-required which can instruct the client to prompt for a MFA token.

// Assemble our policy
const policy: Policy = {
    id: "uuid",
    resources: ["resource"],
    actions: ["action"],
    effect: PolicyEffect.Deny,
    denyType: "mfa-required",
    conditions: [
        {
            field: "user.sessionAge",
            operator: PolicyOperator.greaterThan,
            expected: [600],
        },
    ],
    roles: ["authenticated user"],
};

Running the tests

Use yarn tests or npm run tests.

Tests are written with ava, and we would strongly like tests with any new functionality.

Performance

wahn needs to be as performant as possible. We use wahn to keep track of performance changes. Any new functionality cannot increase the performance beyond resonable limits.

Deployment

Wahn could either be integrated into your application, or setup as a standalone server.

Contributing

Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.

Versioning

We use SemVer for versioning. For the versions available, see the tags on this repository.

Authors

License

This project is licensed under the MIT License - see the LICENSE.md file for details

FOSSA Status

Acknowledgments