Skip to content

Commit

Permalink
Merge pull request #44 from WinnieLYT/winnie-lyt/tdoauth
Browse files Browse the repository at this point in the history
TDOAuth.swift refinement
  • Loading branch information
stury committed Oct 31, 2022
2 parents 2bb9947 + be7b8a0 commit 7648118
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 44 deletions.
4 changes: 4 additions & 0 deletions Example/TDOAuth.xcodeproj/project.pbxproj
Expand Up @@ -18,6 +18,7 @@
607FACEC1AFB9204008FA782 /* OAuth1Spec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* OAuth1Spec.swift */; };
9810E2CF42C71CB54FC9817B /* Pods_TDOAuth_watchOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9AE6DB313043BFD421E116D /* Pods_TDOAuth_watchOS.framework */; };
ED3586181D3AC6A5F479F62A /* Pods_TDOAuth_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1468F75DCB0D1331A62E245C /* Pods_TDOAuth_iOS.framework */; };
F2E921622907D5640093DBB4 /* TestTDOQueryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2E921612907D5640093DBB4 /* TestTDOQueryItem.swift */; };
FC71E7F42416E7A200CCC34C /* Compat.m in Sources */ = {isa = PBXBuildFile; fileRef = FC71E7F32416E7A200CCC34C /* Compat.m */; };
FCC067C9241C1940004997C8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC067C8241C1940004997C8 /* AppDelegate.swift */; };
FCC067CB241C1940004997C8 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCC067CA241C1940004997C8 /* ViewController.swift */; };
Expand Down Expand Up @@ -134,6 +135,7 @@
E5897B5C42516E19A5C8C497 /* Pods_TDOAuth_macOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TDOAuth_macOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E6AC1FB932F461059AB20B60 /* Pods-TDOAuth_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TDOAuth_Example.release.xcconfig"; path = "Target Support Files/Pods-TDOAuth_Example/Pods-TDOAuth_Example.release.xcconfig"; sourceTree = "<group>"; };
ECC894BE67C3786829385505 /* Pods-TDOAuth-TDOAuth-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TDOAuth-TDOAuth-iOS.release.xcconfig"; path = "Target Support Files/Pods-TDOAuth-TDOAuth-iOS/Pods-TDOAuth-TDOAuth-iOS.release.xcconfig"; sourceTree = "<group>"; };
F2E921612907D5640093DBB4 /* TestTDOQueryItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestTDOQueryItem.swift; sourceTree = "<group>"; };
F2EA75221BBDF7E5162DF034 /* Pods-TDOAuth_iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TDOAuth_iOS.release.xcconfig"; path = "Target Support Files/Pods-TDOAuth_iOS/Pods-TDOAuth_iOS.release.xcconfig"; sourceTree = "<group>"; };
FC71E7F22416E7A100CCC34C /* TDOAuth_Tests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TDOAuth_Tests-Bridging-Header.h"; sourceTree = "<group>"; };
FC71E7F32416E7A200CCC34C /* Compat.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Compat.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -310,6 +312,7 @@
FCCE3C4421FAE14200522CBD /* HMACSpec.swift */,
FCCE3C4621FAE1E000522CBD /* PlaintextSpec.swift */,
FCCE3C4821FAE23F00522CBD /* Utils.swift */,
F2E921612907D5640093DBB4 /* TestTDOQueryItem.swift */,
FC71E7F32416E7A200CCC34C /* Compat.m */,
FC71E7F22416E7A100CCC34C /* TDOAuth_Tests-Bridging-Header.h */,
607FACE91AFB9204008FA782 /* Supporting Files */,
Expand Down Expand Up @@ -820,6 +823,7 @@
607FACEC1AFB9204008FA782 /* OAuth1Spec.swift in Sources */,
FCCE3C4921FAE23F00522CBD /* Utils.swift in Sources */,
FC71E7F42416E7A200CCC34C /* Compat.m in Sources */,
F2E921622907D5640093DBB4 /* TestTDOQueryItem.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
92 changes: 92 additions & 0 deletions Example/Tests/TestTDOQueryItem.swift
@@ -0,0 +1,92 @@
// Copyright 2022, Yahoo Inc.
// Licensed under the terms of the MIT license. See LICENSE file in https://github.com/yahoo/TDOAuth for terms.

import XCTest
@testable import TDOAuth

class TestTDOQueryItem: XCTestCase {

static var paramDictionary: [AnyHashable: Any] {
return [
"key_string": "value_string",
"key_positive_int": Int(808),
"key_negative_int": Int(-394),
"key_double": Double(3.141592653589793),
"key_float": Float(3.1415925),
"key_dictionary": [
"dic_string": "dictionary",
"dic_number": Int(761)
],
"key_array": ["array1", "array2", "array3"],
"key_bool": true
]
}

func testStringParam() {
guard let queryItems = TDOQueryItem.getItems(from: Self.paramDictionary, isCollectionValuesSupported: true) else {
assertionFailure("TDOQueryItem: parse parameters failed.")
return
}
XCTAssert(queryItems.count == 8)

if let stringItem = queryItems.first(where: { $0.name == "key_string" }) {
XCTAssert(stringItem.stringValue == "value_string")
XCTAssert(stringItem.rawValue as? String == "value_string")
} else {
assertionFailure("TDOQueryItem: parse string item failed.")
}

if let positiveIntItem = queryItems.first(where: { $0.name == "key_positive_int" }) {
XCTAssert(positiveIntItem.stringValue == "808")
XCTAssert(positiveIntItem.rawValue as? Int == Int(808))
} else {
assertionFailure("TDOQueryItem: parse positive int item failed.")
}

if let negativeIntItem = queryItems.first(where: { $0.name == "key_negative_int" }) {
XCTAssert(negativeIntItem.stringValue == "-394")
XCTAssert(negativeIntItem.rawValue as? Int == Int(-394))
} else {
assertionFailure("TDOQueryItem: parse negative int item failed.")
}

if let doubleItem = queryItems.first(where: { $0.name == "key_double" }) {
XCTAssert(doubleItem.stringValue == "3.141592653589793")
XCTAssert(doubleItem.rawValue as? Double == Double(3.141592653589793))
} else {
assertionFailure("TDOQueryItem: parse double item failed.")
}

if let floatItem = queryItems.first(where: { $0.name == "key_float" }) {
XCTAssert(floatItem.stringValue == "3.1415925")
XCTAssert(floatItem.rawValue as? Float == Float(3.1415925))
} else {
assertionFailure("TDOQueryItem: parse float item failed.")
}

if let dictionaryItem = queryItems.first(where: { $0.name == "key_dictionary" }) {
XCTAssert(dictionaryItem.stringValue == "[\"dic_string\": \"dictionary\", \"dic_number\": 761]" || dictionaryItem.stringValue == "[\"dic_number\": 761, \"dic_string\": \"dictionary\"]")
XCTAssert((dictionaryItem.rawValue as? Dictionary<String, Any>)?["dic_string"] as? String == "dictionary")
XCTAssert((dictionaryItem.rawValue as? Dictionary<String, Any>)?["dic_number"] as? Int == 761)
} else {
assertionFailure("TDOQueryItem: parse dictionary item failed.")
}

if let arrayItem = queryItems.first(where: { $0.name == "key_array" }) {
XCTAssert(arrayItem.stringValue == "[\"array1\", \"array2\", \"array3\"]")
XCTAssert((arrayItem.rawValue as? Array)?[0] == "array1")
XCTAssert((arrayItem.rawValue as? Array)?[1] == "array2")
XCTAssert((arrayItem.rawValue as? Array)?[2] == "array3")
} else {
assertionFailure("TDOQueryItem: parse array item failed.")
}

if let boolItem = queryItems.first(where: { $0.name == "key_bool" }) {
XCTAssert(boolItem.stringValue == "true")
XCTAssert(boolItem.rawValue as? Bool == true)
} else {
assertionFailure("TDOQueryItem: parse bool item failed.")
}
}

}
110 changes: 67 additions & 43 deletions Source/compat/TDOAuth.swift
Expand Up @@ -52,12 +52,58 @@ let TDOAuthURLRequestTimeout = 30.0
// MARK: -

internal class TDOQueryItem : NSObject {
var name : String
var value: String

init(name: String, value: String) {
var name: String
var rawValue: Any
var stringValue: String?

init(name: String, rawValue: Any) {
self.name = name
self.value = value
self.rawValue = rawValue
self.stringValue = Self.getStringValue(by: rawValue)
}

private class func getStringValue(by rawValue: Any, isCollectionValuesSupported: Bool = true) -> String? {
if !isCollectionValuesSupported,
(rawValue is Array<Any> || rawValue is Dictionary<AnyHashable, Any>) {
return nil
}
var formattedValue: String?
switch rawValue {
case let losslessString as CustomStringConvertible:
formattedValue = losslessString.description
case let nsObject as NSObjectProtocol:
formattedValue = nsObject.description
case let arrayValue as Array<CustomStringConvertible>:
formattedValue = String(describing: arrayValue)
case let arrayValue as Array<NSObjectProtocol>:
formattedValue = String(describing: arrayValue)
case let dictionaryValue as Dictionary<AnyHashable, Any>:
formattedValue = String(describing: dictionaryValue)
default:
/// `value` is not a valid type - skipping
assertionFailure("TDOAuth: failed to casting the parameter: \(rawValue)")
}
return formattedValue
}

class func getItems(from dictionary: [AnyHashable: Any]?, isCollectionValuesSupported: Bool = true) -> [TDOQueryItem]? {
guard let dic = dictionary else { return nil }
var queryItems = [TDOQueryItem]()

for (key, value) in dic {
guard let key = key as? String else { continue }
if Self.getStringValue(by: value, isCollectionValuesSupported: isCollectionValuesSupported) == nil {
if isCollectionValuesSupported {
/// `value` is not a valid type - skipping
assertionFailure("TDOAuth: failed to casting the parameter: \(value) for the key: \(key)")
}
continue
}
let queryItem = TDOQueryItem(name: key, rawValue: value)
queryItems.append(queryItem)
}

return queryItems
}
}

Expand Down Expand Up @@ -99,7 +145,7 @@ internal class TDOQueryItem : NSObject {
headerValues:nil,
signatureMethod:.hmacSha1)
}

/**
Some services insist on HTTPS. Or maybe you don't want the data to be sniffed.
You can pass @"https" via the scheme parameter.
Expand Down Expand Up @@ -172,7 +218,7 @@ internal class TDOQueryItem : NSObject {
if let items = urlComponents.queryItems {
items.forEach { item in
if let value = item.value {
let queryItem = TDOQueryItem(name: item.name, value: value)
let queryItem = TDOQueryItem(name: item.name, rawValue: value)
queryItems.append(queryItem)
}
}
Expand All @@ -191,11 +237,11 @@ internal class TDOQueryItem : NSObject {
headerValues:nil,
signatureMethod:.hmacSha1)
}

/**
This method allows the caller to specify particular values for many different parameters such
as scheme, method, header values and alternate signature hash algorithms.
@p scheme may be any string value, generally "http" or "https".
@p requestMethod may be any string value. There is no validation, so remember that all
currently-defined HTTP methods are uppercase and the RFC specifies that the method
Expand Down Expand Up @@ -231,31 +277,9 @@ internal class TDOQueryItem : NSObject {
dataEncoding: TDOAuthContentType,
headerValues: [AnyHashable : Any]?,
signatureMethod: TDOAuthSignatureMethod) -> URLRequest! {

var queryItems = [TDOQueryItem]()

if let unencodedParameters = unencodedParameters {
for (key, value) in unencodedParameters {
guard let key = key as? String else { continue }
let formattedValue: String
switch value {
case let stringValue as String:
formattedValue = stringValue
case let intValue as Int:
formattedValue = String(intValue)
case let boolValue as Bool:
formattedValue = String(boolValue)
default:
/// `value` is not a valid type - skipping
continue
}
let queryItem = TDOQueryItem(name: key, value: formattedValue)
queryItems.append(queryItem)
}
}

return self.urlRequest(forPath: unencodedPathWithoutQuery,
queryItems: queryItems,
queryItems: TDOQueryItem.getItems(from: unencodedParameters, isCollectionValuesSupported: method == "POST") ?? [],
host: host,
consumerKey: consumerKey,
consumerSecret: consumerSecret,
Expand Down Expand Up @@ -313,7 +337,7 @@ internal class TDOQueryItem : NSObject {
guard let host = host, let unencodedPathWithoutQuery = unencodedPathWithoutQuery, let scheme = scheme, let method = method else {
return nil
}

// We don't use pcen as we don't want to percent encode eg. /, this is perhaps
// not the most all encompassing solution, but in practice it seems to work
// everywhere and means that programmer error is *much* less likely.
Expand Down Expand Up @@ -359,9 +383,9 @@ internal class TDOQueryItem : NSObject {
else if (dataEncoding == .jsonObject)
{
// This falls back to dictionary as not sure what's the proper action here.
var unencodedParameters = [String:String]()
var unencodedParameters = [String: Any]()
for queryItem in queryItems {
unencodedParameters[queryItem.name] = queryItem.value;
unencodedParameters[queryItem.name] = queryItem.rawValue
}
do {
let postbody = try JSONSerialization.data(withJSONObject: unencodedParameters)
Expand All @@ -379,11 +403,11 @@ internal class TDOQueryItem : NSObject {
}
else // invalid type
{
return nil;
return nil
}
}

return rq;
return rq
}

// METHOD ADAPTED FROM LEGACY OAUTH1 CLIENT
Expand All @@ -393,19 +417,19 @@ internal class TDOQueryItem : NSObject {
var queryString = String("")
var encodedParameters = [TDOQueryItem]()
for queryItem in unencodedParameters {
let enkey = TDPCEN(queryItem.name)
let envalue = TDPCEN(queryItem.value)
if let enkey = enkey, let envalue = envalue {
if let enkey = TDPCEN(queryItem.name),
let stringValue = queryItem.stringValue,
let envalue = TDPCEN(stringValue) {
if queryString.count > 0 {
queryString.append("&")
}
encodedParameters.append(TDOQueryItem(name: enkey, value: envalue))
encodedParameters.append(TDOQueryItem(name: enkey, rawValue: envalue))
queryString.append(enkey)
queryString.append("=")
queryString.append(envalue)
}
}
return queryString;
return queryString
}

// METHOD ADAPTED FROM LEGACY OAUTH1 CLIENT
Expand Down Expand Up @@ -433,7 +457,7 @@ internal class TDOQueryItem : NSObject {
}

/**
OAuth requires the UTC timestamp we send to be accurate. The user's device
may not be, and often isn't. To work around this you should set this to the
UTC timestamp that you get back in HTTP headers from OAuth servers.
Expand Down
2 changes: 1 addition & 1 deletion TDOAuth.podspec
Expand Up @@ -8,7 +8,7 @@

Pod::Spec.new do |s|
s.name = 'TDOAuth'
s.version = '1.6.1'
s.version = '1.6.2'
s.summary = 'Elegant, simple and compliant OAuth 1.x solution.'

s.description = <<-DESC
Expand Down

0 comments on commit 7648118

Please sign in to comment.