Skip to content

Update auth to allow eventloopfuture resolution, explicit eventloop initialization #4

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions Sources/GraphQLWS/Server.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public class Server<InitPayload: Equatable & Codable> {

let onExecute: (GraphQLRequest) -> EventLoopFuture<GraphQLResult>
let onSubscribe: (GraphQLRequest) -> EventLoopFuture<SubscriptionResult>
var auth: (InitPayload) throws -> EventLoopFuture<Void>

var auth: (InitPayload) throws -> Void = { _ in }
var onExit: () -> Void = { }
var onMessage: (String) -> Void = { _ in }
var onOperationComplete: (String) -> Void = { _ in }
Expand All @@ -34,14 +34,17 @@ public class Server<InitPayload: Equatable & Codable> {
/// - messenger: The messenger to bind the server to.
/// - onExecute: Callback run during `start` resolution for non-streaming queries. Typically this is `API.execute`.
/// - onSubscribe: Callback run during `start` resolution for streaming queries. Typically this is `API.subscribe`.
/// - eventLoop: EventLoop on which to perform server operations.
public init(
messenger: Messenger,
onExecute: @escaping (GraphQLRequest) -> EventLoopFuture<GraphQLResult>,
onSubscribe: @escaping (GraphQLRequest) -> EventLoopFuture<SubscriptionResult>
onSubscribe: @escaping (GraphQLRequest) -> EventLoopFuture<SubscriptionResult>,
eventLoop: EventLoop
) {
self.messenger = messenger
self.onExecute = onExecute
self.onSubscribe = onSubscribe
self.auth = { _ in eventLoop.makeSucceededVoidFuture() }

messenger.onReceive { message in
guard let messenger = self.messenger else { return }
Expand Down Expand Up @@ -100,10 +103,10 @@ public class Server<InitPayload: Equatable & Codable> {
}
}

/// Define the callback run during `connection_init` resolution that allows authorization using the `payload`.
/// Throw from this closure to indicate that authorization has failed.
/// Define a custom callback run during `connection_init` resolution that allows authorization using the `payload`.
/// Throw or fail the future from this closure to indicate that authorization has failed.
/// - Parameter callback: The callback to assign
public func auth(_ callback: @escaping (InitPayload) throws -> Void) {
public func auth(_ callback: @escaping (InitPayload) throws -> EventLoopFuture<Void>) {
self.auth = callback
}

Expand Down Expand Up @@ -138,14 +141,20 @@ public class Server<InitPayload: Equatable & Codable> {
}

do {
try self.auth(connectionInitRequest.payload)
let authResult = try self.auth(connectionInitRequest.payload)
authResult.whenSuccess {
self.initialized = true
self.sendConnectionAck()
}
authResult.whenFailure { error in
self.error(.unauthorized())
return
}
}
catch {
self.error(.unauthorized())
return
}
initialized = true
self.sendConnectionAck()
// TODO: Should we send the `ka` message?
}

Expand Down
40 changes: 35 additions & 5 deletions Tests/GraphQLWSTests/GraphQLWSTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class GraphqlWsTests: XCTestCase {
var clientMessenger: TestMessenger!
var serverMessenger: TestMessenger!
var server: Server<TokenInitPayload>!
var eventLoop: EventLoop!

override func setUp() {
// Point the client and server at each other
Expand All @@ -20,7 +21,7 @@ class GraphqlWsTests: XCTestCase {
clientMessenger.other = serverMessenger
serverMessenger.other = clientMessenger

let eventLoop = MultiThreadedEventLoopGroup(numberOfThreads: 1).next()
eventLoop = MultiThreadedEventLoopGroup(numberOfThreads: 1).next()
let api = TestAPI()
let context = TestContext()

Expand All @@ -30,16 +31,17 @@ class GraphqlWsTests: XCTestCase {
api.execute(
request: graphQLRequest.query,
context: context,
on: eventLoop
on: self.eventLoop
)
},
onSubscribe: { graphQLRequest in
api.subscribe(
request: graphQLRequest.query,
context: context,
on: eventLoop
on: self.eventLoop
)
}
},
eventLoop: self.eventLoop
)
}

Expand Down Expand Up @@ -73,7 +75,7 @@ class GraphqlWsTests: XCTestCase {
}

/// Tests that throwing in the authorization callback forces an unauthorized error
func testAuth() throws {
func testAuthWithThrow() throws {
server.auth { payload in
throw TestError.couldBeAnything
}
Expand All @@ -100,6 +102,34 @@ class GraphqlWsTests: XCTestCase {
)
}

/// Tests that failing a future in the authorization callback forces an unauthorized error
func testAuthWithFailedFuture() throws {
server.auth { payload in
self.eventLoop.makeFailedFuture(TestError.couldBeAnything)
}

var messages = [String]()
let completeExpectation = XCTestExpectation()

let client = Client<TokenInitPayload>(messenger: clientMessenger)
client.onMessage { message, _ in
messages.append(message)
completeExpectation.fulfill()
}

client.sendConnectionInit(
payload: TokenInitPayload(
authToken: ""
)
)

wait(for: [completeExpectation], timeout: 2)
XCTAssertEqual(
messages,
["\(ErrorCode.unauthorized): Unauthorized"]
)
}

/// Test single op message flow works as expected
func testSingleOp() throws {
let id = UUID().description
Expand Down