Skip to content

Commit

Permalink
Update serialization and equality
Browse files Browse the repository at this point in the history
  • Loading branch information
shilgapira committed Jan 7, 2024
1 parent c381bd6 commit c9d635b
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 374 deletions.
89 changes: 25 additions & 64 deletions src/internal/http/DescopeClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ class DescopeClient: HTTPClient {
var user: UserResponse?
var firstSeen: Bool

mutating func setValues(from response: HTTPURLResponse) {
mutating func setValues(from data: Data, response: HTTPURLResponse) throws {
guard let url = response.url, let fields = response.allHeaderFields as? [String: String] else { return }
let cookies = HTTPCookie.cookies(withResponseHeaderFields: fields, for: url)
for cookie in cookies where !cookie.value.isEmpty {
Expand All @@ -421,71 +421,32 @@ class DescopeClient: HTTPClient {
}

struct UserResponse: JSONResponse {
var userId: String
var loginIds: [String]
var name: String?
var picture: String?
var email: String?
var verifiedEmail: Bool = false
var phone: String?
var verifiedPhone: Bool = false
var createdTime: Int
var givenName: String?
var middleName: String?
var familyName: String?
var customAttributes: [String: Any] = [:]

enum CodingKeys: CodingKey {
case userId
case loginIds
case createdTime
case name
case picture
case email
case verifiedEmail
case phone
case verifiedPhone
case givenName
case middleName
case familyName
case customAttributes
// use a nested struct so we can let the compiler generate decoding for most members
struct UserFields: Decodable {
var userId: String
var loginIds: [String]
var createdTime: Int
var email: String?
var verifiedEmail: Bool?
var phone: String?
var verifiedPhone: Bool?
var name: String?
var givenName: String?
var middleName: String?
var familyName: String?
var picture: String?
}


var userFields: UserFields
var customAttributes: [String: Any] = [:]

init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
userId = try values.decode(String.self, forKey: .userId)
loginIds = try values.decode(Array<String>.self, forKey: .loginIds)
createdTime = try values.decode(Int.self, forKey: .createdTime)
if let value = try? values.decode(String?.self, forKey: .name) {
name = value
}
if let value = try? values.decode(String?.self, forKey: .picture) {
picture = value
}
if let value = try? values.decode(String?.self, forKey: .email) {
email = value
}
if let value = try? values.decode(Bool.self, forKey: .verifiedEmail) {
verifiedEmail = value
}
if let value = try? values.decode(String?.self, forKey: .phone) {
phone = value
}
if let value = try? values.decode(Bool?.self, forKey: .verifiedPhone) {
verifiedPhone = value
}
if let value = try? values.decode(String?.self, forKey: .givenName) {
givenName = value
}
if let value = try? values.decode(String?.self, forKey: .middleName) {
middleName = value
}
if let value = try? values.decode(String?.self, forKey: .familyName) {
familyName = value
}
if let value = try? values.nestedContainer(keyedBy: JSONCodingKeys.self, forKey: .customAttributes) {
customAttributes = decodeJson(container: value)
}
userFields = try UserFields(from: decoder)
}

mutating func setValues(from data: Data, response: HTTPURLResponse) throws {
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] ?? [:]
customAttributes = json["customAttributes"] as? [String: Any] ?? [:]
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/internal/http/HTTPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,19 +123,19 @@ class HTTPClient {
// JSON Response

protocol JSONResponse: Decodable {
mutating func setValues(from response: HTTPURLResponse)
mutating func setValues(from data: Data, response: HTTPURLResponse) throws
}

extension JSONResponse {
mutating func setValues(from response: HTTPURLResponse) {
mutating func setValues(from data: Data, response: HTTPURLResponse) throws {
// nothing by default
}
}

private func decodeJSON<T: JSONResponse>(data: Data, response: HTTPURLResponse) throws -> T {
do {
var val = try JSONDecoder().decode(T.self, from: data)
val.setValues(from: response)
try val.setValues(from: data, response: response)
return val
} catch {
throw DescopeError.decodeError.with(cause: error)
Expand Down
112 changes: 0 additions & 112 deletions src/internal/others/Internal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,115 +79,3 @@ class AuthorizationDelegate: NSObject, ASAuthorizationControllerDelegate {
completion = nil
}
}

// JSON

struct JSONCodingKeys: CodingKey {
var stringValue: String
var intValue: Int?

init(stringValue: String) {
self.stringValue = stringValue
}

init(intValue: Int) {
self.init(stringValue: "\(intValue)")
self.intValue = intValue
}
}

func decodeJson(container: KeyedDecodingContainer<JSONCodingKeys>) -> [String: Any] {
var decoded: [String: Any] = [:]
for key in container.allKeys {
if let boolValue = try? container.decode(Bool.self, forKey: key) {
decoded[key.stringValue] = boolValue
} else if let intValue = try? container.decode(Int.self, forKey: key) {
decoded[key.stringValue] = intValue
} else if let doubleValue = try? container.decode(Double.self, forKey: key) {
decoded[key.stringValue] = doubleValue
} else if let stringValue = try? container.decode(String.self, forKey: key) {
decoded[key.stringValue] = stringValue
} else if let nestedContainer = try? container.nestedContainer(keyedBy: JSONCodingKeys.self, forKey: key) {
decoded[key.stringValue] = decodeJson(container: nestedContainer)
} else if var nestedUnkeyedContainer = try? container.nestedUnkeyedContainer(forKey: key) {
decoded[key.stringValue] = decodeJson(unkeydContainer: &nestedUnkeyedContainer)
}
}
return decoded
}


func decodeJson(unkeydContainer: inout UnkeyedDecodingContainer) -> [Any] {
var decoded: [Any] = []
while unkeydContainer.isAtEnd == false {
if let value = try? unkeydContainer.decode(Bool.self) {
decoded.append(value)
} else if let value = try? unkeydContainer.decode(Int.self) {
decoded.append(value)
} else if let value = try? unkeydContainer.decode(Double.self) {
decoded.append(value)
} else if let value = try? unkeydContainer.decode(String.self) {
decoded.append(value)
} else if let _ = try? unkeydContainer.decode(String?.self) {
continue // Skip over `null` values
} else if let nestedContainer = try? unkeydContainer.nestedContainer(keyedBy: JSONCodingKeys.self) {
decoded.append(decodeJson(container: nestedContainer))
} else if var nestedUnkeyedContainer = try? unkeydContainer.nestedUnkeyedContainer() {
decoded.append(decodeJson(unkeydContainer: &nestedUnkeyedContainer))
}
}
return decoded
}

extension Dictionary<String, Any> {
func encodeJson(container: inout KeyedEncodingContainer<JSONCodingKeys>) throws {
try forEach({ (key, value) in
let encodingKey = JSONCodingKeys(stringValue: key)
switch value {
case let value as Bool:
try container.encode(value, forKey: encodingKey)
case let value as Int:
try container.encode(value, forKey: encodingKey)
case let value as Double:
try container.encode(value, forKey: encodingKey)
case let value as String:
try container.encode(value, forKey: encodingKey)
case let value as String?:
try container.encode(value, forKey: encodingKey)
case let value as [String: Any]:
var nestedContainer = container.nestedContainer(keyedBy: JSONCodingKeys.self, forKey: encodingKey)
try value.encodeJson(container: &nestedContainer)
case let value as [Any]:
var nestedUnkeyedContainer = container.nestedUnkeyedContainer(forKey: encodingKey)
try value.encodeJson(container: &nestedUnkeyedContainer)
default:
throw DescopeError.encodeError.with(message: "Invalid JSON value in dict: \(key): \(value)")
}
})
}
}

extension Array<Any> {
func encodeJson(container: inout UnkeyedEncodingContainer) throws {
for value in self {
switch value {
case let value as Bool:
try container.encode(value)
case let value as Int:
try container.encode(value)
case let value as Double:
try container.encode(value)
case let value as String:
try container.encode(value)
case let value as [String: Any]:
var nestedContainer = container.nestedContainer(keyedBy: JSONCodingKeys.self)
try value.encodeJson(container: &nestedContainer)
case let value as [Any]:
var nestedUnkeyedContainer = container.nestedUnkeyedContainer()
try value.encodeJson(container: &nestedUnkeyedContainer)
default:
throw DescopeError.encodeError.with(message: "Invalid JSON value in array: \(value)")
}
}
}
}
33 changes: 20 additions & 13 deletions src/internal/routes/Shared.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,40 @@ extension Route {
}
}

extension DescopeClient.UserResponse {
extension DescopeClient.UserResponse.UserFields {
func convert() -> DescopeUser {
let createdAt = Date(timeIntervalSince1970: TimeInterval(createdTime))
var me = DescopeUser(userId: userId, loginIds: loginIds, createdAt: createdAt, isVerifiedEmail: false, isVerifiedPhone: false)
var user = DescopeUser(userId: userId, loginIds: loginIds, createdAt: createdAt, isVerifiedEmail: false, isVerifiedPhone: false)
if let name, !name.isEmpty {
me.name = name
}
if let picture, let url = URL(string: picture) {
me.picture = url
user.name = name
}
if let email, !email.isEmpty {
me.email = email
me.isVerifiedEmail = verifiedEmail
user.email = email
user.isVerifiedEmail = verifiedEmail ?? false
}
if let phone, !phone.isEmpty {
me.phone = phone
me.isVerifiedPhone = verifiedPhone
user.phone = phone
user.isVerifiedPhone = verifiedPhone ?? false
}
if let givenName, !givenName.isEmpty {
me.givenName = givenName
user.givenName = givenName
}
if let middleName, !middleName.isEmpty {
me.middleName = middleName
user.middleName = middleName
}
if let familyName, !familyName.isEmpty {
me.familyName = familyName
user.familyName = familyName
}
if let picture, let url = URL(string: picture) {

Check failure

Code scanning / CodeQL

Cleartext transmission of sensitive information High

This operation transmits 'picture', which may contain unencrypted sensitive data from
call to passwordSignUp(loginId:password:details:)
.
This operation transmits 'picture', which may contain unencrypted sensitive data from
call to passwordSignIn(loginId:password:)
.
This operation transmits 'picture', which may contain unencrypted sensitive data from
call to passwordReplace(loginId:oldPassword:newPassword:)
.
user.picture = url
}
return user
}
}

extension DescopeClient.UserResponse {
func convert() -> DescopeUser {
var me = userFields.convert()
me.customAttributes = customAttributes
return me
}
Expand Down
Loading

0 comments on commit c9d635b

Please sign in to comment.