From 8056140198785f3c948bbccc85a8075d9a413b0b Mon Sep 17 00:00:00 2001 From: noppoman Date: Wed, 12 Dec 2018 17:42:05 +0900 Subject: [PATCH] Bump HexaviileFramework version to 1.0.0-rc.1 --- Package.resolved | 44 ++++----- Package.swift | 6 +- .../AuthenticationMiddleware.swift | 1 - Sources/HexavilleAuth/HMAC.swift | 2 +- Sources/HexavilleAuth/HTTPClient.swift | 40 ++++++++ Sources/HexavilleAuth/HexaviileAuth.swift | 6 +- .../HexavilleAuth/HexavilleAuth+Router.swift | 10 +- Sources/HexavilleAuth/OAuth/OAuth1.swift | 91 ++++++++----------- Sources/HexavilleAuth/OAuth/OAuth2.swift | 24 ++--- .../FacebookAuthorizationProvider.swift | 13 +-- .../OAuth2/GithubAuthorizationProvider.swift | 15 +-- Sources/HexavilleAuth/URLRequest.swift | 57 ------------ Sources/HexavilleAuth/URLResponse.swift | 25 ----- Sources/HexavilleAuthExample/main.swift | 2 +- 14 files changed, 133 insertions(+), 203 deletions(-) create mode 100644 Sources/HexavilleAuth/HTTPClient.swift delete mode 100644 Sources/HexavilleAuth/URLRequest.swift delete mode 100644 Sources/HexavilleAuth/URLResponse.swift diff --git a/Package.resolved b/Package.resolved index 401472e..874a48c 100644 --- a/Package.resolved +++ b/Package.resolved @@ -2,48 +2,48 @@ "object": { "pins": [ { - "package": "CHTTPParser", - "repositoryURL": "https://github.com/Zewo/CHTTPParser.git", + "package": "HexavilleFramework", + "repositoryURL": "https://github.com/noppoMan/HexavilleFramework.git", "state": { "branch": null, - "revision": "88306ab33bb316b2eedd39c90f4be8f4ebf65a11", - "version": "0.14.0" + "revision": "6edcc3a4e0f4a37e21846b817e27a4f26513d840", + "version": "1.0.0-rc.1" } }, { - "package": "CLibreSSL", - "repositoryURL": "https://github.com/vapor/clibressl.git", + "package": "swift-nio", + "repositoryURL": "https://github.com/apple/swift-nio.git", "state": { "branch": null, - "revision": "23ddb296981d17a8ee6c7418742a40cad5d2f9d0", - "version": "1.0.0" + "revision": "a20e129c22ad00a51c902dca54a5456f90664780", + "version": "1.12.0" } }, { - "package": "HexavilleFramework", - "repositoryURL": "https://github.com/noppoMan/HexavilleFramework.git", + "package": "swift-nio-ssl", + "repositoryURL": "https://github.com/apple/swift-nio-ssl.git", "state": { "branch": null, - "revision": "97e9ca73eb28ed2beabb079bf63f43e51360c7e8", - "version": "0.1.16" + "revision": "db16c3a90b101bb53b26a58867a344ad428072e0", + "version": "1.3.2" } }, { - "package": "Prorsum", - "repositoryURL": "https://github.com/noppoMan/Prorsum.git", + "package": "swift-nio-ssl-support", + "repositoryURL": "https://github.com/apple/swift-nio-ssl-support.git", "state": { "branch": null, - "revision": "b278e88142a3b5a87feabc44b6522dabfcda8f99", - "version": "0.3.3" + "revision": "c02eec4e0e6d351cd092938cf44195a8e669f555", + "version": "1.0.0" } }, { - "package": "ProrsumNet", - "repositoryURL": "https://github.com/noppoman/ProrsumNet.git", + "package": "swift-nio-zlib-support", + "repositoryURL": "https://github.com/apple/swift-nio-zlib-support.git", "state": { "branch": null, - "revision": "941e0c65df620467130e9eb2496a3c1275c1d307", - "version": "0.1.3" + "revision": "37760e9a52030bb9011972c5213c3350fa9d41fd", + "version": "1.0.0" } }, { @@ -51,8 +51,8 @@ "repositoryURL": "https://github.com/jakeheis/SwiftCLI.git", "state": { "branch": null, - "revision": "37f4a7f863f6fe76ce44fc0023f331eea0089beb", - "version": "5.2.0" + "revision": "fb076cba39c679da4e27813518d8860d8815a25b", + "version": "5.2.1" } } ] diff --git a/Package.swift b/Package.swift index 23b8531..03f5cc2 100644 --- a/Package.swift +++ b/Package.swift @@ -9,10 +9,12 @@ let package = Package( .executable(name: "hexaville-todo-example", targets: ["HexavilleAuthExample"]) ], dependencies: [ - .package(url: "https://github.com/noppoMan/HexavilleFramework.git", .upToNextMajor(from: "0.1.16")) + .package(url: "https://github.com/noppoMan/HexavilleFramework.git", .upToNextMajor(from: "1.0.0-rc.1")) ], targets: [ - .target(name: "HexavilleAuth", dependencies: ["HexavilleFramework"]), + .target(name: "HexavilleAuth", dependencies: [ + "HexavilleFramework" + ]), .target(name: "HexavilleAuthExample", dependencies: ["HexavilleAuth"]) ] ) diff --git a/Sources/HexavilleAuth/AuthenticationMiddleware.swift b/Sources/HexavilleAuth/AuthenticationMiddleware.swift index 702fa47..77bec39 100644 --- a/Sources/HexavilleAuth/AuthenticationMiddleware.swift +++ b/Sources/HexavilleAuth/AuthenticationMiddleware.swift @@ -9,7 +9,6 @@ import Foundation import HexavilleFramework - extension HexavilleAuth { public struct AuthenticationMiddleware: Middleware { diff --git a/Sources/HexavilleAuth/HMAC.swift b/Sources/HexavilleAuth/HMAC.swift index 706714d..12c362a 100644 --- a/Sources/HexavilleAuth/HMAC.swift +++ b/Sources/HexavilleAuth/HMAC.swift @@ -7,7 +7,7 @@ // import Foundation -import CLibreSSL +import CNIOOpenSSL func hmacsha1(string: String, key: [UInt8]) -> [UInt8] { var context = HMAC_CTX() diff --git a/Sources/HexavilleAuth/HTTPClient.swift b/Sources/HexavilleAuth/HTTPClient.swift new file mode 100644 index 0000000..8dc84bd --- /dev/null +++ b/Sources/HexavilleAuth/HTTPClient.swift @@ -0,0 +1,40 @@ +// +// HTTPClient.swift +// CNIOAtomics +// +// Created by Yuki Takei on 2018/12/12. +// + +import Foundation +import Dispatch + +struct HTTPClient { + func send(request: URLRequest) throws -> (HTTPURLResponse, Data) { + var _error: Error? + var _data: Data? + var _response: HTTPURLResponse? + let semaphore = DispatchSemaphore(value: 0) + let task = URLSession.shared.dataTask(with: request) { data, response, error in + _error = error + _data = data + _response = response as? HTTPURLResponse + semaphore.signal() + } + + task.resume() + + semaphore.wait() + + if let error = _error { + throw error + } + + return (_response!, _data ?? Data()) + } + + func send(url: URL) throws -> (HTTPURLResponse, Data) { + return try self.send(request: URLRequest(url: url)) + } +} + + diff --git a/Sources/HexavilleAuth/HexaviileAuth.swift b/Sources/HexavilleAuth/HexaviileAuth.swift index 520ff0e..01bafe1 100644 --- a/Sources/HexavilleAuth/HexaviileAuth.swift +++ b/Sources/HexavilleAuth/HexaviileAuth.swift @@ -4,18 +4,18 @@ import HexavilleFramework public enum HexavilleAuthError: Error { case unsupportedPlaform case codeIsMissingInResponseParameters - case responseError(Response) + case responseError(HTTPURLResponse, Data) } extension HexavilleAuthError: CustomStringConvertible { public var description: String { switch self { - case .responseError(let response): + case .responseError(let response, let body): var str = "" str += "\(response)" str += "\n" str += "\n" - str += String(data: response.body.asData(), encoding: .utf8) ?? "Unknown Error" + str += String(data: body, encoding: .utf8) ?? "Unknown Error" return str default: diff --git a/Sources/HexavilleAuth/HexavilleAuth+Router.swift b/Sources/HexavilleAuth/HexavilleAuth+Router.swift index 8885888..782e5a6 100644 --- a/Sources/HexavilleAuth/HexavilleAuth+Router.swift +++ b/Sources/HexavilleAuth/HexavilleAuth+Router.swift @@ -15,18 +15,18 @@ extension HexavilleAuth { for type in providers { switch type { case .oauth1(let provider): - router.use(.get, provider.path) { request, context in + router.use(.GET, provider.path) { request, context in let requestToken = try provider.getRequestToken(for: request) context.session?["hexaville.oauth_token_secret"] = requestToken.oauthTokenSecret context.session?["hexaville.oauth_token"] = requestToken.oauthToken let location = try provider.createAuthorizeURL(requestToken: requestToken).absoluteString var headers = context.responseHeaders - headers["Location"] = location + headers.add(name: "Location", value: location) return Response(status: .found, headers: headers) } - router.use(.get, provider.oauth.callbackURL.path) { request, context in + router.use(.GET, provider.oauth.callbackURL.path) { request, context in guard let secret = context.session?["hexaville.oauth_token_secret"] as? String else { throw OAuth1Error.accessTokenIsMissingInSession } @@ -48,7 +48,7 @@ extension HexavilleAuth { case .oauth2(let provider): - router.use(.get, provider.path) { request, context in + router.use(.GET, provider.path) { request, context in return Response( status: .found, headers: [ @@ -57,7 +57,7 @@ extension HexavilleAuth { ) } - router.use(.get, provider.oauth.callbackURL.path) { request, context in + router.use(.GET, provider.oauth.callbackURL.path) { request, context in let (cred, user) = try provider.authorize(for: request) context.session?[AuthenticationMiddleware.sessionKey] = user.serialize() return try provider.callback(cred, user, request, context) diff --git a/Sources/HexavilleAuth/OAuth/OAuth1.swift b/Sources/HexavilleAuth/OAuth/OAuth1.swift index 0bcbdca..448df3e 100644 --- a/Sources/HexavilleAuth/OAuth/OAuth1.swift +++ b/Sources/HexavilleAuth/OAuth/OAuth1.swift @@ -8,16 +8,15 @@ import Foundation import HexavilleFramework -import CLibreSSL public enum OAuth1Error: Error { case couldNotGenerateSignature case invalidAuthrozeURL(String) case missingRequiredParameters(String) case accessTokenIsMissingInSession - case verifyFailed(Request, Response) - case failedToGetAccessToken(Request, Response) - case failedToGetRequestToken(Request, Response) + case verifyFailed(URLRequest, HTTPURLResponse, Data) + case failedToGetAccessToken(URLRequest, HTTPURLResponse, Data) + case failedToGetRequestToken(URLRequest, HTTPURLResponse, Data) } extension OAuth1Error: CustomStringConvertible { @@ -35,39 +34,41 @@ extension OAuth1Error: CustomStringConvertible { case .accessTokenIsMissingInSession: return "accessTokenIsMissingInSession" - case .verifyFailed(let req, let res): - return stringify(code: "verifyFailed", request: req, response: res) + case .verifyFailed(let req, let res, let body): + return stringify(code: "verifyFailed", request: req, response: res, body: body) - case .failedToGetAccessToken(let req, let res): - return stringify(code: "failedToGetAccessToken", request: req, response: res) + case .failedToGetAccessToken(let req, let res, let body): + return stringify(code: "failedToGetAccessToken", request: req, response: res, body: body) - case .failedToGetRequestToken(let req, let res): - return stringify(code: "failedToGetRequestToken", request: req, response: res) + case .failedToGetRequestToken(let req, let res, let body): + return stringify(code: "failedToGetRequestToken", request: req, response: res, body: body) } } - private func stringify(code: String, request: Request, response: Response) -> String { + private func stringify(code: String, request: URLRequest, response: HTTPURLResponse, body: Data) -> String { var requestHeaders: [String: String] = [:] - for (key, value) in request.headers { + for (key, value) in request.allHTTPHeaderFields ?? [:] { requestHeaders[key.description] = value } var responseHeaders: [String: String] = [:] - for (key, value) in response.headers { - responseHeaders[key.description] = value + for (key, value) in response.allHeaderFields { + if let value = value as? String { + responseHeaders[key.description] = value + } } let requestDict: [String: Any] = [ - "method": request.method.rawValue, - "url": request.url.absoluteString, + "method": request.httpMethod ?? "GET", + "url": request.url!.absoluteString, "headers": requestHeaders, - "body": String(data: request.body.asData(), encoding: .utf8) ?? "" + "body": String(data: body, encoding: .utf8) ?? "" ] let responseDict: [String: Any] = [ "statusCode": response.statusCode, "headers": responseHeaders, - "body": String(data: response.body.asData(), encoding: .utf8) ?? "" + "body": String(data: body, encoding: .utf8) ?? "" ] do { @@ -137,20 +138,17 @@ public class OAuth1 { let authorizationValue = OAuth1.oAuthAuthorizationString(fromParameters: params, withAllowedCharacters: withAllowedCharacters) - let request = Request( - method: .post, - url: URL(string: requestTokenUrl)!, - headers: ["Authorization": authorizationValue] - ) - let client = try HTTPClient(url: request.url) - try client.open() - let response = try client.request(request) + var request = URLRequest(url: URL(string: requestTokenUrl)!) + request.addValue(authorizationValue, forHTTPHeaderField: "Authorization") + request.httpMethod = "POST" + + let (response, body) = try HTTPClient().send(request: request) guard (200..<300).contains(response.statusCode) else { - throw OAuth1Error.failedToGetRequestToken(request, response) + throw OAuth1Error.failedToGetRequestToken(request, response, body) } - let bodyDictionary = OAuth1.parse(bodyData: response.body.asData()) + let bodyDictionary = OAuth1.parse(bodyData: body) guard let oauthToken = bodyDictionary["oauth_token"] else { throw OAuth1Error.missingRequiredParameters("oauth_token") @@ -203,22 +201,16 @@ public class OAuth1 { params["oauth_signature"] = sig let authrozationString = OAuth1.oAuthAuthorizationString(fromParameters: params, withAllowedCharacters: withAllowedCharacters) - - let request = Request( - method: .get, - url: URL(string: verifyURL)!, - headers: ["Authorization": authrozationString] - ) - - let client = try HTTPClient(url: request.url) - try client.open() - let response = try client.request(request) + var request = URLRequest(url: URL(string: verifyURL)!) + request.addValue(authrozationString, forHTTPHeaderField: "Authorization") + + let (response, body) = try HTTPClient().send(request: request) guard (200..<300).contains(response.statusCode) else { - throw OAuth1Error.verifyFailed(request, response) + throw OAuth1Error.verifyFailed(request, response, body) } - return try JSONSerialization.jsonObject(with: response.body.asData(), options: []) as? [String: Any] ?? [:] + return try JSONSerialization.jsonObject(with: body, options: []) as? [String: Any] ?? [:] } public func getAccessToken(request: Request, requestToken: RequestToken) throws -> Credential { @@ -256,21 +248,17 @@ public class OAuth1 { let authrozationString = OAuth1.oAuthAuthorizationString(fromParameters: params, withAllowedCharacters: withAllowedCharacters) - let request = Request( - method: .post, - url: URL(string: urlString)!, - headers: ["Authorization": authrozationString] - ) + var request = URLRequest(url: URL(string: requestTokenUrl)!) + request.addValue(authrozationString, forHTTPHeaderField: "Authorization") + request.httpMethod = "POST" - let client = try HTTPClient(url: request.url) - try client.open() - let response = try client.request(request) + let (response, body) = try HTTPClient().send(request: request) guard (200..<300).contains(response.statusCode) else { - throw OAuth1Error.failedToGetAccessToken(request, response) + throw OAuth1Error.failedToGetAccessToken(request, response, body) } - return try Credential(withDictionary: OAuth1.parse(bodyData: response.body.asData())) + return try Credential(withDictionary: OAuth1.parse(bodyData: body)) } } @@ -324,7 +312,8 @@ extension OAuth1 { } let rawString = [method, percentEncodedUrl, percentEncodedJoinedParameters].joined(separator: "&") - let encodedRawBytes = hmacsha1(string: rawString, key: (percentEncodedConsumerSecret + "&" + (oauthToken ?? "")).bytes) + let bytes = Array((percentEncodedConsumerSecret + "&" + (oauthToken ?? "")).utf8) + let encodedRawBytes = hmacsha1(string: rawString, key: bytes) let encodedString = String(bytes: Base64Encoder.shared.encode(encodedRawBytes), encoding: .utf8) ?? "" diff --git a/Sources/HexavilleAuth/OAuth/OAuth2.swift b/Sources/HexavilleAuth/OAuth/OAuth2.swift index 94d53a4..1fd4ae1 100644 --- a/Sources/HexavilleAuth/OAuth/OAuth2.swift +++ b/Sources/HexavilleAuth/OAuth/OAuth2.swift @@ -65,7 +65,7 @@ public class OAuth2 { let queryItems = blockForCallbackURLQueryParams?(request) ?? [] let redirectURL = callbackURL.absoluteURL(withQueryItems: queryItems)!.absoluteString - let body: [String] = [ + let requestBody: [String] = [ "client_id=\(self.consumerKey)", "client_secret=\(self.consumerSecret)", "code=\(code)", @@ -73,26 +73,20 @@ public class OAuth2 { "redirect_uri=\(redirectURL)" ] - let request = Request( - method: .post, - url: url, - headers: [ - "Accept": "application/json", - "Content-Type": "application/x-www-form-urlencoded" - ], - body: body.joined(separator: "&").data - ) + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.addValue("application/json", forHTTPHeaderField: "Accept") + request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") + request.httpBody = requestBody.joined(separator: "&").data - let client = try HTTPClient(url: request.url) - try client.open() - let response = try client.request(request) + let (response, body) = try HTTPClient().send(request: request) guard (200..<300).contains(response.statusCode) else { - throw HexavilleAuthError.responseError(response) + throw HexavilleAuthError.responseError(response, body) } do { - let bodyDictionary = try JSONSerialization.jsonObject(with: response.body.asData(), options: []) as! [String: Any] + let bodyDictionary = try JSONSerialization.jsonObject(with: body, options: []) as! [String: Any] return try Credential(withDictionary: bodyDictionary) } catch { throw error diff --git a/Sources/HexavilleAuth/Providers/OAuth2/FacebookAuthorizationProvider.swift b/Sources/HexavilleAuth/Providers/OAuth2/FacebookAuthorizationProvider.swift index 5d6164a..655c847 100644 --- a/Sources/HexavilleAuth/Providers/OAuth2/FacebookAuthorizationProvider.swift +++ b/Sources/HexavilleAuth/Providers/OAuth2/FacebookAuthorizationProvider.swift @@ -40,20 +40,15 @@ public struct FacebookAuthorizationProvider: OAuth2AuthorizationProvidable { public func authorize(for request: Request) throws -> (Credential, LoginUser) { let credential = try self.getAccessToken(for: request) - let request = Request( - method: .get, - url: URL(string: "https://graph.facebook.com/me?fields=id,name,email,picture,gender&access_token=\(credential.accessToken)")! - ) + let url = URL(string: "https://graph.facebook.com/me?fields=id,name,email,picture,gender&access_token=\(credential.accessToken)") - let client = try HTTPClient(url: request.url) - try client.open() - let response = try client.request(request) + let (response, body) = try HTTPClient().send(url: url!) guard (200..<300).contains(response.statusCode) else { - throw HexavilleAuthError.responseError(response) + throw HexavilleAuthError.responseError(response, body) } - guard let json = try JSONSerialization.jsonObject(with: response.body.asData(), options: []) as? [String: Any] else { + guard let json = try JSONSerialization.jsonObject(with: body, options: []) as? [String: Any] else { throw FacebookAuthorizationProviderError.bodyShouldBeAJSON } diff --git a/Sources/HexavilleAuth/Providers/OAuth2/GithubAuthorizationProvider.swift b/Sources/HexavilleAuth/Providers/OAuth2/GithubAuthorizationProvider.swift index e98c0c7..b9f29e3 100644 --- a/Sources/HexavilleAuth/Providers/OAuth2/GithubAuthorizationProvider.swift +++ b/Sources/HexavilleAuth/Providers/OAuth2/GithubAuthorizationProvider.swift @@ -39,21 +39,14 @@ public struct GithubAuthorizationProvider: OAuth2AuthorizationProvidable { public func authorize(for request: Request) throws -> (Credential, LoginUser) { let credential = try self.getAccessToken(for: request) - - let request = Request( - method: .get, - url: URL(string: "https://api.github.com/user?access_token=\(credential.accessToken)")! - ) - - let client = try HTTPClient(url: request.url) - try client.open() - let response = try client.request(request) + let url = URL(string: "https://api.github.com/user?access_token=\(credential.accessToken)")! + let (response, body) = try HTTPClient().send(url: url) guard (200..<300).contains(response.statusCode) else { - throw HexavilleAuthError.responseError(response) + throw HexavilleAuthError.responseError(response, body) } - guard let json = try JSONSerialization.jsonObject(with: response.body.asData(), options: []) as? [String: Any] else { + guard let json = try JSONSerialization.jsonObject(with: body, options: []) as? [String: Any] else { throw GithubAuthorizationProviderError.bodyShouldBeAJSON } diff --git a/Sources/HexavilleAuth/URLRequest.swift b/Sources/HexavilleAuth/URLRequest.swift deleted file mode 100644 index 1ccae3e..0000000 --- a/Sources/HexavilleAuth/URLRequest.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// URLRequest.swift -// HexavilleAuth -// -// Created by Yuki Takei on 2017/07/12. -// -// - -import Foundation -import Prorsum - -extension Request.Method { - init(rawValue: String) { - switch rawValue.lowercased() { - case "get": - self = .get - case "post": - self = .post - case "put": - self = .put - case "patch": - self = .patch - case "delete": - self = .delete - case "head": - self = .head - default: - self = .other(method: rawValue) - } - } - - var rawValue: String { - switch self { - case .other(method: let method): - return method.uppercased() - default: - return "\(self)".uppercased() - } - } -} - -extension URLRequest { - func transform() -> Request { - var headers: Headers = [:] - for (key, value) in self.allHTTPHeaderFields ?? [:] { - headers[key] = value - } - let method = Request.Method(rawValue: self.httpMethod ?? "get") - return Request( - method: method, - url: self.url!, - headers: headers, - body: self.httpBody ?? Data() - ) - } -} - diff --git a/Sources/HexavilleAuth/URLResponse.swift b/Sources/HexavilleAuth/URLResponse.swift deleted file mode 100644 index c52472f..0000000 --- a/Sources/HexavilleAuth/URLResponse.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// URLResponse.swift -// HexavilleAuth -// -// Created by Yuki Takei on 2017/05/31. -// -// - -import Foundation -import Prorsum - -extension HTTPURLResponse { - func transform(withBodyData bodyData: Data) -> Response { - var headers: Headers = [:] - for el in allHeaderFields { - headers[el.key.description] = "\(el.value)" - } - - return Response( - status: Response.Status(statusCode: statusCode), - headers: headers, - body: bodyData - ) - } -} diff --git a/Sources/HexavilleAuthExample/main.swift b/Sources/HexavilleAuthExample/main.swift index 7524d99..0f8b440 100644 --- a/Sources/HexavilleAuthExample/main.swift +++ b/Sources/HexavilleAuthExample/main.swift @@ -89,7 +89,7 @@ app.use(auth) var router = Router() -router.use(.get, "/") { req, context in +router.use(.GET, "/") { req, context in return Response(body: "Welcome to Hexaville Auth") }