diff --git a/Package.swift b/Package.swift index 96c5e10..93f41e0 100644 --- a/Package.swift +++ b/Package.swift @@ -13,7 +13,7 @@ let package = Package( // 💧 A server-side Swift web framework. .package( url: "https://github.com/vapor/vapor.git", - from: "4.0.0-rc" + from: "4.0.0" ), // 💧 Vapor's ORM Framework. diff --git a/Sources/Corvus/Authentication/CorvusModelAuthenticatable.swift b/Sources/Corvus/Authentication/CorvusModelAuthenticatable.swift new file mode 100644 index 0000000..34ae1cc --- /dev/null +++ b/Sources/Corvus/Authentication/CorvusModelAuthenticatable.swift @@ -0,0 +1,5 @@ +import Fluent + +/// A protocol that wraps `Vapor`'s `ModelAuthenticatable` +/// for consistency in naming. +public protocol CorvusModelAuthenticatable: ModelAuthenticatable {} diff --git a/Sources/Corvus/Authentication/CorvusModelUserToken.swift b/Sources/Corvus/Authentication/CorvusModelTokenAuthenticatable.swift similarity index 70% rename from Sources/Corvus/Authentication/CorvusModelUserToken.swift rename to Sources/Corvus/Authentication/CorvusModelTokenAuthenticatable.swift index e3ae606..ec7e6ed 100644 --- a/Sources/Corvus/Authentication/CorvusModelUserToken.swift +++ b/Sources/Corvus/Authentication/CorvusModelTokenAuthenticatable.swift @@ -1,29 +1,28 @@ -import Fluent import Vapor +import Fluent // swiftlint:disable identifier_name /// A protocol that defines bearer authentication tokens, similar to -/// `ModelUserToken`. -public protocol CorvusModelUserToken: CorvusModel { - +/// `CorvusModelTokenAuthenticatable`. +public protocol CorvusModelTokenAuthenticatable: CorvusModel, Authenticatable { /// The `User` type the token belongs to. - associatedtype User: CorvusModel & Authenticatable - - /// The `String` value of the token. - var value: String { get set } - - /// The `User` associated with the token. - var user: User { get set } - - /// A boolean that deletes tokens if they're not in use. - var isValid: Bool { get } + associatedtype User: CorvusModel & Authenticatable + + /// The `String` value of the token. + var value: String { get set } + + /// The `User` associated with the token. + var user: User { get set } + + /// A boolean that deletes tokens if they're not in use. + var isValid: Bool { get } } /// An extension to provide a default initializer to tokens so that they can /// be initialized manually in module code. -extension CorvusModelUserToken { - +extension CorvusModelTokenAuthenticatable { + /// Initializes a token. /// - Parameters: /// - id: The unique identifier of the token. @@ -39,15 +38,15 @@ extension CorvusModelUserToken { /// An extension to provide an authenticator for the token that can be /// registered to the `Vapor` application, and field accessors for `value` and /// `user`. -extension CorvusModelUserToken { - +extension CorvusModelTokenAuthenticatable { + /// Provides a `Vapor` authenticator defined below. public static func authenticator( database: DatabaseID? = nil - ) -> CorvusModelUserTokenAuthenticator { - CorvusModelUserTokenAuthenticator(database: database) + ) -> CorvusModelTokenAuthenticator { + CorvusModelTokenAuthenticator(database: database) } - + /// Provides access to the `value` attribute. var _$value: Field { guard let mirror = Mirror(reflecting: self).descendant("_value"), @@ -71,16 +70,16 @@ extension CorvusModelUserToken { /// Provides a `BearerAuthenticator` struct that defines how tokens are /// authenticated. -public struct CorvusModelUserTokenAuthenticator: +public struct CorvusModelTokenAuthenticator: BearerAuthenticator { - + /// The token's user. public typealias User = T.User - + /// The database the token is saved in. public let database: DatabaseID? - + /// Authenticates a token. /// - Parameters: /// - bearer: The bearer token passed in the request. @@ -89,21 +88,23 @@ BearerAuthenticator public func authenticate( bearer: BearerAuthorization, for request: Request - ) -> EventLoopFuture { + ) -> EventLoopFuture { let db = request.db(self.database) return T.query(on: db) .filter(\._$value == bearer.token) .first() .flatMap - { token -> EventLoopFuture in + { token -> EventLoopFuture in guard let token = token else { - return request.eventLoop.makeSucceededFuture(nil) + return request.eventLoop.makeSucceededFuture(()) } guard token.isValid else { - return token.delete(on: db).map { nil } + return token.delete(on: db) + } + request.auth.login(token) + return token._$user.get(on: db).map { + request.auth.login($0) } - return token._$user.get(on: db) - .map { $0 } } } } diff --git a/Sources/Corvus/Authentication/CorvusModelUser.swift b/Sources/Corvus/Authentication/CorvusModelUser.swift deleted file mode 100644 index 6da2463..0000000 --- a/Sources/Corvus/Authentication/CorvusModelUser.swift +++ /dev/null @@ -1,5 +0,0 @@ -import Fluent -import Vapor - -/// A protocol that wraps `Vapor`'s `ModelUser` for consistency in naming. -public protocol CorvusModelUser: ModelUser {} diff --git a/Sources/Corvus/Authentication/CorvusToken.swift b/Sources/Corvus/Authentication/CorvusToken.swift index 9f6e846..6819adf 100644 --- a/Sources/Corvus/Authentication/CorvusToken.swift +++ b/Sources/Corvus/Authentication/CorvusToken.swift @@ -69,7 +69,7 @@ public struct CreateCorvusToken: Migration { /// An extension to conform to the `CorvusModelUserToken` protocol, which /// provides functionality to authenticate a token. -extension CorvusToken: CorvusModelUserToken { +extension CorvusToken: CorvusModelTokenAuthenticatable { /// Prevents tokens from being deleted after authentication. public var isValid: Bool { diff --git a/Sources/Corvus/Authentication/CorvusUser.swift b/Sources/Corvus/Authentication/CorvusUser.swift index 1f84192..c4c9d58 100644 --- a/Sources/Corvus/Authentication/CorvusUser.swift +++ b/Sources/Corvus/Authentication/CorvusUser.swift @@ -67,7 +67,7 @@ public struct CreateCorvusUser: Migration { /// An extension to conform to the `CorvusModelUser` protocol, which provides /// functionality to authenticate a user with username and password. -extension CorvusUser: CorvusModelUser { +extension CorvusUser: CorvusModelAuthenticatable { /// Provides a path to the user's username. public static let usernameKey = \CorvusUser.$username diff --git a/Sources/Corvus/Endpoints/Groups/BasicAuthGroup.swift b/Sources/Corvus/Endpoints/Groups/BasicAuthGroup.swift index e550087..fb8826c 100644 --- a/Sources/Corvus/Endpoints/Groups/BasicAuthGroup.swift +++ b/Sources/Corvus/Endpoints/Groups/BasicAuthGroup.swift @@ -3,7 +3,7 @@ import Fluent /// A special type of `Group` that protects its `content` with basic /// authentication for a generic `CorvusModelUser`. -public struct BasicAuthGroup: Endpoint { +public struct BasicAuthGroup: Endpoint { /// An array of `PathComponent` describing the path that the /// `BasicAuthGroup` extends. @@ -44,7 +44,7 @@ public struct BasicAuthGroup: Endpoint { let guardedRoutesBuilder = groupedRoutesBuilder.grouped([ T.guardMiddleware(), - T.authenticator().middleware() + T.authenticator() ]) content.register(to: guardedRoutesBuilder) diff --git a/Sources/Corvus/Endpoints/Groups/BearerAuthGroup.swift b/Sources/Corvus/Endpoints/Groups/BearerAuthGroup.swift index 781ceb6..d08ac08 100644 --- a/Sources/Corvus/Endpoints/Groups/BearerAuthGroup.swift +++ b/Sources/Corvus/Endpoints/Groups/BearerAuthGroup.swift @@ -2,8 +2,8 @@ import Vapor import Fluent /// A special type of `Group` that protects its `content` with bearer token -/// authentication for a generic `CorvusModelUserToken`. -public struct BearerAuthGroup: Endpoint { +/// authentication for a generic `CorvusModelTokenAuthenticatable`. +public struct BearerAuthGroup: Endpoint { /// An array of `PathComponent` describing the path that the /// `BearerAuthGroup` extends. @@ -44,7 +44,7 @@ public struct BearerAuthGroup: Endpoint { let guardedRoutesBuilder = groupedRoutesBuilder.grouped([ T.User.guardMiddleware(), - T.authenticator().middleware() + T.authenticator() ]) content.register(to: guardedRoutesBuilder) diff --git a/Sources/Corvus/Endpoints/Login.swift b/Sources/Corvus/Endpoints/Login.swift index a03cc71..47e731d 100644 --- a/Sources/Corvus/Endpoints/Login.swift +++ b/Sources/Corvus/Endpoints/Login.swift @@ -1,12 +1,13 @@ import Vapor import Fluent - + /// A class that provides functionality to log in a user with username and /// password credentials sent in a HTTP POST `Request` and save a token for /// that user. Needs an object of type `T` which represents the token to be /// created upon login. -public final class Login: Endpoint -where T.User: CorvusModelUser { +public final class Login< + T: CorvusModelTokenAuthenticatable & ResponseEncodable>: +Endpoint where T.User: CorvusModelAuthenticatable { /// The route for the login functionality let path: PathComponent @@ -42,7 +43,7 @@ where T.User: CorvusModelUser { /// about the HTTP route leading to the current component. public func register(to routes: RoutesBuilder) { let guardedRoutesBuilder = routes.grouped( - T.User.authenticator().middleware() + T.User.authenticator() ) guardedRoutesBuilder.post(path, use: handler) diff --git a/Sources/Corvus/Endpoints/Modifiers/AuthModifier.swift b/Sources/Corvus/Endpoints/Modifiers/AuthModifier.swift index 3586b00..9efa24d 100644 --- a/Sources/Corvus/Endpoints/Modifiers/AuthModifier.swift +++ b/Sources/Corvus/Endpoints/Modifiers/AuthModifier.swift @@ -5,7 +5,7 @@ import Fluent /// allows Corvus to chain modifiers, as it gets treated as any other struct /// conforming to `AuthEndpoint`. Requires an object `T` that represents the /// user to authorize. -public final class AuthModifier: +public final class AuthModifier: AuthEndpoint { /// The return type for the `.handler()` modifier. @@ -109,7 +109,7 @@ extension AuthEndpoint { /// - Parameter user: A `KeyPath` to the related user property. /// - Returns: An instance of a `AuthModifier` with the supplied `KeyPath` /// to the user. - public func auth( + public func auth( _ user: AuthModifier.UserKeyPath ) -> AuthModifier { AuthModifier(self, user: user) diff --git a/Sources/Corvus/Endpoints/Modifiers/UserAuthModifier.swift b/Sources/Corvus/Endpoints/Modifiers/UserAuthModifier.swift index 67699b7..11c4547 100644 --- a/Sources/Corvus/Endpoints/Modifiers/UserAuthModifier.swift +++ b/Sources/Corvus/Endpoints/Modifiers/UserAuthModifier.swift @@ -5,7 +5,7 @@ import Fluent /// That allows Corvus to chain modifiers, as it gets treated as any other /// struct conforming to `AuthEndpoint`. public final class UserAuthModifier: AuthEndpoint -where Q.QuerySubject: CorvusModelUser { +where Q.QuerySubject: CorvusModelAuthenticatable { /// The return type for the `.handler()` modifier. public typealias Element = Q.Element @@ -78,7 +78,7 @@ where Q.QuerySubject: CorvusModelUser { /// An extension that adds the `.userAuth()` modifier to components conforming /// to `AuthEndpoint`. -extension AuthEndpoint where Self.QuerySubject: CorvusModelUser { +extension AuthEndpoint where Self.QuerySubject: CorvusModelAuthenticatable { /// A modifier used to make sure components only authorize requests where /// the supplied `CorvusUser` is actually related to the `QuerySubject`. diff --git a/Sources/Corvus/Endpoints/User.swift b/Sources/Corvus/Endpoints/User.swift index eca7d2f..c198105 100644 --- a/Sources/Corvus/Endpoints/User.swift +++ b/Sources/Corvus/Endpoints/User.swift @@ -3,7 +3,7 @@ import Fluent /// A class that contains Create, Read, Update and Delete functionality for a /// generic type `T` representing a user object. -public final class User: Endpoint { +public final class User: Endpoint { /// The route path to the parameters. let pathComponents: [PathComponent] diff --git a/Tests/CorvusTests/AuthenticationTests.swift b/Tests/CorvusTests/AuthenticationTests.swift index 265c663..8fb30af 100644 --- a/Tests/CorvusTests/AuthenticationTests.swift +++ b/Tests/CorvusTests/AuthenticationTests.swift @@ -27,7 +27,7 @@ final class AuthenticationTests: XCTestCase { let basicAuthenticatorTest = BasicAuthenticatorTest() app.databases.use(.sqlite(.memory), as: .test, isDefault: true) - app.middleware.use(CorvusUser.authenticator().middleware()) + app.middleware.use(CorvusUser.authenticator()) app.migrations.add(CreateAccount()) app.migrations.add(CreateCorvusUser()) @@ -85,7 +85,7 @@ final class AuthenticationTests: XCTestCase { let basicAuthenticatorTest = BasicAuthenticatorTest() app.databases.use(.sqlite(.memory), as: .test, isDefault: true) - app.middleware.use(CorvusUser.authenticator().middleware()) + app.middleware.use(CorvusUser.authenticator()) app.migrations.add(CreateAccount()) app.migrations.add(CreateCorvusUser()) @@ -141,8 +141,8 @@ final class AuthenticationTests: XCTestCase { let bearerAuthenticatorTest = BearerAuthenticatorTest() app.databases.use(.sqlite(.memory), as: .test, isDefault: true) - app.middleware.use(CorvusToken.authenticator().middleware()) - app.middleware.use(CorvusUser.authenticator().middleware()) + app.middleware.use(CorvusToken.authenticator()) + app.middleware.use(CorvusUser.authenticator()) app.migrations.add(CreateAccount()) app.migrations.add(CreateCorvusUser()) app.migrations.add(CreateCorvusToken()) @@ -216,7 +216,7 @@ final class AuthenticationTests: XCTestCase { let bearerAuthenticatorTest = BearerAuthenticatorTest() app.databases.use(.sqlite(.memory), as: .test, isDefault: true) - app.middleware.use(CorvusToken.authenticator().middleware()) + app.middleware.use(CorvusToken.authenticator()) app.migrations.add(CreateAccount()) app.migrations.add(CreateCorvusUser()) app.migrations.add(CreateCorvusToken()) @@ -262,8 +262,8 @@ final class AuthenticationTests: XCTestCase { let authModifierTest = AuthModifierTest() app.databases.use(.sqlite(.memory), as: .test, isDefault: true) - app.middleware.use(CorvusToken.authenticator().middleware()) - app.middleware.use(CorvusUser.authenticator().middleware()) + app.middleware.use(CorvusToken.authenticator()) + app.middleware.use(CorvusUser.authenticator()) app.migrations.add(CreateSecureAccount()) app.migrations.add(CreateCorvusUser()) app.migrations.add(CreateCorvusToken()) @@ -392,8 +392,8 @@ final class AuthenticationTests: XCTestCase { let authModifierTest = AuthModifierTest() app.databases.use(.sqlite(.memory), as: .test, isDefault: true) - app.middleware.use(CustomToken.authenticator().middleware()) - app.middleware.use(CustomUser.authenticator().middleware()) + app.middleware.use(CustomToken.authenticator()) + app.middleware.use(CustomUser.authenticator()) app.migrations.add(CreateCustomAccount()) app.migrations.add(CreateCustomUser()) app.migrations.add(CreateCustomToken()) @@ -516,8 +516,8 @@ final class AuthenticationTests: XCTestCase { let userAuthModifierTest = UserAuthModifierTest() app.databases.use(.sqlite(.memory), as: .test, isDefault: true) - app.middleware.use(CorvusToken.authenticator().middleware()) - app.middleware.use(CorvusUser.authenticator().middleware()) + app.middleware.use(CorvusToken.authenticator()) + app.middleware.use(CorvusUser.authenticator()) app.migrations.add(CreateSecureAccount()) app.migrations.add(CreateCorvusUser()) app.migrations.add(CreateCorvusToken()) diff --git a/Tests/CorvusTests/Models/CustomToken.swift b/Tests/CorvusTests/Models/CustomToken.swift index 074b3dd..54cc29f 100644 --- a/Tests/CorvusTests/Models/CustomToken.swift +++ b/Tests/CorvusTests/Models/CustomToken.swift @@ -2,7 +2,7 @@ import Corvus import Foundation import Fluent -public final class CustomToken: CorvusModelUserToken { +public final class CustomToken: CorvusModelTokenAuthenticatable { public static let schema = "custom_tokens" diff --git a/Tests/CorvusTests/Models/CustomUser.swift b/Tests/CorvusTests/Models/CustomUser.swift index 3b2ad87..129348f 100644 --- a/Tests/CorvusTests/Models/CustomUser.swift +++ b/Tests/CorvusTests/Models/CustomUser.swift @@ -3,7 +3,7 @@ import Fluent import Vapor import Foundation -public final class CustomUser: CorvusModel { +public final class CustomUser: CorvusModel, Authenticatable { public static let schema = "custom_users" @@ -62,7 +62,7 @@ public struct CreateCustomUser: Migration { } } -extension CustomUser: CorvusModelUser { +extension CustomUser: CorvusModelAuthenticatable { public static let usernameKey = \CustomUser.$username