Skip to content
This repository has been archived by the owner on May 30, 2023. It is now read-only.

Commit

Permalink
Separate dependency on Kitura from Core component
Browse files Browse the repository at this point in the history
  • Loading branch information
Keno42 committed Oct 10, 2018
1 parent aa6ee83 commit 64c1ec3
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 103 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ ENV PATH_FOR_DEBUG ${PATH_FOR_DEBUG}
ARG LOG_TYPES
ENV LOG_TYPES ${LOG_TYPES}

# For Geocoding APi
# For Geocoding API
ARG GOOGLE_API_KEY
ENV GOOGLE_API_KEY ${GOOGLE_API_KEY}

Expand Down
24 changes: 12 additions & 12 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,26 @@
"repositoryURL": "https://github.com/IBM-Swift/BlueCryptor.git",
"state": {
"branch": null,
"revision": "4b10b2c97cb55d1f90b9f3a027419e71c3cef79b",
"version": "1.0.8"
"revision": "efdb770164a7d3b222d6dcb78f253633c38a2ec2",
"version": "1.0.10"
}
},
{
"package": "Socket",
"repositoryURL": "https://github.com/IBM-Swift/BlueSocket.git",
"state": {
"branch": null,
"revision": "85cfafa00baa149aa8b4ca1c57459d03d94940a9",
"version": "1.0.12"
"revision": "57092aba30b32ab6925ab2202672897ee5f34c40",
"version": "1.0.17"
}
},
{
"package": "SSLService",
"repositoryURL": "https://github.com/IBM-Swift/BlueSSLService.git",
"state": {
"branch": null,
"revision": "cfb35c421338d696ebfa600690073eeaf00e54ce",
"version": "1.0.12"
"revision": "763b3d88d07d3e7eb84ff537900f6db2b70fffdd",
"version": "1.0.17"
}
},
{
Expand Down Expand Up @@ -60,8 +60,8 @@
"repositoryURL": "https://github.com/IBM-Swift/Kitura",
"state": {
"branch": null,
"revision": "daa071669590584b6d9c277a2e3c465a904cd0d7",
"version": "2.4.1"
"revision": "bb19ec7ef8321082a234624cc76c11a17cd88971",
"version": "2.5.2"
}
},
{
Expand All @@ -87,8 +87,8 @@
"repositoryURL": "https://github.com/IBM-Swift/KituraContracts.git",
"state": {
"branch": null,
"revision": "83e5cb37871dce2b1e4ca61821c20b2c654c0c1c",
"version": "1.0.2"
"revision": "8e41f3fc81b662722020754f74a8fd8c7bdbbf3b",
"version": "1.1.0"
}
},
{
Expand All @@ -114,8 +114,8 @@
"repositoryURL": "https://github.com/IBM-Swift/SwiftyRequest.git",
"state": {
"branch": null,
"revision": "82d49d1bde19c4acfc63c276b67c8a25ac434e52",
"version": "1.1.2"
"revision": "617de05a8bea00acf0c1fd68fd8c42aa7a42c036",
"version": "1.1.3"
}
},
{
Expand Down
13 changes: 10 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,29 @@ let package = Package(
.library(
name: "Clova-CEK-SDK-Swift",
targets: ["Clova-CEK-SDK-Swift"]),
.library(
name: "Clova-CEK-SDK-Swift-Kitura",
targets: ["Clova-CEK-SDK-Swift-Kitura"]),
],
dependencies: [
.package(url: "https://github.com/IBM-Swift/Kitura", from: "2.4.0"),
.package(url: "https://github.com/IBM-Swift/LoggerAPI.git", from: "1.7.3"),
.package(url: "https://github.com/IBM-Swift/BlueCryptor.git", .upToNextMinor(from: "1.0.1")),
.package(url: "https://github.com/IBM-Swift/SwiftyRequest.git", .upToNextMajor(from: "1.0.0")),
.package(url: "https://github.com/IBM-Swift/OpenSSL", .upToNextMajor(from: "1.0.0")),
],
targets: [
.target(
name: "Clova-CEK-SDK-Swift",
dependencies: ["Kitura", "Cryptor", "OpenSSL"]),
dependencies: ["Cryptor", "OpenSSL"]),
.target(
name: "Clova-CEK-SDK-Swift-Kitura",
dependencies: ["Kitura", "Clova-CEK-SDK-Swift"]),
.target(
name: "WorldClock",
dependencies: ["Clova-CEK-SDK-Swift", "SwiftyRequest"]),
dependencies: ["Clova-CEK-SDK-Swift-Kitura", "SwiftyRequest"]),
.testTarget(
name: "Clova-CEK-SDK-SwiftTests",
dependencies: ["Clova-CEK-SDK-Swift"]),
dependencies: ["Clova-CEK-SDK-Swift", "LoggerAPI"]),
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* Copyright 2018 LINE Corporation
*
* LINE Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
**/

import Foundation
import Kitura
import KituraNet
import KituraContracts
import LoggerAPI
import Clova_CEK_SDK_Swift


// MARK: - Provides default implementation of sessionEndedHandler(request:)
public extension ExtensionRequestHandler {
func sessionEndedHandler(request: CEKRequest) {
}


// A function to parse body and invoke each handler
internal func handle(cekRequest: CEKRequest, request: RouterRequest, response: RouterResponse, next: @escaping () -> Void) throws {
let responseHandler: (CEKResponse) -> () = { cekResponse in
try! response.send(data: JSONEncoder().encode(cekResponse))
next()
}

switch cekRequest.request {
case .launch:
self.launchHandler(request: cekRequest, next: responseHandler)
case .intent:
self.intentHandler(request: cekRequest, next: responseHandler)
case .sessionEnded:
self.sessionEndedHandler(request: cekRequest)
response.status(.OK)
next()
}
}
}


/// Start a webhook server for the `Clova Extension Kit (CEK)`
///
/// - Parameters:
/// - port: Port to listen to
/// - paths: API path. It would respond to https://example.com/<path>
/// - handler: Handler for Business logic
public func startServer(port: Int, paths: [ApiPath], handler: ExtensionRequestHandler) {
let router: Router = Router()

// Routing
for path in (paths.filter{$0.getPath().isEmpty == false}) {
router.post(path.getPath()) { (request: RouterRequest, response: RouterResponse, next: @escaping () -> Void) in
guard let rawBody = try? request.readString(), let body = rawBody else {
response.status(.badRequest)
return
}

let cekRequest: CEKRequest

do {
cekRequest = try verifyRequest(for: path, body: body, signatureCek: request.headers["SignatureCEK"])
} catch {
response.status(.unauthorized)
return
}

do {
try handler.handle(cekRequest: cekRequest, request: request, response: response, next: next)
} catch {
response.status(.badRequest)
return
}
}
}

Kitura.addHTTPServer(onPort: port, with: router)
Kitura.run()
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@
**/

import Foundation
import Kitura
import KituraNet
import KituraContracts
import LoggerAPI


/// A dictionary that stores Certificates in memory per URL
//private var signatureCEKCertChain = [String : String]()
Expand All @@ -38,7 +33,7 @@ public enum ApiPath {
/// Internal function to only return the path string to be used for routing.
///
/// - Returns: Path string to be used for routing.
func getPath() -> String {
public func getPath() -> String {
switch self {
case .withVerification(path: let path, applicationId: _): return path
case .forDebug(path: let path): return path
Expand Down Expand Up @@ -68,6 +63,7 @@ public enum ApiPath {
}
}


/// A type that implements business logis for each CEK request types
public protocol ExtensionRequestHandler {

Expand All @@ -91,82 +87,42 @@ public protocol ExtensionRequestHandler {
func sessionEndedHandler(request: CEKRequest) -> ()
}

// MARK: - Provides default implementation of sessionEndedHandler(request:)
public extension ExtensionRequestHandler {
func sessionEndedHandler(request: CEKRequest) {
}

internal func decodeBody(body: String) throws -> CEKRequest {
return try JSONDecoder().decode(CEKRequest.self, from: body.data(using: .utf8)!)
}

// A function to parse body and invoke each handler
internal func handle(cekRequest: CEKRequest, request: RouterRequest, response: RouterResponse, next: @escaping () -> Void) throws {
let responseHandler: (CEKResponse) -> () = { cekResponse in
try! response.send(data: JSONEncoder().encode(cekResponse))
next()
}

switch cekRequest.request {
case .launch:
self.launchHandler(request: cekRequest, next: responseHandler)
case .intent:
self.intentHandler(request: cekRequest, next: responseHandler)
case .sessionEnded:
self.sessionEndedHandler(request: cekRequest)
response.status(.OK)
next()
}
}
/// A helper method to decode given message body to `CEKRequest` object.
///
/// - Parameter body: A raw string of the message body
/// - Returns: A `CEKRequest` object which is decoded from the message body
/// - throws: `DecodingError.dataCorrupted` if values requested from the payload are corrupted, or if the given data is not valid JSON.
/// - throws: An error if any value throws an error during decoding.
private func decodeBody(body: String) throws -> CEKRequest {
return try JSONDecoder().decode(CEKRequest.self, from: body.data(using: .utf8)!)
}

/// A simple struct that conforms to Error
private struct ParseError: Swift.Error {}

/// Start a webhook server for the `Clova Extension Kit (CEK)`
/// Verify given request for given path and return `CEKRequest`.
///
/// - Parameters:
/// - port: Port to listen to
/// - paths: API path. It would respond to https://example.com/<path>
/// - handler: Handler for Business logic
public func startServer(port: Int, paths: [ApiPath], handler: ExtensionRequestHandler) {
let router: Router = Router()

// Routing
for path in (paths.filter{$0.getPath().isEmpty == false}) {
router.post(path.getPath()) { (request: RouterRequest, response: RouterResponse, next: @escaping () -> Void) in
guard let rawBody = try? request.readString(), let body = rawBody else {
response.status(.badRequest)
return
}

let cekRequest: CEKRequest

if path.needVerification() {
do {
try SignatureVerifier.verifyRequest(body: body, headers: request.headers)
cekRequest = try handler.decodeBody(body: body)
guard let applicationId = cekRequest.context.system.application?.applicationId,
path.check(applicationId: applicationId) else {
throw VerificationError()
}
} catch {
response.status(.unauthorized)
return
}
} else {
cekRequest = try handler.decodeBody(body: body)
}

do {
try handler.handle(cekRequest: cekRequest, request: request, response: response, next: next)
} catch {
response.status(.badRequest)
return
/// - path: `ApiPath` object to specify a path to receive the request and the necessity of verification
/// - body: A raw string of the message body
/// - signatureCek: A signature if given
/// - Returns: A `CEKRequest` object which is decoded from the message body
/// - Throws: A `VerificationError` object
public func verifyRequest(for path: ApiPath, body: String, signatureCek: String?) throws -> CEKRequest {
let cekRequest: CEKRequest

if path.needVerification() {
do {
try SignatureVerifier.verifyRequest(body: body, signatureCek: signatureCek)
cekRequest = try decodeBody(body: body)
guard let applicationId = cekRequest.context.system.application?.applicationId,
path.check(applicationId: applicationId) else {
throw VerificationError()
}
} catch {
throw VerificationError()
}
} else {
cekRequest = try decodeBody(body: body)
}

Kitura.addHTTPServer(onPort: port, with: router)
Kitura.run()
return cekRequest
}
6 changes: 2 additions & 4 deletions Sources/Clova-CEK-SDK-Swift/SignatureVerifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
**/

import Foundation
import Kitura
import KituraNet
import Cryptor

#if os(macOS)
Expand Down Expand Up @@ -185,10 +183,10 @@ public class SignatureVerifier {

/// A function to verify a signature. throws
/// - Throws: VerificationError
public static func verifyRequest(body: String, headers: Headers) throws {
public static func verifyRequest(body: String, signatureCek: String?) throws {
_ = setupChecker // Run some logic only once

guard let signatureCek = headers["SignatureCEK"],
guard let signatureCek = signatureCek,
let signatureData = Data.init(base64Encoded: signatureCek) else {
throw VerificationError()
}
Expand Down
1 change: 1 addition & 0 deletions Sources/WorldClock/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import Foundation
import Clova_CEK_SDK_Swift
import Clova_CEK_SDK_Swift_Kitura
import LoggerAPI

// Make sure port number is defined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import LoggerAPI

class Clova_CEK_SDK_SwiftTests: XCTestCase {
private struct EmptyHandlers: ExtensionRequestHandler {
func sessionEndedHandler(request: CEKRequest) {}
func launchHandler(request: CEKRequest, next: @escaping (CEKResponse) -> ()) {}
func intentHandler(request: CEKRequest, next: @escaping (CEKResponse) -> ()) {}
}
Expand Down
8 changes: 1 addition & 7 deletions Tests/Clova-CEK-SDK-SwiftTests/SignatureVerifierTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

import XCTest
@testable import Clova_CEK_SDK_Swift
@testable import Kitura
import KituraNet

class SignatureVerifierTests: XCTestCase {

Expand All @@ -28,12 +26,8 @@ class SignatureVerifierTests: XCTestCase {

let signatureCEK = "iEW0Y9f/4HwCdHI7trS8qLY7XEiTc+lFurZHwCLKspJB0P7MMvcLpckUEIdSvRI9/GP2JfaI5J007dqKZqdmLQo+rSV9rkPnXDN8b1m2G5olQySi0WnOcOk3Dhded5Ts2zzrKINYd7VEIFnE1srN4O1UTfDOHzKcK9yV7anHuxw3X7MUU/KWdR4k3dVJz+kfQxnL2zhafUkC9X2luYah3ja0au3oLw81weizAA1+Y0FEXsx1/mhMLtZA+WLuGEKzuz3UM5V2UtfRBHKVRGnTSsUisR9U9WxUxBFo4RQJ4pK1r0uAeyszzJC4aMsLR9Ca4ysrpxb8rtLgfcurpcb3RQ=="

let headersContainer = HeadersContainer()
headersContainer.append("signaturecek", value: signatureCEK)
headersContainer.append("SignatureCEKCertChainUrl", value: "https://clova-cek-requests.line.me/cek/request-cert.crt")
let headers = Headers(headers: headersContainer)
do {
try SignatureVerifier.verifyRequest(body: body, headers: headers)
try SignatureVerifier.verifyRequest(body: body, signatureCek: signatureCEK)
} catch {
XCTFail()
}
Expand Down
Loading

0 comments on commit 64c1ec3

Please sign in to comment.