Skip to content
This repository has been archived by the owner on Nov 16, 2020. It is now read-only.

Add System for Authorization Checks #32

Open
daSkier opened this issue Mar 22, 2018 · 6 comments
Open

Add System for Authorization Checks #32

daSkier opened this issue Mar 22, 2018 · 6 comments
Labels
enhancement New feature or request
Projects

Comments

@daSkier
Copy link

daSkier commented Mar 22, 2018

I've been wanting to add some permissions around my routes and I thought I'd write my thoughts down as as suggestions or ideas for future development. I'm new to Vapor and backend development, so please take this with a grain of salt.

Roles/Permissions

First, I thought it would be great to be able to add some ability to define permissions levels or roles in conjunction with the User model. It seems like the most flexible system would allow the author to define these levels on their own (These could be defined as either one-to-one or one-to-many). Alternatively, Vapor could provide preset permissions levels, but that seems like possible overkill. Presumably this could be built as a Protocol any model could conform to, but concrete implementations could be provided as well.

Authorizable Could be designed like Timestampable:

extension User: Authorizable {
    // Adds mapping to Authorizable key path properties
    static var permissionsKey: WritableKeyPath<User, [PermissionType]> { return \.roleLevel }
}

Rules/Authorization

Now, to add Authorization the routes you could add an extra function to the route chain like .authorize(using: .FooPolicy). This could be done with a closure or function that accepts arguments that could be used to define a true/false or pass/fail test. Laravel does something similar with their Authorization system.

If you start with a collection of routes like this:

let athleteRoutes = router.grouped("api", "athletes")
athleteRoutes.get(use: index)
athleteRoutes.post(use: create)
athleteRoutes.delete(Athlete.parameter, use: delete)

With Authorization the route group could end up looking like the following:

let athleteRoutes = router.authorize(using: athletePolicy).grouped("api", "athletes")
athleteRoutes.get(use: index)
athleteRoutes.post(use: create)
athleteRoutes.delete(Athlete.parameter, use: delete)

Or:

let athleteRoutes = router.grouped("api", "athletes")
athleteRoutes.get(use: index)
athleteRoutes.authorize(using: athleteEditPolicy).post(use: create)
athleteRoutes.authorize(using: athleteEditPolicy).delete(Athlete.parameter, use: delete)

Ideally, you could add this authorization component at both the individual route or route group level.

The authorization function could look something like this:

func athletePolicy(_ req: Request) throws -> Future<Bool> {
    return user.permissions.contains(.athleteEditor) // pseudocode for getting user permissions levels 
}

Or:

func athleteEditPolicy(_ req: Request) throws -> Future<Bool> {
    return try req.content.decode(Athlete.self).map(to: Bool.self) { athlete in 
        return athlete.ownerID == user.ID //pseudocode for getting user.ID
    }
}

In the event that the Authorization process fails, I'd be great if Vapor sent the appropriate HTTP error code (I think this would be a 403, but I could be wrong).

@tanner0101 tanner0101 added the enhancement New feature or request label Mar 22, 2018
@natebird
Copy link
Member

I like the implementation above but I feel like authentication and authorization shouldn't be part of the same package. This should be separate.

@tanner0101 tanner0101 added this to To Do in Vapor 4 via automation Mar 20, 2019
@tanner0101
Copy link
Member

This actually was a part of the first version of this package, we just didn't have time to port it over during Vapor 3's release. See: https://github.com/vapor/auth/tree/1.2.1/Sources/Authorization

Hopefully I'll have time for Vapor 4's release to work on it. I think the "policy" idea here is interesting. I also like how Laravel does it: https://laravel.com/docs/5.8/authorization.

@natebird
Copy link
Member

Oh, interesting. I might be able to help with it. I'll have need of something like that in my app in a couple of weeks.

@tanner0101
Copy link
Member

tanner0101 commented Mar 20, 2019

Cool, that would be greatly appreciated. For this large of a change, I'd recommend doing a pitch first to really flesh out the idea and get feedback before code is written. You can see a couple examples of that here:

https://forums.swift.org/tags/c/related-projects/vapor/pitch

@jonny7
Copy link
Member

jonny7 commented Apr 9, 2019

Hi @tanner0101

I started a generic package for RBAC. It's currently stalled as it can't compile with Swift 5 due to a regression I logged with the Swift team. It's based off the NIST model. I've used it with Yii and it allowed me really granular control over routes and to apply custom rule files to those routes. I was hoping to get it finished before Vapor 4. But depends on when this bug is fixed.
But if it sounds like it or aspects of it might be useful let me know i'd be happy to help. Gotta earn that contributor/maintainer badge somehow :)

@jdmcd
Copy link
Member

jdmcd commented Apr 12, 2019

I've posted a pitch on the forum here with some ideas: https://forums.swift.org/t/pitch-vapor-4-authorization-system/22980

@tanner0101 tanner0101 moved this from Backlog to Done in Vapor 4 Mar 6, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
Vapor 4
  
Done
Development

No branches or pull requests

5 participants