Skip to content

Commit 8dede23

Browse files
authored
Merge pull request #653 from vapor/tn-model-user
Add ModelUser and ModelUserToken
2 parents fa4382f + efaca97 commit 8dede23

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-0
lines changed

Sources/Fluent/ModelUser.swift

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import Vapor
2+
3+
public protocol ModelUser: Model, Authenticatable {
4+
static var usernameKey: KeyPath<Self, Field<String>> { get }
5+
static var passwordHashKey: KeyPath<Self, Field<String>> { get }
6+
func verify(password: String) throws -> Bool
7+
}
8+
9+
extension ModelUser {
10+
public static func authenticator(
11+
database: DatabaseID? = nil
12+
) -> ModelUserAuthenticator<Self> {
13+
ModelUserAuthenticator<Self>(database: database)
14+
}
15+
16+
var _$username: Field<String> {
17+
self[keyPath: Self.usernameKey]
18+
}
19+
20+
var _$passwordHash: Field<String> {
21+
self[keyPath: Self.passwordHashKey]
22+
}
23+
}
24+
25+
public struct ModelUserAuthenticator<User>: BasicAuthenticator
26+
where User: ModelUser
27+
{
28+
public let database: DatabaseID?
29+
30+
public func authenticate(
31+
basic: BasicAuthorization,
32+
for request: Request
33+
) -> EventLoopFuture<User?> {
34+
User.query(on: request.db(self.database))
35+
.filter(\._$username == basic.username)
36+
.first()
37+
.flatMapThrowing
38+
{
39+
guard let user = $0 else {
40+
return nil
41+
}
42+
guard try user.verify(password: basic.password) else {
43+
return nil
44+
}
45+
return user
46+
}
47+
}
48+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import Vapor
2+
3+
public protocol ModelUserToken: Model {
4+
associatedtype User: Model & Authenticatable
5+
static var valueKey: KeyPath<Self, Field<String>> { get }
6+
static var userKey: KeyPath<Self, Parent<User>> { get }
7+
var isValid: Bool { get }
8+
}
9+
10+
extension ModelUserToken {
11+
public static func authenticator(
12+
database: DatabaseID? = nil
13+
) -> ModelUserTokenAuthenticator<Self> {
14+
ModelUserTokenAuthenticator<Self>(database: database)
15+
}
16+
17+
var _$value: Field<String> {
18+
self[keyPath: Self.valueKey]
19+
}
20+
21+
var _$user: Parent<User> {
22+
self[keyPath: Self.userKey]
23+
}
24+
}
25+
26+
public struct ModelUserTokenAuthenticator<Token>: BearerAuthenticator
27+
where Token: ModelUserToken
28+
{
29+
public typealias User = Token.User
30+
public let database: DatabaseID?
31+
32+
public func authenticate(
33+
bearer: BearerAuthorization,
34+
for request: Request
35+
) -> EventLoopFuture<User?> {
36+
let db = request.db(self.database)
37+
return Token.query(on: db)
38+
.filter(\._$value == bearer.token)
39+
.first()
40+
.flatMap
41+
{ token -> EventLoopFuture<User?> in
42+
guard let token = token else {
43+
return request.eventLoop.makeSucceededFuture(nil)
44+
}
45+
guard token.isValid else {
46+
return token.delete(on: db).map { nil }
47+
}
48+
return token._$user.get(on: db)
49+
.map { $0 }
50+
}
51+
}
52+
}
53+

0 commit comments

Comments
 (0)