github.com/qutheory/gate

Basic authorization


License
MIT

Documentation

Gate

This package offers basic authorization.

Documentation

First you need to initialize a shared instance somewhere in your code. So that you can be sure that your whole project uses the same policy rules.

let gate = Gate(for: User.self)

Defining policies

There are two types of policies. Policies for general types and policies that check specific objects of those types.

General type policies

To create a policy for just a general type, you write something like this:

gate.addPolicy(to: .create, a: Post.self) { user in
    return user != nil // only authenticated users may create posts
}

Specific object policies

To create a policy where you need to check the specific object, you write:

gate.addPolicy(to: .update, a: Post.self) { user, post in
    return user == post.author // users may only update their own posts
}

You see the only difference is which arguments you ask for in your closure.

Policies return an optional boolean. Returning nil means "undecided, keep checking other policies". When you check the policy for a specific Post, it will first try the specific-object policies and if they don't return an answer then it will try the general-type policies.

Actions you can create policies for

Besides .create and .update, you can also define policies for .delete, .list (which is used for index-pages) and .inspect (which is used for view-pages).

Checking policies

There are several ways to check policies. You can check policies "softly", by only asking a boolean back.

func update(user: User, post: Post) throws {
    if try gate.check(if: user, can: .update, this: post) {
        // save updated post
    } else {
        // manually abort
    }
}

Or you can make Gate enforce its policy like so:

func update(user: User, post: Post) throws {
    // if the user is not authorized then this will
    // automatically throw an unauthorized error
    try gate.ensure(that: user, can: .update, this: post)

    // Save your updated post in the database here...
}

Of course in some cases you don't have a specific object to check against, that's why we created those general-type policies. You can use those like so:

func create(user: User, request: Request) throws {
    try gate.ensure(that: user, can: .create, a: Post.self)

    // Create your post here from the request data...
}

Since some types start with a vowel, you can also write "an" instead of "a"

func create(user: User, request: Request) throws {
    try gate.ensure(that: user, can: .create, an: Image.self)

    // Create your image here from the request data...
}

Convenience methods

Finally, if you make your User conform to the Authorizable protocol, it gets a few convenience methods:

if try user.can(.update, this: post) { ... }
if try user.can(.create, a: Post.self) { ... }
if try user.can(.create, an: Image.self) { ... }

if try user.cannot(.update, this: post) { ... }
if try user.cannot(.create, a: Post.self) { ... }
if try user.cannot(.create, an: Image.self) { ... }