mirror of
https://github.com/AU-COVIDSafe/mobile-ios.git
synced 2025-04-19 04:55:19 +00:00
COVIDSafe code from version 1.1
This commit is contained in:
commit
3640e52eb2
330 changed files with 261540 additions and 0 deletions
25
CovidSafe/API/Certificates/CovidCertificates.swift
Normal file
25
CovidSafe/API/Certificates/CovidCertificates.swift
Normal file
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// Certificates.swift
|
||||
// CovidSafe
|
||||
//
|
||||
// Copyright © 2020 Australian Government. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
struct CovidCertificates {
|
||||
|
||||
static let AmazonRootCA1: SecCertificate = CovidCertificates.certificate(filename: "AmazonRootCA1")
|
||||
static let AmazonRootCA2: SecCertificate = CovidCertificates.certificate(filename: "AmazonRootCA2")
|
||||
static let AmazonRootCA3: SecCertificate = CovidCertificates.certificate(filename: "AmazonRootCA3")
|
||||
static let AmazonRootCA4: SecCertificate = CovidCertificates.certificate(filename: "AmazonRootCA4")
|
||||
static let SFSRootCA: SecCertificate = CovidCertificates.certificate(filename: "SFSRootCAG2")
|
||||
|
||||
private static func certificate(filename: String) -> SecCertificate {
|
||||
|
||||
let filePath = Bundle.main.path(forResource: filename, ofType: "cer")!
|
||||
let data = try! Data(contentsOf: URL(fileURLWithPath: filePath))
|
||||
let certificate = SecCertificateCreateWithData(nil, data as CFData)!
|
||||
|
||||
return certificate
|
||||
}
|
||||
}
|
47
CovidSafe/API/CovidNetworking.swift
Normal file
47
CovidSafe/API/CovidNetworking.swift
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// CovidNetworking.swift
|
||||
// CovidSafe
|
||||
//
|
||||
// Copyright © 2020 Australian Government. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Alamofire
|
||||
|
||||
final class CovidServerTrustManager: ServerTrustManager {
|
||||
override func serverTrustEvaluator(forHost host: String) throws -> ServerTrustEvaluating? {
|
||||
guard let evaluator = evaluators[host] else {
|
||||
for key in evaluators.keys {
|
||||
if (host.hasSuffix(key)) {
|
||||
return evaluators[key]
|
||||
}
|
||||
}
|
||||
if allHostsMustBeEvaluated {
|
||||
throw AFError.serverTrustEvaluationFailed(reason: .noRequiredEvaluator(host: host))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
return evaluator
|
||||
}
|
||||
}
|
||||
|
||||
class CovidNetworking {
|
||||
static private let validCerts = [CovidCertificates.AmazonRootCA1, CovidCertificates.AmazonRootCA2, CovidCertificates.AmazonRootCA3, CovidCertificates.AmazonRootCA4, CovidCertificates.SFSRootCA]
|
||||
private let evaluators = [
|
||||
"covidsafe.gov.au": PinnedCertificatesTrustEvaluator(certificates: CovidNetworking.validCerts)
|
||||
]
|
||||
|
||||
static let shared = CovidNetworking()
|
||||
public let session: Session
|
||||
|
||||
init() {
|
||||
let serverTrustPolicy = CovidServerTrustManager(evaluators: evaluators)
|
||||
session = Session(serverTrustManager:serverTrustPolicy)
|
||||
}
|
||||
}
|
||||
|
||||
enum APIError: Error {
|
||||
case ExpireSession
|
||||
case ServerError
|
||||
}
|
30
CovidSafe/API/CovidRequestRetrier.swift
Normal file
30
CovidSafe/API/CovidRequestRetrier.swift
Normal file
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// CovidRequestInterceptor.swift
|
||||
// CovidSafe
|
||||
//
|
||||
// Copyright © 2020 Australian Government. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Alamofire
|
||||
final class CovidRequestRetrier: Alamofire.RequestInterceptor {
|
||||
private let numRetries: Int
|
||||
private var retriesExecuted: Int = 0
|
||||
|
||||
init(retries: Int) {
|
||||
self.numRetries = retries
|
||||
}
|
||||
|
||||
func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
|
||||
guard let response = request.task?.response as? HTTPURLResponse, response.statusCode == 403 else {
|
||||
/// The request did not fail due to a 403 Forbidden response.
|
||||
let isServerTrustEvaluationError = error.asAFError?.isServerTrustEvaluationError ?? false
|
||||
if ( retriesExecuted >= numRetries || isServerTrustEvaluationError) {
|
||||
return completion(.doNotRetryWithError(error))
|
||||
}
|
||||
retriesExecuted += 1
|
||||
return completion(.retryWithDelay(1.0))
|
||||
}
|
||||
return completion(.doNotRetryWithError(error))
|
||||
}
|
||||
}
|
32
CovidSafe/API/DataUploadS3.swift
Normal file
32
CovidSafe/API/DataUploadS3.swift
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// DataUploadS3.swift
|
||||
// CovidSafe
|
||||
//
|
||||
// Copyright © 2020 Australian Government. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class DataUploadS3 {
|
||||
static func uploadJSONData(data: Data, presignedUrl: String, completion: @escaping (Bool, Swift.Error?) -> Void) {
|
||||
guard let url = URL(string: presignedUrl) else {
|
||||
completion(false, nil)
|
||||
return
|
||||
}
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "PUT"
|
||||
|
||||
let uploadRequest = CovidNetworking.shared.session.upload(data,
|
||||
with: request,
|
||||
interceptor: CovidRequestRetrier(retries: 3)
|
||||
).validate().response { (response) in
|
||||
switch response.result {
|
||||
case .success:
|
||||
completion(true, nil)
|
||||
case let .failure(error):
|
||||
completion(false, error)
|
||||
}
|
||||
}
|
||||
uploadRequest.resume()
|
||||
}
|
||||
}
|
53
CovidSafe/API/GetTempIdAPI.swift
Normal file
53
CovidSafe/API/GetTempIdAPI.swift
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// PhoneValidationAPI.swift
|
||||
// CovidSafe
|
||||
//
|
||||
// Copyright © 2020 Australian Government. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Alamofire
|
||||
import KeychainSwift
|
||||
|
||||
class GetTempIdAPI {
|
||||
|
||||
static func getTempId(completion: @escaping (String?, Int?, Swift.Error?) -> Void) {
|
||||
let keychain = KeychainSwift()
|
||||
guard let apiHost = PlistHelper.getvalueFromInfoPlist(withKey: "API_Host", plistName: "CovidSafe-config") else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let token = keychain.get("JWT_TOKEN") else {
|
||||
completion(nil, nil, nil)
|
||||
return
|
||||
}
|
||||
let headers: HTTPHeaders = [
|
||||
"Authorization": "Bearer \(token)"
|
||||
]
|
||||
CovidNetworking.shared.session.request("\(apiHost)/getTempId",
|
||||
method: .get,
|
||||
encoding: JSONEncoding.default,
|
||||
headers: headers,
|
||||
interceptor: CovidRequestRetrier(retries: 3)).validate().responseDecodable(of: TempIdResponse.self) { (response) in
|
||||
switch response.result {
|
||||
case .success:
|
||||
guard let tempIdResponse = response.value else { return }
|
||||
completion(tempIdResponse.tempId, tempIdResponse.expiryTime, nil)
|
||||
case let .failure(error):
|
||||
completion(nil, nil, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TempIdResponse: Decodable {
|
||||
let tempId: String
|
||||
let expiryTime: Int
|
||||
let refreshTime: Int
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case tempId
|
||||
case expiryTime
|
||||
case refreshTime
|
||||
}
|
||||
}
|
103
CovidSafe/API/InitiateUploadAPI.swift
Normal file
103
CovidSafe/API/InitiateUploadAPI.swift
Normal file
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// InitiateUploadAPI.swift
|
||||
// CovidSafe
|
||||
//
|
||||
// Copyright © 2020 Australian Government. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Alamofire
|
||||
|
||||
class InitiateUploadAPI {
|
||||
|
||||
static func requestUploadOTP(session: String, completion: @escaping (Bool, APIError?) -> Void) {
|
||||
guard let apiHost = PlistHelper.getvalueFromInfoPlist(withKey: "API_Host", plistName: "CovidSafe-config") else {
|
||||
return
|
||||
}
|
||||
let headers: HTTPHeaders = [
|
||||
"Authorization": "Bearer \(session)"
|
||||
]
|
||||
CovidNetworking.shared.session.request("\(apiHost)/requestUploadOtp", method: .get, headers: headers).validate().responseString { (response) in
|
||||
switch response.result {
|
||||
case .success:
|
||||
if response.value != nil {
|
||||
completion(true, nil)
|
||||
} else {
|
||||
completion(false, .ServerError)
|
||||
}
|
||||
case .failure(_):
|
||||
if (response.response?.statusCode == 403) {
|
||||
completion(false, .ExpireSession)
|
||||
} else {
|
||||
completion(false, .ServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func initiateUploadAPI(session: String, pin: String?, completion: @escaping (UploadResponse?, APIError?) -> Void) {
|
||||
guard let apiHost = PlistHelper.getvalueFromInfoPlist(withKey: "API_Host", plistName: "CovidSafe-config") else {
|
||||
return
|
||||
}
|
||||
var headers: HTTPHeaders = [
|
||||
"Authorization": "Bearer \(session)"
|
||||
]
|
||||
|
||||
if let uploadPin = pin {
|
||||
headers.add(name: "pin", value: uploadPin)
|
||||
}
|
||||
|
||||
guard pin != nil else {
|
||||
completion(nil, .ServerError)
|
||||
return
|
||||
}
|
||||
|
||||
CovidNetworking.shared.session.request("\(apiHost)/initiateDataUpload", method: .get, headers: headers, interceptor: CovidRequestRetrier(retries: 3)).validate().responseData { (response) in
|
||||
guard let respData = response.data else {
|
||||
completion(nil, .ServerError)
|
||||
return
|
||||
}
|
||||
switch response.result {
|
||||
case .success:
|
||||
do {
|
||||
let uploadResponse = try JSONDecoder().decode(UploadResponse.self, from: respData)
|
||||
completion(uploadResponse, nil)
|
||||
} catch {
|
||||
completion(nil, .ServerError)
|
||||
}
|
||||
case .failure(_):
|
||||
if (response.response?.statusCode == 403) {
|
||||
do {
|
||||
let uploadResponse = try JSONDecoder().decode(ErrorResponse.self, from: respData)
|
||||
if uploadResponse.message == "InvalidPin" {
|
||||
completion(nil, .ServerError)
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
completion(nil, .ServerError)
|
||||
return
|
||||
}
|
||||
completion(nil, .ExpireSession)
|
||||
} else {
|
||||
completion(nil, .ServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ErrorResponse: Decodable {
|
||||
let message: String
|
||||
}
|
||||
|
||||
struct UploadResponse: Decodable {
|
||||
let UploadLink: String
|
||||
let UploadPrefix: String
|
||||
let ExpiresIn: Int
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case UploadLink
|
||||
case UploadPrefix
|
||||
case ExpiresIn
|
||||
}
|
||||
}
|
62
CovidSafe/API/PhoneValidationAPI.swift
Normal file
62
CovidSafe/API/PhoneValidationAPI.swift
Normal file
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// PhoneValidationAPI.swift
|
||||
// CovidSafe
|
||||
//
|
||||
// Copyright © 2020 Australian Government. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Alamofire
|
||||
|
||||
class PhoneValidationAPI {
|
||||
|
||||
static func verifyPhoneNumber(regInfo: RegistrationRequest,
|
||||
completion: @escaping (String?, Swift.Error?) -> Void) {
|
||||
|
||||
guard let apiHost = PlistHelper.getvalueFromInfoPlist(withKey: "API_Host", plistName: "CovidSafe-config") else {
|
||||
return
|
||||
}
|
||||
let params = [
|
||||
"phone_number": regInfo.phoneNumber,
|
||||
"age": String(regInfo.age),
|
||||
"postcode": regInfo.postcode,
|
||||
"name": regInfo.fullName,
|
||||
"device_id": UIDevice.current.identifierForVendor!.uuidString
|
||||
]
|
||||
CovidNetworking.shared.session.request("\(apiHost)/initiateAuth",
|
||||
method: .post,
|
||||
parameters: params,
|
||||
encoding: JSONEncoding.default,
|
||||
interceptor: CovidRequestRetrier(retries:3)).validate().responseDecodable(of: AuthResponse.self) { (response) in
|
||||
switch response.result {
|
||||
case .success:
|
||||
guard let authResponse = response.value else { return }
|
||||
completion(authResponse.session, nil)
|
||||
case let .failure(error):
|
||||
completion(nil, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RegistrationRequest {
|
||||
var fullName: String
|
||||
var postcode: String
|
||||
var age: Int
|
||||
var isMinor: Bool
|
||||
var phoneNumber: String
|
||||
}
|
||||
|
||||
struct AuthResponse: Decodable {
|
||||
let session: String
|
||||
let challengeName: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case session
|
||||
case challengeName
|
||||
}
|
||||
}
|
||||
|
||||
protocol RegistrationHandler {
|
||||
var registrationInfo: RegistrationRequest? { get set }
|
||||
}
|
63
CovidSafe/API/RespondToAuthChallengeAPI.swift
Normal file
63
CovidSafe/API/RespondToAuthChallengeAPI.swift
Normal file
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// PhoneValidationAPI.swift
|
||||
// CovidSafe
|
||||
//
|
||||
// Copyright © 2020 Australian Government. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Alamofire
|
||||
|
||||
class RespondToAuthChallengeAPI {
|
||||
|
||||
static func respondToAuthChallenge(session: String,
|
||||
code: String,
|
||||
completion: @escaping (String?, ChallengeErrorResponse?) -> Void) {
|
||||
guard let apiHost = PlistHelper.getvalueFromInfoPlist(withKey: "API_Host", plistName: "CovidSafe-config") else {
|
||||
return
|
||||
}
|
||||
let params = [
|
||||
"session": session,
|
||||
"code": code
|
||||
]
|
||||
|
||||
CovidNetworking.shared.session.request("\(apiHost)/respondToAuthChallenge", method: .post, parameters: params, encoding: JSONEncoding.default).validate().responseDecodable(of: ChallengeResponse.self) { (response) in
|
||||
switch response.result {
|
||||
case .success:
|
||||
guard let challengeResponse = response.value else { return }
|
||||
completion(challengeResponse.token, nil)
|
||||
case .failure(_):
|
||||
guard let errorData = response.data else {
|
||||
completion(nil, nil)
|
||||
return
|
||||
}
|
||||
var errorResp: ChallengeErrorResponse
|
||||
do {
|
||||
let decoder = JSONDecoder()
|
||||
errorResp = try decoder.decode(ChallengeErrorResponse.self, from: errorData)
|
||||
} catch {
|
||||
DLog("error parsing response \(error)")
|
||||
completion(nil, nil)
|
||||
return
|
||||
}
|
||||
completion(nil, errorResp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ChallengeErrorResponse: Decodable, Error {
|
||||
let message: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case message
|
||||
}
|
||||
}
|
||||
|
||||
struct ChallengeResponse: Decodable {
|
||||
let token: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case token
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue