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

Introduced metadata when logging. Refactored logger access to be more… #9

Merged
merged 1 commit into from
May 22, 2024
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
22 changes: 21 additions & 1 deletion Sources/SwiftRestRequests/RestApiCaller.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import Foundation
import FoundationNetworking
#endif

import Logging


// MARK: - Protocols used by RestapiCaller

Expand Down Expand Up @@ -52,6 +54,8 @@ extension URLRequestInterceptor {
///
/// **NOTE:** Ensure to configure `App Transport Security` appropriately.
open class RestApiCaller : NSObject {

let logger = Logger.SwiftRestRequests.apiCaller

let session: URLSession
let baseUrl: URL
Expand Down Expand Up @@ -110,7 +114,13 @@ open class RestApiCaller : NSObject {
registerRequestInterceptor(LogNetworkInterceptor())
}
#endif


logger.info("Created RestApiCaller", metadata: [
"baseUrl": "\(baseUrl)",
"errorDeserializer": "\(String(describing: errorDeserializer))",
"headerGenerator": "\(String(describing: headerGenerator))",
"authorizer": "\(String(describing: authorizer))"
])
}

// MARK: Generic request dispatching methods
Expand Down Expand Up @@ -194,6 +204,13 @@ open class RestApiCaller : NSObject {
/// - Returns: The data returned by server and the corresponding `HTTPURLResponse`
private func dataTask(relativePath: String?, httpMethod: String, accept: String, payload: Data?, options: RestOptions) async throws -> (Data, HTTPURLResponse) {

logger.debug("Data Task started", metadata: [
"relativePath": "\(String(describing: relativePath))",
"httpMethod": "\(String(describing: httpMethod))",
"accept": "\(String(describing: accept))",
"requestTimeout": "\(String(describing: options.requestTimeoutSeconds))"
])

var restURL: URL;
if let relativeURL = relativePath {
restURL = baseUrl.appendingPathComponent(relativeURL)
Expand Down Expand Up @@ -305,6 +322,9 @@ open class RestApiCaller : NSObject {
/// Add request interceptor to the api caller.
/// - Parameter interceptor: The interceptor to be called
public func registerRequestInterceptor(_ interceptor: URLRequestInterceptor) {
logger.info("Registering request interceptor", metadata: [
"interceptor": "\(interceptor)"
])
if self.interceptors == nil {
self.interceptors = [URLRequestInterceptor]()
}
Expand Down
21 changes: 12 additions & 9 deletions Sources/SwiftRestRequests/interceptors/LogNetworkInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ open class LogNetworkInterceptor: URLRequestInterceptor {

static let noBody = "none"

let logger = Logger.SwiftRestRequests.interceptor

public func invokeRequest(request: inout URLRequest, for session: URLSession) {

guard let requestHeaders = request.allHTTPHeaderFields,
let headerData = try? JSONSerialization.data(withJSONObject: requestHeaders , options: .prettyPrinted),
let prettyJsonHeaders = String(data: headerData , encoding: .utf8) else {
Logger.interceptorLogger.warning("Something went wrong while converting headers to JSON data.")
logger.warning("Something went wrong while converting headers to JSON data.")
return
}

Expand All @@ -46,10 +48,10 @@ open class LogNetworkInterceptor: URLRequestInterceptor {
let url = request.url?.absoluteString ?? "nil"
let method = request.httpMethod ?? "nil"

Logger.interceptorLogger.info("Will invoke request: \(method) \(url)")

Logger.interceptorLogger.trace("HTTP request headers: \(prettyJsonHeaders)")
Logger.interceptorLogger.trace("HTTP request body: \(prettyJsonBody)")
logger.info("Send HTTP \(method) request \(url)", metadata: [ "method": "\(method)",
"url": "\(url)",
"headers": "\(prettyJsonHeaders)",
"body": "\(prettyJsonBody)"])
}

public func receiveResponse(data: Data, response: HTTPURLResponse, for session: URLSession) {
Expand All @@ -65,12 +67,13 @@ open class LogNetworkInterceptor: URLRequestInterceptor {

let url = response.url?.absoluteString ?? "nil"
let status = response.statusCode


Logger.interceptorLogger.info("Did receive response: \(url) -> \(status)")
logger.info("Received HTTP response \(url) -> \(status)", metadata: [
"url": "\(url)",
"status": "\(status)",
"headers": "\(prettyJsonHeaders)",
"body": "\(prettyJsonBody)"])

Logger.interceptorLogger.trace("HTTP response headers: \(prettyJsonHeaders)")
Logger.interceptorLogger.trace("HTTP response body: \(prettyJsonBody)")
}
}

Expand Down
8 changes: 5 additions & 3 deletions Sources/SwiftRestRequests/logging/Logger+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ extension Logger {

static let labelPrefix = "com.swisscom.swiftRestRequests."

public static var interceptorLogger = Logger(label: labelPrefix + "Interceptor")
public static var securityLogger = Logger(label: labelPrefix + "Security")
public static var apiCallerLogger = Logger(label: labelPrefix + "ApiCaller")
public struct SwiftRestRequests {
public static var interceptor = Logger(label: labelPrefix + "Interceptor")
public static var security = Logger(label: labelPrefix + "Security")
public static var apiCaller = Logger(label: labelPrefix + "ApiCaller")
}

}

Expand Down
12 changes: 8 additions & 4 deletions Sources/SwiftRestRequests/security/CertificateCAPinning.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,20 @@ open class CertificateCAPinning: NSObject, URLSessionDelegate {

let pinnedCACertificates: [SecCertificate]

let logger = Logger.SwiftRestRequests.security

public init(pinnedCACertificates: [SecCertificate]) {
self.pinnedCACertificates = pinnedCACertificates
Logger.securityLogger.info("Initialized CertificateCAPinning with \(pinnedCACertificates)")
logger.info("Initialized CertificateCAPinning", metadata: [
"pinnedCACertificates": "\(pinnedCACertificates)"
])
super.init()
}

public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) {

guard let serverTrust = challenge.protectionSpace.serverTrust else {
Logger.securityLogger.error("Could not get serverTrust! Will cancel authentication.")
logger.error("Could not get serverTrust. Will cancel authentication!!!")
return (.cancelAuthenticationChallenge, nil)
}

Expand All @@ -54,10 +58,10 @@ open class CertificateCAPinning: NSObject, URLSessionDelegate {
let status = SecTrustEvaluateWithError(serverTrust, &error)

if error == nil && status {
Logger.securityLogger.info("ServerTrust evaluation was successful. Will proceed.")
logger.info("ServerTrust evaluation was successful. Will proceed.")
return (.useCredential, URLCredential(trust: serverTrust))
} else {
Logger.securityLogger.error("ServerTrustevaluation evaluation failed. Will cancel the request.")
logger.error("ServerTrustevaluation evaluation failed. Will cancel the request!!!")
return (.cancelAuthenticationChallenge, nil)
}

Expand Down
10 changes: 6 additions & 4 deletions Sources/SwiftRestRequests/security/PublicKeyServerPinning.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ open class PublicKeyServerPinning: NSObject, URLSessionDelegate {

let pinnedPublicKeys: [SecKey]

let logger = Logger.SwiftRestRequests.security

public init(pinnedPublicKeys: [SecKey]) {
self.pinnedPublicKeys = pinnedPublicKeys
super.init()
Expand All @@ -41,7 +43,7 @@ open class PublicKeyServerPinning: NSObject, URLSessionDelegate {
public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) {

guard let serverTrust = challenge.protectionSpace.serverTrust else {
Logger.securityLogger.error("Could not get serverTrust! Will cancel authentication.")
logger.error("Could not get serverTrust. Will cancel authentication!!")
return(.cancelAuthenticationChallenge, nil)
}

Expand All @@ -51,14 +53,14 @@ open class PublicKeyServerPinning: NSObject, URLSessionDelegate {
if pinnedPublicKeys.contains(where: { publicKey in
publicKey == serverPublicKey
}) {
Logger.securityLogger.info("Trust evaluation was successful. The public key is known (pinned).")
logger.info("Trust evaluation was successful. The public key is known (pinned).")
return (.useCredential, URLCredential(trust: serverTrust))
} else {
Logger.securityLogger.error("Trust evaluation failed. The public key is unkown therfore cancel request.")
logger.error("Trust evaluation failed. The public key is unkown therefore cancel request!!!")
return (.cancelAuthenticationChallenge, nil)
}
} else {
Logger.securityLogger.error("Trust evaluation failed. No public key found on server. Cancel request.")
logger.error("Trust evaluation failed. No public key found on server. Cancel request!")
return (.cancelAuthenticationChallenge, nil)
}
}
Expand Down
11 changes: 9 additions & 2 deletions Sources/SwiftRestRequests/security/URLRequestAuthorizer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public protocol URLRequestAuthorizer {
/// `AuthorizationDelegate` used to configure HTTP header for basic authorization.
public class BasicRequestAuthorizer: URLRequestAuthorizer {

let logger = Logger.SwiftRestRequests.security

public let username: String
public let password: String

Expand All @@ -58,14 +60,18 @@ public class BasicRequestAuthorizer: URLRequestAuthorizer {
}

public func configureAuthorizationHeader(for urlRequest: inout URLRequest) {
Logger.securityLogger.trace("Set HTTP Authorization header is set to: \(self.headerValue)")
logger.trace("Set HTTP Authorization header", metadata: [
"urlRequest": "\(String(describing: urlRequest.url?.absoluteString))",
"Authorization": "\(headerValue)"])
urlRequest.setValue(self.headerValue, forHTTPHeaderField: "Authorization")
}
}

/// `AuthorizationDelegate` used to configure HTTP header for bearer authorization.
public class BearerReqeustAuthorizer: URLRequestAuthorizer {

let logger = Logger.SwiftRestRequests.security

// The token value (without `Bearer` prefix) to be used for the HTTP `Authorization` request header.
public var token: String

Expand All @@ -76,7 +82,8 @@ public class BearerReqeustAuthorizer: URLRequestAuthorizer {
}

public func configureAuthorizationHeader(for urlRequest: inout URLRequest) {
Logger.securityLogger.trace("Set HTTP Authorization header with bearer: \(self.token)")
logger.trace("Set HTTP Authorization header", metadata: [
"Authorization": "Bearer \(self.token)"])
urlRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
}
}
Expand Down
6 changes: 3 additions & 3 deletions Tests/SwiftRestRequestsTests/AbstractRestApiCallerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ class AbstractRestApiCallerTests: XCTestCase {
LoggingSystem.bootstrap(OSLogHandler.init)
#endif

Logger.securityLogger.logLevel = .trace
Logger.interceptorLogger.logLevel = .trace
Logger.apiCallerLogger.logLevel = .trace
Logger.SwiftRestRequests.security.logLevel = .trace
Logger.SwiftRestRequests.interceptor.logLevel = .trace
Logger.SwiftRestRequests.apiCaller.logLevel = .trace

}()

Expand Down
Loading