Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support a closure for instantiating RedirectMiddleware #2364

Merged
merged 9 commits into from Jul 10, 2020
21 changes: 15 additions & 6 deletions Sources/Vapor/Authentication/RedirectMiddleware.swift
Expand Up @@ -4,26 +4,35 @@ extension Authenticatable {
/// - parameters:
/// - path: The path to redirect to if the request is not authenticated
public static func redirectMiddleware(path: String) -> Middleware {
return RedirectMiddleware<Self>(Self.self, path: path)
self.redirectMiddleware(makePath: { _ in path })
}

/// Basic middleware to redirect unauthenticated requests to the supplied path
///
/// - parameters:
/// - makePath: The closure that returns the redirect path based on the given `Request` object
public static func redirectMiddleware(makePath: @escaping (Request) -> String) -> Middleware {
RedirectMiddleware<Self>(Self.self, makePath: makePath)
}
}


private final class RedirectMiddleware<A>: Middleware
where A: Authenticatable
{
let path: String

init(_ authenticatableType: A.Type = A.self, path: String) {
self.path = path
let makePath: (Request) -> String
init(_ authenticatableType: A.Type = A.self, makePath: @escaping (Request) -> String) {
self.makePath = makePath
}

/// See Middleware.respond
func respond(to req: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {
if req.auth.has(A.self) {
return next.respond(to: req)
}
let redirect = req.redirect(to: path)

let redirect = req.redirect(to: self.makePath(req))
return req.eventLoop.makeSucceededFuture(redirect)
}
}
44 changes: 44 additions & 0 deletions Tests/VaporTests/AuthenticationTests.swift
Expand Up @@ -76,6 +76,50 @@ final class AuthenticationTests: XCTestCase {
XCTAssertEqual(res.body.string, "Vapor")
}
}

func testBasicAuthenticatorWithRedirect() throws {
struct Test: Authenticatable {
static func authenticator() -> Authenticator {
TestAuthenticator()
}

var name: String
}

struct TestAuthenticator: BasicAuthenticator {
typealias User = Test

func authenticate(basic: BasicAuthorization, for request: Request) -> EventLoopFuture<Void> {
if basic.username == "test" && basic.password == "secret" {
let test = Test(name: "Vapor")
request.auth.login(test)
}
return request.eventLoop.makeSucceededFuture(())
}
}

let app = Application(.testing)
defer { app.shutdown() }

let redirectMiddleware = Test.redirectMiddleware { req -> String in
return "/redirect?orig=\(req.url.path)"
}

app.routes.grouped([
Test.authenticator(), redirectMiddleware
]).get("test") { req -> String in
return try req.auth.require(Test.self).name
}

let basic = "test:secret".data(using: .utf8)!.base64EncodedString()
try app.testable().test(.GET, "/test") { res in
XCTAssertEqual(res.status, .seeOther)
XCTAssertEqual(res.headers["Location"].first, "/redirect?orig=/test")
}.test(.GET, "/test", headers: ["Authorization": "Basic \(basic)"]) { res in
XCTAssertEqual(res.status, .ok)
XCTAssertEqual(res.body.string, "Vapor")
}
}

func testSessionAuthentication() throws {
struct Test: Authenticatable, SessionAuthenticatable {
Expand Down