From 1437d1adbb2ece47af1ba4d28253765a08ff4f43 Mon Sep 17 00:00:00 2001 From: Marten Rebane <54431068+martenrebane@users.noreply.github.com> Date: Mon, 15 Jan 2024 19:49:37 +0200 Subject: [PATCH] Add proxy support --- .../xcschemes/xcschememanagement.plist | 4 +- MoppApp/MoppApp.xcodeproj/project.pbxproj | 31 +- MoppApp/MoppApp/Constants.swift | 2 + MoppApp/MoppApp/DefaultsHelper.swift | 69 ++- MoppApp/MoppApp/FileDownloader.swift | 10 +- .../InitializationViewController.swift | 3 +- MoppApp/MoppApp/LocalizationKeys.swift | 17 + MoppApp/MoppApp/ManualProxy.swift | 43 ++ MoppApp/MoppApp/MoppApp-Bridging-Header.h | 1 + MoppApp/MoppApp/MoppApp.swift | 10 +- MoppApp/MoppApp/ProxyCategoryCell.swift | 66 +++ .../ProxyChoiceTapGestureRecognizer.swift | 29 + MoppApp/MoppApp/ProxyViewController.swift | 147 +++++ MoppApp/MoppApp/Session.swift | 2 +- MoppApp/MoppApp/SessionCertificate.swift | 2 +- MoppApp/MoppApp/SessionStatus.swift | 2 +- MoppApp/MoppApp/Settings.storyboard | 507 ++++++++++++++++++ MoppApp/MoppApp/SettingsConfiguration.swift | 15 +- MoppApp/MoppApp/SettingsFieldCell.swift | 1 + MoppApp/MoppApp/SettingsProxyCell.swift | 350 ++++++++++++ MoppApp/MoppApp/SettingsViewController.swift | 37 +- .../SigningCategoryViewController.swift | 27 +- .../{ => Siva}/SivaCertViewController.swift | 2 + MoppApp/MoppApp/SmartIDSignature.swift | 6 +- MoppApp/MoppApp/en.lproj/Localizable.strings | 17 + MoppApp/MoppApp/et.lproj/Localizable.strings | 17 + MoppApp/MoppApp/ru.lproj/Localizable.strings | 17 + MoppLib/MoppLib.xcodeproj/project.pbxproj | 8 + MoppLib/MoppLib/MoppLibDigidocManager.h | 3 +- MoppLib/MoppLib/MoppLibDigidocManager.mm | 25 +- .../MoppLib/PublicInterface/MoppLibManager.h | 3 +- .../MoppLib/PublicInterface/MoppLibManager.m | 4 +- .../MoppLibProxyConfiguration.h | 35 ++ .../MoppLibProxyConfiguration.m | 40 ++ .../SkSigningLib.xcodeproj/project.pbxproj | 16 + .../xcschemes/xcschememanagement.plist | 2 +- .../Requests/Mobile-ID/RequestSession.swift | 18 +- .../Requests/Mobile-ID/RequestSignature.swift | 9 +- .../Requests/Smart-ID/SIDRequest.swift | 24 +- .../SkSigningLib/Utilities/Proxy/Proxy.swift | 40 ++ .../Utilities/Proxy/ProxyUtil.swift | 94 ++++ 41 files changed, 1697 insertions(+), 58 deletions(-) create mode 100644 MoppApp/MoppApp/ManualProxy.swift create mode 100644 MoppApp/MoppApp/ProxyCategoryCell.swift create mode 100644 MoppApp/MoppApp/ProxyChoiceTapGestureRecognizer.swift create mode 100644 MoppApp/MoppApp/ProxyViewController.swift create mode 100644 MoppApp/MoppApp/SettingsProxyCell.swift rename MoppApp/MoppApp/Signing/{ => Siva}/SivaCertViewController.swift (97%) create mode 100644 MoppLib/MoppLib/PublicInterface/MoppLibProxyConfiguration.h create mode 100644 MoppLib/MoppLib/PublicInterface/MoppLibProxyConfiguration.m create mode 100644 SkSigningLib/SkSigningLib/Utilities/Proxy/Proxy.swift create mode 100644 SkSigningLib/SkSigningLib/Utilities/Proxy/ProxyUtil.swift diff --git a/CryptoLib/CryptoLib.xcodeproj/xcuserdata/martenr.xcuserdatad/xcschemes/xcschememanagement.plist b/CryptoLib/CryptoLib.xcodeproj/xcuserdata/martenr.xcuserdatad/xcschemes/xcschememanagement.plist index 53fee9481..662173333 100644 --- a/CryptoLib/CryptoLib.xcodeproj/xcuserdata/martenr.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/CryptoLib/CryptoLib.xcodeproj/xcuserdata/martenr.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ CryptoLib.xcscheme_^#shared#^_ orderHint - 13 + 25 cdoc.xcscheme_^#shared#^_ orderHint - 18 + 26 diff --git a/MoppApp/MoppApp.xcodeproj/project.pbxproj b/MoppApp/MoppApp.xcodeproj/project.pbxproj index 594ea6cfd..c7357f0bd 100644 --- a/MoppApp/MoppApp.xcodeproj/project.pbxproj +++ b/MoppApp/MoppApp.xcodeproj/project.pbxproj @@ -188,6 +188,7 @@ DF3D29CF23F6B3CF007181B8 /* SkSigningLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF3D29CE23F6B3CF007181B8 /* SkSigningLib.framework */; }; DF3D29D023F6B3CF007181B8 /* SkSigningLib.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DF3D29CE23F6B3CF007181B8 /* SkSigningLib.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; DF3F62B026BD71180070F930 /* FileDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF3F62AF26BD71180070F930 /* FileDownloader.swift */; }; + DF41B5512B50736600594D01 /* ManualProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF41B5502B50736600594D01 /* ManualProxy.swift */; }; DF46181F2962E464003A1B56 /* FileUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF46181E2962E464003A1B56 /* FileUtil.swift */; }; DF4B8418284008CF005CB875 /* SignatureDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF4B8417284008CF005CB875 /* SignatureDetail.swift */; }; DF4B841A2840F8FC005CB875 /* CertificateDetailsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF4B84192840F8FC005CB875 /* CertificateDetailsCell.swift */; }; @@ -207,6 +208,10 @@ DF8A948A29AFD11A004F6E4B /* CharacterSet+Additions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF8A948929AFD11A004F6E4B /* CharacterSet+Additions.swift */; }; DF8EFB1A287498B900A96DE3 /* SettingsDefaultValueCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF8EFB19287498B900A96DE3 /* SettingsDefaultValueCell.swift */; }; DF900C972386768F00887385 /* tslFiles.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DF900C94238567CA00887385 /* tslFiles.bundle */; }; + DF9709692B4D79E400361544 /* ProxyCategoryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF9709682B4D79E400361544 /* ProxyCategoryCell.swift */; }; + DF97096B2B4D7A4600361544 /* ProxyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF97096A2B4D7A4600361544 /* ProxyViewController.swift */; }; + DF97096D2B4D7AA900361544 /* SettingsProxyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF97096C2B4D7AA900361544 /* SettingsProxyCell.swift */; }; + DF97096F2B4D92A700361544 /* ProxyChoiceTapGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF97096E2B4D92A700361544 /* ProxyChoiceTapGestureRecognizer.swift */; }; DF97A51129DC8CAB006FB917 /* ContainerAddAllButtonCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF97A51029DC8CAB006FB917 /* ContainerAddAllButtonCell.swift */; }; DFA56CEE2AEFEE45007D7F7E /* SettingsSivaCertCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFA56CED2AEFEE45007D7F7E /* SettingsSivaCertCell.swift */; }; DFA56CF22AF003F8007D7F7E /* RadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFA56CF12AF003F8007D7F7E /* RadioButton.swift */; }; @@ -531,6 +536,7 @@ DF3D299C23F5ADB7007181B8 /* MimeTypeExtractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MimeTypeExtractor.swift; sourceTree = ""; }; DF3D29CE23F6B3CF007181B8 /* SkSigningLib.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SkSigningLib.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DF3F62AF26BD71180070F930 /* FileDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileDownloader.swift; sourceTree = ""; }; + DF41B5502B50736600594D01 /* ManualProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManualProxy.swift; sourceTree = ""; }; DF46181E2962E464003A1B56 /* FileUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUtil.swift; sourceTree = ""; }; DF4B8417284008CF005CB875 /* SignatureDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignatureDetail.swift; sourceTree = ""; }; DF4B84192840F8FC005CB875 /* CertificateDetailsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificateDetailsCell.swift; sourceTree = ""; }; @@ -550,6 +556,10 @@ DF8A948929AFD11A004F6E4B /* CharacterSet+Additions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CharacterSet+Additions.swift"; sourceTree = ""; }; DF8EFB19287498B900A96DE3 /* SettingsDefaultValueCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDefaultValueCell.swift; sourceTree = ""; }; DF900C94238567CA00887385 /* tslFiles.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = tslFiles.bundle; sourceTree = ""; }; + DF9709682B4D79E400361544 /* ProxyCategoryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyCategoryCell.swift; sourceTree = ""; }; + DF97096A2B4D7A4600361544 /* ProxyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyViewController.swift; sourceTree = ""; }; + DF97096C2B4D7AA900361544 /* SettingsProxyCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsProxyCell.swift; sourceTree = ""; }; + DF97096E2B4D92A700361544 /* ProxyChoiceTapGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyChoiceTapGestureRecognizer.swift; sourceTree = ""; }; DF97A51029DC8CAB006FB917 /* ContainerAddAllButtonCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerAddAllButtonCell.swift; sourceTree = ""; }; DFA56CED2AEFEE45007D7F7E /* SettingsSivaCertCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSivaCertCell.swift; sourceTree = ""; }; DFA56CF12AF003F8007D7F7E /* RadioButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButton.swift; sourceTree = ""; }; @@ -820,6 +830,7 @@ DFA56CF12AF003F8007D7F7E /* RadioButton.swift */, DFA56CF32AF009E5007D7F7E /* RadioButton.xib */, DF50BBCE2AF06A0800C1A7D0 /* SivaChoiceTapGestureRecognizer.swift */, + DF97096E2B4D92A700361544 /* ProxyChoiceTapGestureRecognizer.swift */, ); name = "Custom views"; sourceTree = ""; @@ -957,6 +968,7 @@ DFD54CF429CDDB0600CD92C7 /* SettingsTSACertCell.swift */, DFBDF1F127D79B8B00A5CF3C /* SettingsStateCell.swift */, DFA56CED2AEFEE45007D7F7E /* SettingsSivaCertCell.swift */, + DF97096C2B4D7AA900361544 /* SettingsProxyCell.swift */, DF3756172AFABEB7006960C5 /* SettingsResetCell.swift */, ); name = Settings; @@ -1042,12 +1054,23 @@ name = Protocols; sourceTree = ""; }; + DF548B142B4C6CDE00340EE5 /* Proxy */ = { + isa = PBXGroup; + children = ( + DF9709682B4D79E400361544 /* ProxyCategoryCell.swift */, + DF97096A2B4D7A4600361544 /* ProxyViewController.swift */, + DF41B5502B50736600594D01 /* ManualProxy.swift */, + ); + name = Proxy; + path = ..; + sourceTree = ""; + }; DF716E932B45CFD60004DCB6 /* Signing */ = { isa = PBXGroup; children = ( DFB29EE22B4604BF005BEB34 /* Siva */, + DF548B142B4C6CDE00340EE5 /* Proxy */, DF716E942B45CFE90004DCB6 /* SigningCategoryViewController.swift */, - DF716E962B45D0F10004DCB6 /* SivaCertViewController.swift */, DF716E982B45D1FF0004DCB6 /* SigningCategoryCell.swift */, ); path = Signing; @@ -1057,6 +1080,7 @@ isa = PBXGroup; children = ( DFB29EE32B4604DB005BEB34 /* SivaCategoryCell.swift */, + DF716E962B45D0F10004DCB6 /* SivaCertViewController.swift */, ); path = Siva; sourceTree = ""; @@ -1659,6 +1683,7 @@ C56C6206201A189600CCC2A5 /* MenuHeaderCell.swift in Sources */, DFBF242E23DB0923008DD8D0 /* LOTLDecoder.swift in Sources */, DF24E9D62AD5ECFB00471FFA /* DiagnosticError.swift in Sources */, + DF97096B2B4D7A4600361544 /* ProxyViewController.swift in Sources */, DF3950602AD766DF005D96AC /* KeychainUtil.swift in Sources */, C50DCD441FDD7E4A00D48E16 /* DataFilePreviewViewController.swift in Sources */, C50FC431224BA3B20041925C /* ScreenDisguise.swift in Sources */, @@ -1668,6 +1693,7 @@ C506EC7C1FB9CBFD00E07226 /* Constants.swift in Sources */, DFD54CF529CDDB0600CD92C7 /* SettingsTSACertCell.swift in Sources */, C58FCD5B206505BE00FE3B57 /* MyeIDStatusViewController.swift in Sources */, + DF41B5512B50736600594D01 /* ManualProxy.swift in Sources */, DF32A67D2909B20C00AE5F82 /* AlertUtil.swift in Sources */, C5310F2620C1777B004D3E78 /* MenuLanguageCell.swift in Sources */, DF4B841A2840F8FC005CB875 /* CertificateDetailsCell.swift in Sources */, @@ -1708,6 +1734,7 @@ 39CDA7EE20ADBE93006E2E9F /* CryptoViewController.swift in Sources */, C52E82781FC1D3ED0074B280 /* MenuViewController.swift in Sources */, C50DCCC71FC32A2500D48E16 /* MyeIDViewController.swift in Sources */, + DF9709692B4D79E400361544 /* ProxyCategoryCell.swift in Sources */, DF26DC8326CD45670073ABA7 /* SivaUtil.swift in Sources */, DF35B1922AEAE32A0018AC7E /* PersonalCodeField.swift in Sources */, DFF3C3B5233231F20079458A /* Notification+Additions.swift in Sources */, @@ -1720,6 +1747,7 @@ C5964CE820079AE0001FE732 /* TabButton.swift in Sources */, 3921CA9920B2F5B200BF3178 /* PreviewActions.swift in Sources */, DFBA32A629A5A64000788A87 /* CancelUtil.swift in Sources */, + DF97096D2B4D7AA900361544 /* SettingsProxyCell.swift in Sources */, DFBDF1FC27DF6BA800A5CF3C /* MobileIDParameters.swift in Sources */, C56C620F201A2B6500CCC2A5 /* RecentContainersHeaderCell.swift in Sources */, C593D9F4208F3526000B3BF6 /* SettingsChoiceView.swift in Sources */, @@ -1738,6 +1766,7 @@ DFD54CF729D1CBAF00CD92C7 /* CertUtil.swift in Sources */, 4E59080024B0F914001B23A6 /* SmartIDEditViewController.swift in Sources */, DFD8BEF7291C434500FE8F07 /* FontUtil.swift in Sources */, + DF97096F2B4D92A700361544 /* ProxyChoiceTapGestureRecognizer.swift in Sources */, DFB29EE42B4604DB005BEB34 /* SivaCategoryCell.swift in Sources */, C50DCD1B1FD1576B00D48E16 /* SigningTableViewHeaderView.swift in Sources */, C54EA73A2057E58D0039AC78 /* MoppButton.swift in Sources */, diff --git a/MoppApp/MoppApp/Constants.swift b/MoppApp/MoppApp/Constants.swift index 9be53ee1b..405ec0f14 100644 --- a/MoppApp/MoppApp/Constants.swift +++ b/MoppApp/MoppApp/Constants.swift @@ -43,6 +43,8 @@ let kAlternativeDisplayTextFormat = "UCS-2" let kDigestMethodSHA256 = "http://www.w3.org/2001/04/xmlenc#sha256" let kHashType = "SHA256" +let proxyPasswordKey = "proxyPasswordKey" + // Mobile-ID and Smart-ID polling interval let kDefaultTimeoutMs = 5000 let kDefaultTimeoutS = 5 diff --git a/MoppApp/MoppApp/DefaultsHelper.swift b/MoppApp/MoppApp/DefaultsHelper.swift index 9d41d9561..cf8821d4f 100644 --- a/MoppApp/MoppApp/DefaultsHelper.swift +++ b/MoppApp/MoppApp/DefaultsHelper.swift @@ -21,6 +21,9 @@ * */ +import Foundation +import SkSigningLib + let ContainerFormatAdoc = "adoc" let ContainerFormatBdoc = "bdoc" let ContainerFormatEdoc = "edoc" @@ -46,6 +49,7 @@ let CrashlyticsAlwaysSend = "Always" let CrashlyticsNeverSend = "Never" let CrashlyticsDefault = "Default" // Keys +fileprivate let kFirstStartKey = "kFirstStartKey" fileprivate let kSignMethodKey = "kSignMethodKey" fileprivate let kPhoneNumberKey = "kPhoneNumberKey" fileprivate let kIDCodeKey = "kIDCodeKey" @@ -75,19 +79,35 @@ fileprivate let kIsRoleAndAddressEnabled = "kIsRoleAndAddressEnabled" fileprivate let kSivaAccessState = "kSivaAccessState" fileprivate let kSivaUrl = "kSivaUrl" fileprivate let kSivaFileCertName = "kSivaFileCertName" +fileprivate let kProxySetting = "kProxySetting" +fileprivate let kIsProxyForSSLEnabled = "kIsProxyForSSLEnabled" +fileprivate let kProxyHost = "kProxyHost" +fileprivate let kProxyPort = "kProxyPort" +fileprivate let kProxyUsername = "kProxyUsername" class DefaultsHelper { static func setDefaultKeys() { UserDefaults.standard.register( defaults: [ + kFirstStartKey: true, kSettingsDefaultSwitchKey: true, kMobileIdRememberMeKey: true, - kSmartIdRememberMeKey: true + kSmartIdRememberMeKey: true, + kIsProxyForSSLEnabled: true ] ) } + class var firstStart: Bool { + set { + UserDefaults.standard.set(newValue, forKey: kFirstStartKey) + } + get { + return (UserDefaults.standard.bool(forKey: kFirstStartKey)) + } + } + class var signMethod: String { set { UserDefaults.standard.set(newValue, forKey: kSignMethodKey) @@ -334,4 +354,51 @@ class DefaultsHelper return UserDefaults.standard.value(forKey: kSivaFileCertName) as? String } } + + class var proxySetting: ProxySetting { + set { + UserDefaults.standard.set(newValue.rawValue, forKey: kProxySetting) + } + get { + return ProxySetting(rawValue: UserDefaults.standard.value(forKey: kProxySetting) as? String ?? "") ?? .noProxy + } + } + + class var isProxyForSSLEnabled: Bool { + set { + UserDefaults.standard.set(newValue, forKey: kIsProxyForSSLEnabled) + } + get { + return UserDefaults.standard.bool(forKey: kIsProxyForSSLEnabled) + } + } + + class var proxyHost: String? { + set { + UserDefaults.standard.set(newValue, forKey: kProxyHost) + } + get { + return UserDefaults.standard.value(forKey: kProxyHost) as? String + } + } + + class var proxyPort: Int { + set { + // Set default port 80 + UserDefaults.standard.set(newValue == 0 ? 80 : newValue, forKey: kProxyPort) + } + get { + let port = UserDefaults.standard.integer(forKey: kProxyPort) + return port == 0 ? 80 : port + } + } + + class var proxyUsername: String? { + set { + UserDefaults.standard.set(newValue, forKey: kProxyUsername) + } + get { + return UserDefaults.standard.value(forKey: kProxyUsername) as? String + } + } } diff --git a/MoppApp/MoppApp/FileDownloader.swift b/MoppApp/MoppApp/FileDownloader.swift index 8a63a97fa..df494bba7 100644 --- a/MoppApp/MoppApp/FileDownloader.swift +++ b/MoppApp/MoppApp/FileDownloader.swift @@ -22,13 +22,21 @@ */ import Foundation +import SkSigningLib class FileDownloader: NSObject, URLSessionDelegate { static let shared = FileDownloader() func downloadFile(url: URL, completion: @escaping (URL?) -> Void) { - let downloadTask: URLSessionDownloadTask = URLSession(configuration: .default, delegate: self, delegateQueue: nil).downloadTask(with: url) { (fileTempUrl, response, error) in + let manualProxyConf = ManualProxy.getManualProxyConfiguration() + var urlSessionConfiguration = URLSessionConfiguration.default + ProxyUtil.configureURLSessionWithProxy(urlSessionConfiguration: &urlSessionConfiguration, manualProxyConf: manualProxyConf) + + var request = URLRequest(url: url) + ProxyUtil.setProxyAuthorizationHeader(request: &request, urlSessionConfiguration: urlSessionConfiguration, manualProxyConf: manualProxyConf) + + let downloadTask: URLSessionDownloadTask = URLSession.shared.downloadTask(with: request) { (fileTempUrl, response, error) in if error != nil { printLog("Unable to download file: \(error?.localizedDescription ?? "Unable to display error")"); return completion(nil) } if let fileTempUrl: URL = fileTempUrl { do { diff --git a/MoppApp/MoppApp/InitializationViewController.swift b/MoppApp/MoppApp/InitializationViewController.swift index 525594694..8a9a0dcd3 100644 --- a/MoppApp/MoppApp/InitializationViewController.swift +++ b/MoppApp/MoppApp/InitializationViewController.swift @@ -65,7 +65,8 @@ class InitializationViewController : UIViewController { }, usingTestDigiDocService: useTestDDS, andTSUrl: DefaultsHelper.timestampUrl ?? MoppConfiguration.getMoppLibConfiguration().tsaurl, - withMoppConfiguration: MoppConfiguration.getMoppLibConfiguration() + withMoppConfiguration: MoppConfiguration.getMoppLibConfiguration(), + andProxyConfiguration: ManualProxy.getMoppLibProxyConfiguration() ) } diff --git a/MoppApp/MoppApp/LocalizationKeys.swift b/MoppApp/MoppApp/LocalizationKeys.swift index 5470cf442..fadaf68ec 100644 --- a/MoppApp/MoppApp/LocalizationKeys.swift +++ b/MoppApp/MoppApp/LocalizationKeys.swift @@ -314,6 +314,15 @@ enum LocKey : String case settingsTimestampCertAddCertificateButton = "settings-timestamp-cert-add-certificate-button" case settingsTimestampCertShowCertificateButton = "settings-timestamp-cert-show-certificate-button" case settingsSivaServiceTitle = "settings-siva-service-title" + case settingsProxyTitle = "settings-proxy-title" + case settingsProxyNoProxy = "settings-proxy-no-proxy" + case settingsProxyUseSystem = "settings-proxy-use-system" + case settingsProxyUseManual = "settings-proxy-use-manual" + case settingsProxyHost = "settings-proxy-host" + case settingsProxyPort = "settings-proxy-port" + case settingsProxyUsername = "settings-proxy-username" + case settingsProxyPassword = "settings-proxy-password" + case settingsProxyAllowSSL = "settings-proxy-allow-ssl" case settingsSivaDefaultAccessTitle = "settings-siva-default-access-title" case settingsSivaDefaultManualAccessTitle = "settings-siva-default-manual-access-title" case settingsSivaDefaultCertificateTitle = "settings-siva-default-certificate-title" @@ -492,4 +501,12 @@ enum LocKey : String case voiceControlDisableDefaultTimestampingService = "voice-control-disable-default-timestamping-service"; case voiceControlEnableRoleAndAddress = "voice-control-enable-role-and-address"; case voiceControlDisableRoleAndAddress = "voice-control-disable-role-and-address"; + case voiceControlProxyNoProxy = "voice-control-no-proxy"; + case voiceControlProxySystemProxy = "voice-control-proxy-system"; + case voiceControlProxyManualProxy = "voice-control-proxy-manual"; + case voiceControlProxyHost = "voice-control-proxy-host"; + case voiceControlProxyPort = "voice-control-proxy-port"; + case voiceControlProxyUsername = "voice-control-proxy-username"; + case voiceControlProxyPassword = "voice-control-proxy-password"; + case voiceControlProxyDisallowSsl = "voice-control-proxy-disallow-ssl"; } diff --git a/MoppApp/MoppApp/ManualProxy.swift b/MoppApp/MoppApp/ManualProxy.swift new file mode 100644 index 000000000..5366b66aa --- /dev/null +++ b/MoppApp/MoppApp/ManualProxy.swift @@ -0,0 +1,43 @@ +// +// ManualProxy.swift +// MoppApp +// +/* + * Copyright 2017 - 2023 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +import Foundation +import MoppLib +import SkSigningLib + +public class ManualProxy { + + public static func getManualProxyConfiguration() -> Proxy { + return Proxy( + host: DefaultsHelper.proxyHost ?? "", + port: DefaultsHelper.proxyPort, + username: DefaultsHelper.proxyUsername ?? "", + password: KeychainUtil.retrieve(key: proxyPasswordKey) ?? "", + isSSLEnabled: DefaultsHelper.isProxyForSSLEnabled) + } + + public static func getMoppLibProxyConfiguration() -> MoppLibProxyConfiguration { + let manualProxy = ManualProxy.getManualProxyConfiguration() + return MoppLibProxyConfiguration(configuration: manualProxy.host, port: NSNumber(value: manualProxy.port), username: manualProxy.username, password: manualProxy.password) + } +} diff --git a/MoppApp/MoppApp/MoppApp-Bridging-Header.h b/MoppApp/MoppApp/MoppApp-Bridging-Header.h index 406a8307a..b8e91a2af 100644 --- a/MoppApp/MoppApp/MoppApp-Bridging-Header.h +++ b/MoppApp/MoppApp/MoppApp-Bridging-Header.h @@ -6,3 +6,4 @@ #import #import #import +#import diff --git a/MoppApp/MoppApp/MoppApp.swift b/MoppApp/MoppApp/MoppApp.swift index a3ec594c3..457d3e15f 100644 --- a/MoppApp/MoppApp/MoppApp.swift +++ b/MoppApp/MoppApp/MoppApp.swift @@ -24,6 +24,7 @@ import Foundation import FirebaseCrashlytics import Firebase +import SkSigningLib class MoppApp: UIApplication, URLSessionDelegate, URLSessionDownloadDelegate { @@ -96,6 +97,11 @@ class MoppApp: UIApplication, URLSessionDelegate, URLSessionDownloadDelegate { Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(false) DefaultsHelper.setDefaultKeys() + + if DefaultsHelper.firstStart { + DefaultsHelper.firstStart = false + KeychainUtil.remove(key: proxyPasswordKey) + } window = UIWindow(frame: UIScreen.main.bounds) window?.backgroundColor = UIColor.white @@ -438,7 +444,9 @@ class MoppApp: UIApplication, URLSessionDelegate, URLSessionDownloadDelegate { func handleEventsForBackgroundURLSession(identifier: String, completionHandler: @escaping () -> Void) { downloadCompletion = completionHandler - let conf = URLSessionConfiguration.background(withIdentifier: identifier) + let manualProxyConf = ManualProxy.getManualProxyConfiguration() + var conf = URLSessionConfiguration.background(withIdentifier: identifier) + ProxyUtil.configureURLSessionWithProxy(urlSessionConfiguration: &conf, manualProxyConf: manualProxyConf) let session = URLSession(configuration: conf, delegate: self, delegateQueue: nil) session.getTasksWithCompletionHandler({(_ dataTasks: [URLSessionDataTask], _ uploadTasks: [URLSessionUploadTask], _ downloadTasks: [URLSessionDownloadTask]) -> Void in }) diff --git a/MoppApp/MoppApp/ProxyCategoryCell.swift b/MoppApp/MoppApp/ProxyCategoryCell.swift new file mode 100644 index 000000000..15e2d189a --- /dev/null +++ b/MoppApp/MoppApp/ProxyCategoryCell.swift @@ -0,0 +1,66 @@ +// +// ProxyCategoryCell.swift +// MoppApp +// +/* + * Copyright 2017 - 2023 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +import UIKit + +class ProxyCategoryCell: UITableViewCell { + + + @IBOutlet weak var accessToProxySettings: ScaledButton! + + @IBAction func openAccessToProxySettings(_ sender: ScaledButton) { + openAccessToProxyView() + } + + weak var topViewController: UIViewController? + + override func awakeFromNib() { + updateUI() + + guard let accessToProxyUISettings: ScaledButton = accessToProxySettings else { + printLog("Unable to get accessToProxySettings") + return + } + + if UIAccessibility.isVoiceOverRunning { + self.accessibilityElements = [accessToProxyUISettings] + } + } + + func populate() { + updateUI() + } + + func updateUI() { + self.accessToProxySettings.setTitle(L(.settingsProxyTitle)) + self.accessToProxySettings.accessibilityLabel = self.accessToProxySettings.titleLabel?.text?.lowercased() + self.accessToProxySettings.mediumFont() + } + + private func openAccessToProxyView() { + let accessToProxyViewController = UIStoryboard.settings.instantiateViewController(of: ProxyViewController.self) + accessToProxyViewController.modalPresentationStyle = .custom + accessToProxyViewController.modalTransitionStyle = .crossDissolve + topViewController?.present(accessToProxyViewController, animated: true) + } +} diff --git a/MoppApp/MoppApp/ProxyChoiceTapGestureRecognizer.swift b/MoppApp/MoppApp/ProxyChoiceTapGestureRecognizer.swift new file mode 100644 index 000000000..b3b1def5b --- /dev/null +++ b/MoppApp/MoppApp/ProxyChoiceTapGestureRecognizer.swift @@ -0,0 +1,29 @@ +// +// ProxyChoiceTapGestureRecognizer.swift +// MoppApp +// +/* + * Copyright 2017 - 2023 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +import Foundation +import SkSigningLib + +class ProxyChoiceTapGestureRecognizer: UITapGestureRecognizer { + var proxySetting: ProxySetting = .noProxy +} diff --git a/MoppApp/MoppApp/ProxyViewController.swift b/MoppApp/MoppApp/ProxyViewController.swift new file mode 100644 index 000000000..8d861dd07 --- /dev/null +++ b/MoppApp/MoppApp/ProxyViewController.swift @@ -0,0 +1,147 @@ +// +// ProxyViewController.swift +// MoppApp +// +/* + * Copyright 2017 - 2023 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +import UIKit + +class ProxyViewController: MoppViewController { + + @IBOutlet weak var tableView: UITableView! + + var currentlyEditingCell: IndexPath? + var currentlyEditingField: UITextField? + + enum Section { + case header + case fields + } + + enum FieldId { + case proxy + } + + var sections:[Section] = [.header, .fields] + + var fields: [FieldId] = [ + .proxy + ] + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + view.backgroundColor = .gray.withAlphaComponent(0.5) + } + + override func keyboardWillShow(notification: NSNotification) { + tableView.setContentOffset(CGPoint(x: 0, y: (currentlyEditingField?.frame.origin.y ?? 0)), animated: true) + } + + override func keyboardWillHide(notification: NSNotification) { + tableView.setContentOffset(CGPoint(x: 0, y: 0), animated: true) + } +} + +extension ProxyViewController: UITableViewDelegate, UITableViewDataSource { + func numberOfSections(in tableView: UITableView) -> Int { + return sections.count + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section_: Int) -> Int { + switch sections[section_] { + case .header: + return 1 + case .fields: + return fields.count + } + } + + func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { + return UITableView.automaticDimension + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + tableView.estimatedRowHeight = 44 + return UITableView.automaticDimension + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + switch sections[indexPath.section] { + case .header: + let headerCell = tableView.dequeueReusableCell(withType: SettingsHeaderCell.self, for: indexPath)! + headerCell.delegate = self + headerCell.populate(with: " ") + headerCell.titleLabel.isAccessibilityElement = false + return headerCell + case .fields: + let field = fields[indexPath.row] + switch field { + case .proxy: + let proxyCell = tableView.dequeueReusableCell(withType: SettingsProxyCell.self, for: indexPath)! + proxyCell.topViewController = getTopViewController() + proxyCell.delegate = self + proxyCell.populate() + return proxyCell + } + } + } + + @objc func editingChanged(sender: UITextField) { + let text = sender.text ?? String() + if (text.count > 11) { + sender.deleteBackward() + } + } +} + +extension ProxyViewController: SettingsHeaderCellDelegate { + func didDismissSettings() { + dismiss(animated: true, completion: nil) + } +} + +extension ProxyViewController: SettingsCellDelegate { + func didStartEditingField(_ field: SigningCategoryViewController.FieldId, _ textField: UITextField) { + switch field { + case .proxy: + currentlyEditingField = textField + break + default: + break + } + } + + func didStartEditingField(_ field: SigningCategoryViewController.FieldId, _ indexPath: IndexPath) { + currentlyEditingCell = indexPath + } + + func didStartEditingField(_ field: FieldId, _ indexPath: IndexPath) { + switch field { + case .proxy: + currentlyEditingCell = indexPath + break + } + } + + func didEndEditingField(_ fieldId: SigningCategoryViewController.FieldId, with value: String) { + currentlyEditingCell = nil + UIAccessibility.post(notification: .screenChanged, argument: L(.settingsValueChanged)) + } +} diff --git a/MoppApp/MoppApp/Session.swift b/MoppApp/MoppApp/Session.swift index 82d8534f8..388dd6482 100644 --- a/MoppApp/MoppApp/Session.swift +++ b/MoppApp/MoppApp/Session.swift @@ -29,7 +29,7 @@ class Session { func getSession(baseUrl: String, uuid: String, phoneNumber: String, nationalIdentityNumber: String, hash: String, hashType: String, language: String, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void) -> Void { do { - _ = try RequestSession.shared.getSession(baseUrl: baseUrl, requestParameters: SessionRequestParameters(relyingPartyName: kRelyingPartyName, relyingPartyUUID: uuid, phoneNumber: "+\(phoneNumber)", nationalIdentityNumber: nationalIdentityNumber, hash: hash, hashType: hashType, language: language, displayText: L(.simToolkitSignDocumentTitle).asUnicode, displayTextFormat: DefaultsHelper.moppLanguageID == "ru" ? kAlternativeDisplayTextFormat : kDisplayTextFormat), trustedCertificates: trustedCertificates) { (sessionResult) in + _ = try RequestSession.shared.getSession(baseUrl: baseUrl, requestParameters: SessionRequestParameters(relyingPartyName: kRelyingPartyName, relyingPartyUUID: uuid, phoneNumber: "+\(phoneNumber)", nationalIdentityNumber: nationalIdentityNumber, hash: hash, hashType: hashType, language: language, displayText: L(.simToolkitSignDocumentTitle).asUnicode, displayTextFormat: DefaultsHelper.moppLanguageID == "ru" ? kAlternativeDisplayTextFormat : kDisplayTextFormat), trustedCertificates: trustedCertificates, manualProxyConf: ManualProxy.getManualProxyConfiguration()) { (sessionResult) in switch sessionResult { case .success(let response): diff --git a/MoppApp/MoppApp/SessionCertificate.swift b/MoppApp/MoppApp/SessionCertificate.swift index bdb4835d0..d13b99f27 100644 --- a/MoppApp/MoppApp/SessionCertificate.swift +++ b/MoppApp/MoppApp/SessionCertificate.swift @@ -30,7 +30,7 @@ class SessionCertificate { func getCertificate(baseUrl: String, uuid: String, phoneNumber: String, nationalIdentityNumber: String, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void) -> Void { do { - _ = try RequestSignature.shared.getCertificate(baseUrl: baseUrl, requestParameters: CertificateRequestParameters(relyingPartyUUID: uuid, relyingPartyName: kRelyingPartyName, phoneNumber: "+\(phoneNumber)", nationalIdentityNumber: nationalIdentityNumber), trustedCertificates: trustedCertificates) { (result) in + _ = try RequestSignature.shared.getCertificate(baseUrl: baseUrl, requestParameters: CertificateRequestParameters(relyingPartyUUID: uuid, relyingPartyName: kRelyingPartyName, phoneNumber: "+\(phoneNumber)", nationalIdentityNumber: nationalIdentityNumber), trustedCertificates: trustedCertificates, manualProxyConf: ManualProxy.getManualProxyConfiguration()) { (result) in switch result { case .success(let response): diff --git a/MoppApp/MoppApp/SessionStatus.swift b/MoppApp/MoppApp/SessionStatus.swift index c1ed401bf..38dc2ffb3 100644 --- a/MoppApp/MoppApp/SessionStatus.swift +++ b/MoppApp/MoppApp/SessionStatus.swift @@ -31,7 +31,7 @@ class SessionStatus { func getSessionStatus(baseUrl: String, process: PollingProcess, sessionId: String, timeoutMs: Int?, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void ) { DispatchQueue.main.async { do { - _ = try RequestSession.shared.getSessionStatus(baseUrl: baseUrl, process: process, requestParameters: SessionStatusRequestParameters(sessionId: sessionId, timeoutMs: timeoutMs), trustedCertificates: trustedCertificates) { (sessionStatusResult: Result) in + _ = try RequestSession.shared.getSessionStatus(baseUrl: baseUrl, process: process, requestParameters: SessionStatusRequestParameters(sessionId: sessionId, timeoutMs: timeoutMs), trustedCertificates: trustedCertificates, manualProxyConf: ManualProxy.getManualProxyConfiguration()) { (sessionStatusResult: Result) in switch sessionStatusResult { case .success(let sessionStatus): if self.isSessionStateComplete(sessionState: self.getSessionState(sessionStatus: sessionStatus)) { diff --git a/MoppApp/MoppApp/Settings.storyboard b/MoppApp/MoppApp/Settings.storyboard index 7b0fc0008..1446797fb 100644 --- a/MoppApp/MoppApp/Settings.storyboard +++ b/MoppApp/MoppApp/Settings.storyboard @@ -650,6 +650,34 @@ + + + + + + + + + + + + + + + + + + + + @@ -1024,6 +1052,482 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1505,6 +2009,9 @@ + + + diff --git a/MoppApp/MoppApp/SettingsConfiguration.swift b/MoppApp/MoppApp/SettingsConfiguration.swift index 75387c211..e3893c040 100644 --- a/MoppApp/MoppApp/SettingsConfiguration.swift +++ b/MoppApp/MoppApp/SettingsConfiguration.swift @@ -43,6 +43,7 @@ */ import Foundation +import SkSigningLib class SettingsConfiguration: NSObject, URLSessionDelegate, URLSessionTaskDelegate, URLSessionDataDelegate { @@ -217,21 +218,26 @@ class SettingsConfiguration: NSObject, URLSessionDelegate, URLSessionTaskDelegat private func fetchDataFromCentralConfiguration(fromUrl: String, completionHandler: @escaping (String?, Error?) -> Void) -> Void { guard let url = URL(string: fromUrl) else { return } - - let urlSessionConfiguration: URLSessionConfiguration = URLSessionConfiguration.default + + let manualProxyConf = ManualProxy.getManualProxyConfiguration() + + var urlSessionConfiguration = URLSessionConfiguration.default + ProxyUtil.configureURLSessionWithProxy(urlSessionConfiguration: &urlSessionConfiguration, manualProxyConf: manualProxyConf) urlSessionConfiguration.timeoutIntervalForResource = 5.0 urlSessionConfiguration.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData + let urlSession = URLSession(configuration: urlSessionConfiguration, delegate: self, delegateQueue: nil) let userAgent = MoppLibManager.sharedInstance().userAgent() var request = URLRequest(url: url) request.setValue(userAgent, forHTTPHeaderField: "User-Agent") + ProxyUtil.setProxyAuthorizationHeader(request: &request, urlSessionConfiguration: urlSessionConfiguration, manualProxyConf: manualProxyConf) let task = urlSession.dataTask(with: request, completionHandler: { data, response, error in if let err = error as? NSError { - if err.code == -1009 { + if err.code == -1009 || err.code == -1003 || err.code == 310 { completionHandler(nil, DiagnosticError.noInternetConnection) } else { completionHandler(nil, err) @@ -338,7 +344,8 @@ class SettingsConfiguration: NSObject, URLSessionDelegate, URLSessionTaskDelegat printLog("Failed to reload DigiDocConf") fatalError("Failed to reload DigiDocConf") }, usingTestDigiDocService: useTestDDS, andTSUrl: DefaultsHelper.timestampUrl ?? MoppConfiguration.getMoppLibConfiguration().tsaurl, - withMoppConfiguration: MoppConfiguration.getMoppLibConfiguration()) + withMoppConfiguration: MoppConfiguration.getMoppLibConfiguration(), + andProxyConfiguration: ManualProxy.getMoppLibProxyConfiguration()) } diff --git a/MoppApp/MoppApp/SettingsFieldCell.swift b/MoppApp/MoppApp/SettingsFieldCell.swift index 4e4252eb3..c30028974 100644 --- a/MoppApp/MoppApp/SettingsFieldCell.swift +++ b/MoppApp/MoppApp/SettingsFieldCell.swift @@ -25,6 +25,7 @@ import UIKit protocol SettingsCellDelegate: AnyObject { func didStartEditingField(_ field: SigningCategoryViewController.FieldId, _ indexPath: IndexPath) + func didStartEditingField(_ field: SigningCategoryViewController.FieldId, _ textField: UITextField) func didEndEditingField(_ field: SigningCategoryViewController.FieldId, with value: String) } diff --git a/MoppApp/MoppApp/SettingsProxyCell.swift b/MoppApp/MoppApp/SettingsProxyCell.swift new file mode 100644 index 000000000..19ff7dacf --- /dev/null +++ b/MoppApp/MoppApp/SettingsProxyCell.swift @@ -0,0 +1,350 @@ +// +// SettingsProxyCell.swift +// MoppApp +// +/* + * Copyright 2017 - 2023 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +import UIKit +import SkSigningLib + +class SettingsProxyCell: UITableViewCell { + + private static let defaultEnableSSLWidth = CGFloat(150) + + @IBOutlet weak var proxyTitle: ScaledLabel! + + @IBOutlet weak var proxyChoicesStackView: UIStackView! + @IBOutlet weak var noProxyTitle: ScaledLabel! + @IBOutlet weak var systemProxyTitle: ScaledLabel! + @IBOutlet weak var manualProxyTitle: ScaledLabel! + + @IBOutlet weak var noProxyStackView: UIStackView! + @IBOutlet weak var noProxyView: UIView! + @IBOutlet weak var systemProxyStackView: UIStackView! + @IBOutlet weak var systemProxyView: UIView! + @IBOutlet weak var manualProxyStackView: UIStackView! + @IBOutlet weak var manualProxyView: UIView! + @IBOutlet weak var noProxyRadioButton: RadioButton! + @IBOutlet weak var useSystemProxyRadioButton: RadioButton! + @IBOutlet weak var useManualProxyRadioButton: RadioButton! + + @IBOutlet weak var hostTitle: ScaledLabel! + @IBOutlet weak var hostTextField: SettingsTextField! + + @IBOutlet weak var portTitle: ScaledLabel! + @IBOutlet weak var portTextField: SettingsTextField! + @IBOutlet weak var portNumberError: ScaledLabel! + + @IBOutlet weak var usernameTitle: ScaledLabel! + @IBOutlet weak var usernameTextField: SettingsTextField! + + @IBOutlet weak var passwordTitle: ScaledLabel! + @IBOutlet weak var passwordTextField: SettingsTextField! + + @IBOutlet weak var enableSSLStackView: UIStackView! + @IBOutlet weak var enableSSLTitle: ScaledLabel! + @IBOutlet weak var enableSSLButton: SwitchButton! + + weak var delegate: SettingsCellDelegate! + + weak var topViewController: UIViewController? + + @IBAction func toggleProxySSL(_ sender: SwitchButton) { + DefaultsHelper.isProxyForSSLEnabled = sender.isOn + setSSLButtonVoiceLabel() + } + + override func awakeFromNib() { + hostTextField.delegate = self + portTextField.delegate = self + usernameTextField.delegate = self + passwordTextField.delegate = self + + noProxyTitle.text = L(.settingsProxyNoProxy) + systemProxyTitle.text = L(.settingsProxyUseSystem) + manualProxyTitle.text = L(.settingsProxyUseManual) + + presentDismissButton(hostTextField) + presentDismissButton(portTextField) + presentDismissButton(usernameTextField) + presentDismissButton(passwordTextField) + + setBorder(hostTextField) + setBorder(portTextField) + setBorder(usernameTextField) + setBorder(passwordTextField) + + hostTextField.isAccessibilityElement = true + portTextField.isAccessibilityElement = true + usernameTextField.isAccessibilityElement = true + passwordTextField.isAccessibilityElement = true + + hostTextField.accessibilityLabel = L(.settingsProxyHost) + portTextField.accessibilityLabel = L(.settingsProxyPort) + usernameTextField.accessibilityLabel = L(.settingsProxyUsername) + passwordTextField.accessibilityLabel = L(.settingsProxyPassword) + + hostTextField.accessibilityUserInputLabels = [L(.voiceControlProxyHost)] + portTextField.accessibilityUserInputLabels = [L(.voiceControlProxyPort)] + usernameTextField.accessibilityUserInputLabels = [L(.voiceControlProxyUsername)] + passwordTextField.accessibilityUserInputLabels = [L(.voiceControlProxyPassword)] + + noProxyView.accessibilityUserInputLabels = [L(.voiceControlProxyNoProxy)] + systemProxyView.accessibilityUserInputLabels = [L(.voiceControlProxySystemProxy)] + manualProxyView.accessibilityUserInputLabels = [L(.voiceControlProxyManualProxy)] + + setSSLButtonVoiceLabel() + + guard let proxyUITitle = proxyTitle, let noProxyUIView = noProxyView, let systemProxyUIView = systemProxyView, let manualProxyUIView = manualProxyView, let hostUITextField: UITextField = hostTextField, let portUITextField = portTextField, let usernameUITextField = usernameTextField, let passwordUITextField = passwordTextField, let enableSSLUIButton = enableSSLButton else { + printLog("Unable to get proxyTitle, noProxyView, systemProxyView, manualProxyView, hostTextField, portTextField, usernameTextField, passwordTextField or enableSSLButton") + return + } + + if UIAccessibility.isVoiceOverRunning { + self.accessibilityElements = [proxyUITitle, noProxyUIView, systemProxyUIView, manualProxyUIView, hostUITextField, portUITextField, usernameUITextField, passwordUITextField, enableSSLUIButton] + } + + updateUI() + } + + func populate() { + updateUI() + } + + func setAccessibilityElementsInStackView(stackView: UIStackView, isAccessibilityElement: Bool) { + for subview in stackView.arrangedSubviews { + subview.isAccessibilityElement = isAccessibilityElement + } + } + + func handleProxySetting(proxySetting: ProxySetting) { + switch proxySetting { + case .noProxy: + self.noProxyRadioButton.setSelectedState(state: true) + self.useSystemProxyRadioButton.setSelectedState(state: false) + self.useManualProxyRadioButton.setSelectedState(state: false) + + DefaultsHelper.proxySetting = .noProxy + + self.hostTextField.isEnabled = false + self.portTextField.isEnabled = false + self.usernameTextField.isEnabled = false + self.passwordTextField.isEnabled = false + + self.hostTextField.textColor = .lightGray + self.portTextField.textColor = .lightGray + self.usernameTextField.textColor = .lightGray + self.passwordTextField.textColor = .lightGray + + case .systemProxy: + self.noProxyRadioButton.setSelectedState(state: false) + self.useSystemProxyRadioButton.setSelectedState(state: true) + self.useManualProxyRadioButton.setSelectedState(state: false) + + DefaultsHelper.proxySetting = .systemProxy + + self.hostTextField.isEnabled = false + self.portTextField.isEnabled = false + self.usernameTextField.isEnabled = false + self.passwordTextField.isEnabled = false + + self.hostTextField.textColor = .lightGray + self.portTextField.textColor = .lightGray + self.usernameTextField.textColor = .lightGray + self.passwordTextField.textColor = .lightGray + + case .manualProxy: + self.noProxyRadioButton.setSelectedState(state: false) + self.useSystemProxyRadioButton.setSelectedState(state: false) + self.useManualProxyRadioButton.setSelectedState(state: true) + + DefaultsHelper.proxySetting = .manualProxy + + self.hostTextField.isEnabled = true + self.portTextField.isEnabled = true + self.usernameTextField.isEnabled = true + self.passwordTextField.isEnabled = true + + self.hostTextField.textColor = .black + self.portTextField.textColor = .black + self.usernameTextField.textColor = .black + self.passwordTextField.textColor = .black + } + } + + @objc func handleState(_ sender: ProxyChoiceTapGestureRecognizer) { + handleProxySetting(proxySetting: sender.proxySetting) + } + + func updateUI() { + DispatchQueue.main.async { + + self.proxyChoicesStackView.isAccessibilityElement = false + self.enableSSLStackView.isAccessibilityElement = false + + self.enableSSLButton.isAccessibilityElement = true + + self.setAccessibilityElementsInStackView(stackView: self.proxyChoicesStackView, isAccessibilityElement: true) + + let savedProxySetting = DefaultsHelper.proxySetting + self.noProxyRadioButton.setSelectedState(state: savedProxySetting == .noProxy) + self.useSystemProxyRadioButton.setSelectedState(state: savedProxySetting == .systemProxy) + self.useManualProxyRadioButton.setSelectedState(state: savedProxySetting == .manualProxy) + + self.hostTextField.text = DefaultsHelper.proxyHost + self.portTextField.text = String(DefaultsHelper.proxyPort) + self.usernameTextField.text = DefaultsHelper.proxyUsername + self.passwordTextField.text = KeychainUtil.retrieve(key: proxyPasswordKey) ?? "" + + self.enableSSLButton.isOn = DefaultsHelper.isProxyForSSLEnabled + self.enableSSLButton.accessibilityLabel = L(.settingsProxyAllowSSL) + + // Detect which RadioButton was clicked + if !(self.noProxyView.gestureRecognizers?.contains(where: { $0 is ProxyChoiceTapGestureRecognizer }) ?? false) { + + let tapGesture = ProxyChoiceTapGestureRecognizer(target: self, action: #selector(self.handleState(_:))) + tapGesture.proxySetting = .noProxy + self.noProxyView.addGestureRecognizer(tapGesture) + self.noProxyView.isUserInteractionEnabled = true + } + + if !(self.systemProxyView.gestureRecognizers?.contains(where: { $0 is ProxyChoiceTapGestureRecognizer }) ?? false) { + + let tapGesture = ProxyChoiceTapGestureRecognizer(target: self, action: #selector(self.handleState(_:))) + tapGesture.proxySetting = .systemProxy + self.systemProxyView.addGestureRecognizer(tapGesture) + self.systemProxyView.isUserInteractionEnabled = true + } + + if !(self.manualProxyView.gestureRecognizers?.contains(where: { $0 is ProxyChoiceTapGestureRecognizer }) ?? false) { + let tapGesture = ProxyChoiceTapGestureRecognizer(target: self, action: #selector(self.handleState(_:))) + tapGesture.proxySetting = .manualProxy + self.manualProxyView.addGestureRecognizer(tapGesture) + self.manualProxyView.isUserInteractionEnabled = true + } + + if let number = Int(self.portTextField.text ?? ""), self.isPortNumberValid(portNumber: number) { + self.portNumberError.isHidden = true + } else { + self.portNumberError.isHidden = false + } + + self.noProxyView.isAccessibilityElement = true + self.systemProxyView.isAccessibilityElement = true + self.manualProxyView.isAccessibilityElement = true + + self.noProxyView.accessibilityLabel = L(.settingsProxyNoProxy) + self.systemProxyView.accessibilityLabel = L(.settingsProxyUseSystem) + self.manualProxyView.accessibilityLabel = L(.settingsProxyUseManual) + + self.proxyTitle.text = L(.settingsProxyTitle) + + self.noProxyTitle.text = L(.settingsProxyNoProxy) + self.systemProxyTitle.text = L(.settingsProxyUseSystem) + self.manualProxyTitle.text = L(.settingsProxyUseManual) + + self.hostTitle.text = L(.settingsProxyHost) + self.portTitle.text = L(.settingsProxyPort) + self.usernameTitle.text = L(.settingsProxyUsername) + self.passwordTitle.text = L(.settingsProxyPassword) + + self.enableSSLTitle.text = L(.settingsProxyAllowSSL) + + self.handleProxySetting(proxySetting: savedProxySetting) + } + } + + func presentDismissButton(_ textField: SettingsTextField) { + textField.moppPresentDismissButton() + } + + func setBorder(_ textField: SettingsTextField) { + textField.layer.borderColor = UIColor.moppContentLine.cgColor + textField.layer.borderWidth = 1 + } + + func isPortNumberValid(portNumber: Int) -> Bool { + return (1...65535).contains(portNumber) + } + + func setSSLButtonVoiceLabel() { + enableSSLButton.accessibilityUserInputLabels = enableSSLButton.isOn ? [L(.voiceControlProxyDisallowSsl)] : [L(.settingsProxyAllowSSL)] + } + + deinit { + let savedProxySetting = DefaultsHelper.proxySetting + if savedProxySetting == .noProxy || savedProxySetting == .systemProxy { + DefaultsHelper.proxyHost = "" + DefaultsHelper.proxyPort = 80 + DefaultsHelper.proxyUsername = "" + KeychainUtil.remove(key: proxyPasswordKey) + } + printLog("Deinit SettingsProxyCell") + } +} + +extension SettingsProxyCell: UITextFieldDelegate { + func textFieldDidBeginEditing(_ textField: UITextField) { + delegate.didStartEditingField(.proxy, textField) + } + + func textFieldDidEndEditing(_ textField: UITextField) { + switch textField { + case hostTextField: + DefaultsHelper.proxyHost = textField.text + case portTextField: + DefaultsHelper.proxyPort = Int(textField.text ?? "80") ?? 80 + case usernameTextField: + DefaultsHelper.proxyUsername = textField.text + case passwordTextField: + let _ = KeychainUtil.save(key: proxyPasswordKey, info: textField.text ?? "") + default: + break + } + delegate.didEndEditingField(.proxy, with: textField.text ?? String()) + UIAccessibility.post(notification: UIAccessibility.Notification.screenChanged, argument: textField) + } + + func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + + // Allow only numeric characters + if textField == portTextField { + + let allowedCharacterSet = CharacterSet.decimalDigits + let inputCharacterSet = CharacterSet(charactersIn: string) + + guard allowedCharacterSet.isSuperset(of: inputCharacterSet) else { + return false + } + + guard let text = textField.text as? NSString else { return false } + let portNumber = text.replacingCharacters(in: range, with: string) + guard let number = Int(portNumber) else { if portNumber.isEmpty { return true }; return false } + + // Check if port number is within range + let isValidPortNumber = isPortNumberValid(portNumber: Int(number)) + portNumberError.isHidden = isValidPortNumber + + return true + } + return true + } +} + diff --git a/MoppApp/MoppApp/SettingsViewController.swift b/MoppApp/MoppApp/SettingsViewController.swift index 4c1e4e661..f041e7c1f 100644 --- a/MoppApp/MoppApp/SettingsViewController.swift +++ b/MoppApp/MoppApp/SettingsViewController.swift @@ -73,28 +73,49 @@ class SettingsViewController: MoppViewController { } func resetSettings() { + resetRPAndTimestamp() + resetRoleAndAddress() + resetTSA() + resetSiva() + resetProxy() + + tableView.reloadData() + } + + func resetRPAndTimestamp() { DefaultsHelper.rpUuid = "" DefaultsHelper.timestampUrl = nil DefaultsHelper.defaultSettingsSwitch = true - + } + + func resetRoleAndAddress() { DefaultsHelper.isRoleAndAddressEnabled = false DefaultsHelper.roleNames = [] DefaultsHelper.roleCity = "" DefaultsHelper.roleState = "" DefaultsHelper.roleCountry = "" DefaultsHelper.roleZip = "" - - CertUtil.removeCertificate(folder: SettingsTSACertCell.tsaFileFolder, fileName: DefaultsHelper.tsaCertFileName ?? "") - - CertUtil.removeCertificate(folder: SettingsSivaCertCell.sivaFileFolder, fileName: DefaultsHelper.sivaCertFileName ?? "") + } + func resetTSA() { + CertUtil.removeCertificate(folder: SettingsTSACertCell.tsaFileFolder, fileName: DefaultsHelper.tsaCertFileName ?? "") DefaultsHelper.tsaCertFileName = "" + } + + func resetSiva() { + CertUtil.removeCertificate(folder: SettingsSivaCertCell.sivaFileFolder, fileName: DefaultsHelper.sivaCertFileName ?? "") DefaultsHelper.sivaCertFileName = "" - DefaultsHelper.sivaAccessState = .defaultAccess DefaultsHelper.sivaUrl = Configuration.getConfiguration().SIVAURL - - tableView.reloadData() + } + + func resetProxy() { + DefaultsHelper.proxySetting = .noProxy + DefaultsHelper.proxyHost = "" + DefaultsHelper.proxyPort = 80 + DefaultsHelper.proxyUsername = "" + KeychainUtil.remove(key: proxyPasswordKey) + DefaultsHelper.isProxyForSSLEnabled = true } deinit { diff --git a/MoppApp/MoppApp/Signing/SigningCategoryViewController.swift b/MoppApp/MoppApp/Signing/SigningCategoryViewController.swift index 092b240da..157d0cf26 100644 --- a/MoppApp/MoppApp/Signing/SigningCategoryViewController.swift +++ b/MoppApp/MoppApp/Signing/SigningCategoryViewController.swift @@ -47,6 +47,7 @@ class SigningCategoryViewController: MoppViewController { case useDefault case tsaCert case sivaCert + case proxy } struct Field { @@ -58,6 +59,7 @@ class SigningCategoryViewController: MoppViewController { case defaultSwitch case tsaCert case sivaCert + case proxy } let id: FieldId @@ -114,6 +116,12 @@ class SigningCategoryViewController: MoppViewController { kind: .sivaCert, title: L(.settingsSivaServiceTitle), placeholderText: NSAttributedString(string: L(.settingsSivaServiceTitle)), + value: ""), + Field( + id: .proxy, + kind: .proxy, + title: L(.settingsProxyTitle), + placeholderText: NSAttributedString(string: L(.settingsProxyTitle)), value: "") ] @@ -139,7 +147,7 @@ class SigningCategoryViewController: MoppViewController { @objc func accessibilityElementFocused(_ notification: Notification) { let topViewController = getTopViewController() - if topViewController is SivaCertViewController { return } + if topViewController is SivaCertViewController || topViewController is ProxyViewController { return } if let element = notification.userInfo?[UIAccessibility.focusedElementUserInfoKey] as? UIView { let elementRect = element.convert(element.bounds, to: tableView) let offsetY = elementRect.midY - (tableView.frame.size.height / 4) @@ -163,7 +171,7 @@ class SigningCategoryViewController: MoppViewController { if !isDefaultTimestampSettingsEnabled { - let classOrder = [SettingsDefaultValueCell.self, SettingsRoleAndAddressCell.self, SettingsFieldCell.self, SettingsTimeStampCell.self, SettingsTSACertCell.self, SivaCategoryCell.self, SettingsHeaderCell.self, SettingsDefaultValueCell.self] + let classOrder = [SettingsDefaultValueCell.self, SettingsRoleAndAddressCell.self, SettingsFieldCell.self, SettingsTimeStampCell.self, SettingsTSACertCell.self, SivaCategoryCell.self, ProxyCategoryCell.self, SettingsHeaderCell.self, SettingsDefaultValueCell.self] for className in classOrder { for key in tableViewCells.keys.sorted() { @@ -175,7 +183,7 @@ class SigningCategoryViewController: MoppViewController { } } } else { - let accessibilityClassOrder = [SettingsDefaultValueCell.self, SettingsRoleAndAddressCell.self, SettingsFieldCell.self, SettingsTimeStampCell.self, SivaCategoryCell.self, SettingsHeaderCell.self, SettingsDefaultValueCell.self] + let accessibilityClassOrder = [SettingsDefaultValueCell.self, SettingsRoleAndAddressCell.self, SettingsFieldCell.self, SettingsTimeStampCell.self, SivaCategoryCell.self, ProxyCategoryCell.self, SettingsHeaderCell.self, SettingsDefaultValueCell.self] for className in accessibilityClassOrder { for key in tableViewCells.keys.sorted() { @@ -280,6 +288,12 @@ extension SigningCategoryViewController: UITableViewDelegate, UITableViewDataSou sivaCertCell.populate() tableViewCells[indexPath] = sivaCertCell return sivaCertCell + case .proxy: + let proxyCell = tableView.dequeueReusableCell(withType: ProxyCategoryCell.self, for: indexPath)! + proxyCell.topViewController = getTopViewController() + proxyCell.populate() + tableViewCells[indexPath] = proxyCell + return proxyCell } } return UITableViewCell() @@ -300,6 +314,10 @@ extension SigningCategoryViewController: SettingsHeaderCellDelegate { } extension SigningCategoryViewController: SettingsCellDelegate { + func didStartEditingField(_ field: FieldId, _ textField: UITextField) { + return + } + func didStartEditingField(_ field: FieldId, _ indexPath: IndexPath) { switch field { case .rpuuid: @@ -339,7 +357,8 @@ extension SigningCategoryViewController: SettingsTimeStampCellDelegate { self?.errorAlertWithLink(message: MessageUtil.generateDetailedErrorMessage(error: nsError) ?? L(.genericErrorMessage)) }, usingTestDigiDocService: useTestDDS, andTSUrl: DefaultsHelper.timestampUrl ?? MoppConfiguration.getMoppLibConfiguration().tsaurl, - withMoppConfiguration: MoppConfiguration.getMoppLibConfiguration()) + withMoppConfiguration: MoppConfiguration.getMoppLibConfiguration(), + andProxyConfiguration: ManualProxy.getMoppLibProxyConfiguration()) } } diff --git a/MoppApp/MoppApp/Signing/SivaCertViewController.swift b/MoppApp/MoppApp/Signing/Siva/SivaCertViewController.swift similarity index 97% rename from MoppApp/MoppApp/Signing/SivaCertViewController.swift rename to MoppApp/MoppApp/Signing/Siva/SivaCertViewController.swift index 011a568db..9a3b608a5 100644 --- a/MoppApp/MoppApp/Signing/SivaCertViewController.swift +++ b/MoppApp/MoppApp/Signing/Siva/SivaCertViewController.swift @@ -118,6 +118,8 @@ extension SivaCertViewController: SettingsHeaderCellDelegate { } extension SivaCertViewController: SettingsCellDelegate { + func didStartEditingField(_ field: SigningCategoryViewController.FieldId, _ textField: UITextField) { return } + func didStartEditingField(_ field: SigningCategoryViewController.FieldId, _ indexPath: IndexPath) { currentlyEditingCell = indexPath } diff --git a/MoppApp/MoppApp/SmartIDSignature.swift b/MoppApp/MoppApp/SmartIDSignature.swift index 657912630..929e3ce87 100644 --- a/MoppApp/MoppApp/SmartIDSignature.swift +++ b/MoppApp/MoppApp/SmartIDSignature.swift @@ -72,7 +72,7 @@ class SmartIDSignature { } self.selectAccount() - SIDRequest.shared.getCertificate(baseUrl: baseUrl, country: country, nationalIdentityNumber: nationalIdentityNumber, requestParameters: requestParameters, trustedCertificates: trustedCertificates) { result in + SIDRequest.shared.getCertificate(baseUrl: baseUrl, country: country, nationalIdentityNumber: nationalIdentityNumber, requestParameters: requestParameters, trustedCertificates: trustedCertificates, manualProxyConf: ManualProxy.getManualProxyConfiguration()) { result in switch result { case .success(let response): printLog("Received Certificate (session ID): \(response.sessionID)") @@ -106,7 +106,7 @@ class SmartIDSignature { return CancelUtil.handleCancelledRequest(errorMessageDetails: "User cancelled Smart-ID signing") } - SIDRequest.shared.getSignature(baseUrl: baseUrl, documentNumber: documentNumber, allowedInteractionsOrder: allowedInteractionsOrder, trustedCertificates: trustedCertificates) { result in + SIDRequest.shared.getSignature(baseUrl: baseUrl, documentNumber: documentNumber, allowedInteractionsOrder: allowedInteractionsOrder, trustedCertificates: trustedCertificates, manualProxyConf: ManualProxy.getManualProxyConfiguration()) { result in switch result { case .success(let response): printLog("Received Signature (session ID): \(response.sessionID)") @@ -134,7 +134,7 @@ class SmartIDSignature { return CancelUtil.handleCancelledRequest(errorMessageDetails: "User cancelled Smart-ID signing") } - SIDRequest.shared.getSessionStatus(baseUrl: baseUrl, sessionId: sessionId, timeoutMs: kDefaultTimeoutMs, trustedCertificates: trustedCertificates) { result in + SIDRequest.shared.getSessionStatus(baseUrl: baseUrl, sessionId: sessionId, timeoutMs: kDefaultTimeoutMs, trustedCertificates: trustedCertificates, manualProxyConf: ManualProxy.getManualProxyConfiguration()) { result in switch result { case .success(let sessionStatus): printLog("RIA.SmartID - Session status \(sessionStatus.state.rawValue)") diff --git a/MoppApp/MoppApp/en.lproj/Localizable.strings b/MoppApp/MoppApp/en.lproj/Localizable.strings index eca2e1388..a7f351161 100755 --- a/MoppApp/MoppApp/en.lproj/Localizable.strings +++ b/MoppApp/MoppApp/en.lproj/Localizable.strings @@ -381,6 +381,15 @@ "settings-siva-default-access-title" = "Use default access"; "settings-siva-default-manual-access-title" = "Use manually configured access"; "settings-siva-default-certificate-title" = "Digital Signature Validation Service SiVa SSL certificate"; +"settings-proxy-title" = "Proxy"; +"settings-proxy-no-proxy" = "No proxy"; +"settings-proxy-use-system" = "Use system proxy settings"; +"settings-proxy-use-manual" = "Manual proxy configuration"; +"settings-proxy-host" = "Host"; +"settings-proxy-port" = "Port"; +"settings-proxy-username" = "Username"; +"settings-proxy-password" = "Password"; +"settings-proxy-allow-ssl" = "Enable proxy for SSL connections"; "settings-reset-button" = "USE DEFAULT SETTINGS"; "diagnostics-title" = "Diagnostics"; "diagnostics-activate-one-time-logging" = "Enable one-time log generation"; @@ -535,3 +544,11 @@ "voice-control-disable-default-timestamping-service" = "Disable default TimeStamping service access"; "voice-control-enable-role-and-address" = "Enable role and address"; "voice-control-disable-role-and-address" = "Disable role and address"; +"voice-control-no-proxy" = "No proxy"; +"voice-control-proxy-system" = "Use system proxy"; +"voice-control-proxy-manual" = "Use manual proxy"; +"voice-control-proxy-host" = "Edit proxy host"; +"voice-control-proxy-port" = "Edit proxy port"; +"voice-control-proxy-username" = "Edit proxy username"; +"voice-control-proxy-password" = "Edit proxy password"; +"voice-control-proxy-disallow-ssl" = "Disable proxy for SSL connections"; diff --git a/MoppApp/MoppApp/et.lproj/Localizable.strings b/MoppApp/MoppApp/et.lproj/Localizable.strings index 173e3f65f..7ea387d35 100755 --- a/MoppApp/MoppApp/et.lproj/Localizable.strings +++ b/MoppApp/MoppApp/et.lproj/Localizable.strings @@ -382,6 +382,15 @@ "settings-siva-default-access-title" = "Kasutan vaikimisi määratud ligipääsu"; "settings-siva-default-manual-access-title" = "Kasutan käsitsi määratud ligipääsu"; "settings-siva-default-certificate-title" = "Valideerimisteenuse SiVa SSL sertifikaat"; +"settings-proxy-title" = "Proksi"; +"settings-proxy-no-proxy" = "Ei kasuta proksit"; +"settings-proxy-use-system" = "Kasuta süsteemseid proksi seadeid"; +"settings-proxy-use-manual" = "Proksi seaded määratakse käsitsi"; +"settings-proxy-host" = "Host"; +"settings-proxy-port" = "Port"; +"settings-proxy-username" = "Kasutajanimi"; +"settings-proxy-password" = "Parool"; +"settings-proxy-allow-ssl" = "Luba proksi kasutamine SSL-ühenduste jaoks"; "settings-reset-button" = "TAASTA ALGSEADED"; "diagnostics-title" = "Diagnostika"; "diagnostics-activate-one-time-logging" = "Aktiveeri ühekordne logifaili genereerimine"; @@ -535,3 +544,11 @@ "voice-control-disable-default-timestamping-service" = "Disable default TimeStamping service access"; "voice-control-enable-role-and-address" = "Enable role and address"; "voice-control-disable-role-and-address" = "Disable role and address"; +"voice-control-no-proxy" = "No proxy"; +"voice-control-proxy-system" = "Use system proxy"; +"voice-control-proxy-manual" = "Use manual proxy"; +"voice-control-proxy-host" = "Edit proxy host"; +"voice-control-proxy-port" = "Edit proxy port"; +"voice-control-proxy-username" = "Edit proxy username"; +"voice-control-proxy-password" = "Edit proxy password"; +"voice-control-proxy-disallow-ssl" = "Disable proxy for SSL connections"; diff --git a/MoppApp/MoppApp/ru.lproj/Localizable.strings b/MoppApp/MoppApp/ru.lproj/Localizable.strings index 62cd512ee..3d287992f 100644 --- a/MoppApp/MoppApp/ru.lproj/Localizable.strings +++ b/MoppApp/MoppApp/ru.lproj/Localizable.strings @@ -379,6 +379,15 @@ "settings-timestamp-cert-add-certificate-button" = "ДОБАВИТЬ СЕРТИФИКАТ"; "settings-timestamp-cert-show-certificate-button" = "ПОКАЗАТЬ СЕРТИФИКАТ"; "settings-siva-service-title" = "Доступ к услуге валидации SiVa"; +"settings-proxy-title" = "Прокси"; +"settings-proxy-no-proxy" = "Прокси не используется"; +"settings-proxy-use-system" = "Использовать системные настройки прокси"; +"settings-proxy-use-manual" = "Настроить прокси вручную"; +"settings-proxy-host" = "Хост"; +"settings-proxy-port" = "Порт"; +"settings-proxy-username" = "Имя пользователя"; +"settings-proxy-password" = "Пароль"; +"settings-proxy-allow-ssl" = "Включить прокси-сервер для подключения SSL"; "settings-siva-default-access-title" = "Использовать доступ, назначенный по умолчанию"; "settings-siva-default-manual-access-title" = "Использовать доступ, назначенный вручную"; "settings-siva-default-certificate-title" = "Сертификат услуги валидации SiVa SSL"; @@ -536,3 +545,11 @@ "voice-control-disable-default-timestamping-service" = "Disable default TimeStamping service access"; "voice-control-enable-role-and-address" = "Enable role and address"; "voice-control-disable-role-and-address" = "Disable role and address"; +"voice-control-no-proxy" = "No proxy"; +"voice-control-proxy-system" = "Use system proxy"; +"voice-control-proxy-manual" = "Use manual proxy"; +"voice-control-proxy-host" = "Edit proxy host"; +"voice-control-proxy-port" = "Edit proxy port"; +"voice-control-proxy-username" = "Edit proxy username"; +"voice-control-proxy-password" = "Edit proxy password"; +"voice-control-proxy-disallow-ssl" = "Disable proxy for SSL connections"; diff --git a/MoppLib/MoppLib.xcodeproj/project.pbxproj b/MoppLib/MoppLib.xcodeproj/project.pbxproj index 6855ab375..61bb31806 100644 --- a/MoppLib/MoppLib.xcodeproj/project.pbxproj +++ b/MoppLib/MoppLib.xcodeproj/project.pbxproj @@ -73,6 +73,8 @@ DF87E9CE292CC79500C2E3F4 /* libdigidocpp_util.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DF87E9CD292CC77C00C2E3F4 /* libdigidocpp_util.a */; }; DFBDF1F827DA44BD00A5CF3C /* MoppLibRoleAddressData.h in Headers */ = {isa = PBXBuildFile; fileRef = DFBDF1F727DA44BD00A5CF3C /* MoppLibRoleAddressData.h */; settings = {ATTRIBUTES = (Public, ); }; }; DFBDF1FA27DA44FD00A5CF3C /* MoppLibRoleAddressData.m in Sources */ = {isa = PBXBuildFile; fileRef = DFBDF1F927DA44FD00A5CF3C /* MoppLibRoleAddressData.m */; }; + DFDD76292B507EFF0008EC2C /* MoppLibProxyConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = DFDD76282B507EFF0008EC2C /* MoppLibProxyConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DFDD762B2B507F9E0008EC2C /* MoppLibProxyConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = DFDD762A2B507F9E0008EC2C /* MoppLibProxyConfiguration.m */; }; DFF3C3A923322E0D0079458A /* MOPPLibConfiguration.h in Headers */ = {isa = PBXBuildFile; fileRef = DFF3C3A723322E0C0079458A /* MOPPLibConfiguration.h */; settings = {ATTRIBUTES = (Public, ); }; }; DFF3C3AA23322E0D0079458A /* MOPPLibConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = DFF3C3A823322E0C0079458A /* MOPPLibConfiguration.m */; }; DFFFCBCC238D470200059717 /* MoppLibGlobals.h in Headers */ = {isa = PBXBuildFile; fileRef = DFFFCBCB238D470200059717 /* MoppLibGlobals.h */; }; @@ -183,6 +185,8 @@ DF87E9CD292CC77C00C2E3F4 /* libdigidocpp_util.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libdigidocpp_util.a; path = MoppLib/libdigidocpp/libdigidocpp.iphoneos/lib/libdigidocpp_util.a; sourceTree = ""; }; DFBDF1F727DA44BD00A5CF3C /* MoppLibRoleAddressData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MoppLibRoleAddressData.h; sourceTree = ""; }; DFBDF1F927DA44FD00A5CF3C /* MoppLibRoleAddressData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MoppLibRoleAddressData.m; sourceTree = ""; }; + DFDD76282B507EFF0008EC2C /* MoppLibProxyConfiguration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MoppLibProxyConfiguration.h; sourceTree = ""; }; + DFDD762A2B507F9E0008EC2C /* MoppLibProxyConfiguration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MoppLibProxyConfiguration.m; sourceTree = ""; }; DFF3C3A723322E0C0079458A /* MOPPLibConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MOPPLibConfiguration.h; sourceTree = ""; }; DFF3C3A823322E0C0079458A /* MOPPLibConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MOPPLibConfiguration.m; sourceTree = ""; }; DFFFCBCB238D470200059717 /* MoppLibGlobals.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MoppLibGlobals.h; sourceTree = ""; }; @@ -270,6 +274,8 @@ 54DC0DC81E0BCCEF00C62B3D /* MoppLibConstants.m */, C54EA735204D90D50039AC78 /* MoppLibCardReaderManager.h */, C54EA737204D91730039AC78 /* MoppLibCardReaderManager.m */, + DFDD76282B507EFF0008EC2C /* MoppLibProxyConfiguration.h */, + DFDD762A2B507F9E0008EC2C /* MoppLibProxyConfiguration.m */, ); name = PublicInterface; path = MoppLib/PublicInterface; @@ -470,6 +476,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + DFDD76292B507EFF0008EC2C /* MoppLibProxyConfiguration.h in Headers */, DFBDF1F827DA44BD00A5CF3C /* MoppLibRoleAddressData.h in Headers */, C5AAAF8620CAA3F00087D6DA /* winscard.h in Headers */, C5AAAF8820CAA4240087D6DA /* wintypes.h in Headers */, @@ -644,6 +651,7 @@ E42B08B61E1F0B3B00EA24A3 /* MoppLibContainer.m in Sources */, 54D1647C1E926AAB0069C725 /* MoppLibCardActions.m in Sources */, E42B08CA1E1F11C100EA24A3 /* MoppLibSignature.m in Sources */, + DFDD762B2B507F9E0008EC2C /* MoppLibProxyConfiguration.m in Sources */, 54DC0DCA1E0BCCEF00C62B3D /* MoppLibConstants.m in Sources */, 54DC0DF51E0BEF2A00C62B3D /* NSString+Additions.m in Sources */, 540CB2211E1CDCED00FE18A3 /* MoppLibCertificate.mm in Sources */, diff --git a/MoppLib/MoppLib/MoppLibDigidocManager.h b/MoppLib/MoppLib/MoppLibDigidocManager.h index 11c6aa9f5..0201d0cee 100644 --- a/MoppLib/MoppLib/MoppLibDigidocManager.h +++ b/MoppLib/MoppLib/MoppLibDigidocManager.h @@ -27,6 +27,7 @@ #import "MoppLibConstants.h" #import "MoppLibConfiguration.h" #import "MoppLibRoleAddressData.h" +#import "MoppLibProxyConfiguration.h" typedef enum { Unspecified, @@ -38,7 +39,7 @@ typedef enum { @property (readonly) BOOL useTestDigiDocService; + (MoppLibDigidocManager *)sharedInstance; -- (void)setupWithSuccess:(VoidBlock)success andFailure:(FailureBlock)failure usingTestDigiDocService:(BOOL)useTestDDS andTSUrl:(NSString*)tsUrl withMoppConfiguration:(MoppLibConfiguration*)moppConfiguration; +- (void)setupWithSuccess:(VoidBlock)success andFailure:(FailureBlock)failure usingTestDigiDocService:(BOOL)useTestDDS andTSUrl:(NSString*)tsUrl withMoppConfiguration:(MoppLibConfiguration*)moppConfiguration andProxyConfiguration:(MoppLibProxyConfiguration*)proxyConfiguration; - (MoppLibContainer *)getContainerWithPath:(NSString *)containerPath error:(NSError **)error; - (MoppLibContainer *)createContainerWithPath:(NSString *)containerPath withDataFilePaths:(NSArray *)dataFilePaths error:(NSError **)error; diff --git a/MoppLib/MoppLib/MoppLibDigidocManager.mm b/MoppLib/MoppLib/MoppLibDigidocManager.mm index b07cd103f..4d6252a9e 100644 --- a/MoppLib/MoppLib/MoppLibDigidocManager.mm +++ b/MoppLib/MoppLib/MoppLibDigidocManager.mm @@ -42,6 +42,7 @@ #import #import #import "MoppLibGlobals.h" +#import "MoppLibProxyConfiguration.h" #include @@ -59,10 +60,11 @@ private: std::string m_tsUrl; MoppLibConfiguration *moppLibConfiguration; + MoppLibProxyConfiguration *proxyConfiguration; public: - DigiDocConf(const std::string& tsUrl, MoppLibConfiguration* moppConfiguration) : m_tsUrl( tsUrl ), moppLibConfiguration( moppConfiguration ) {} + DigiDocConf(const std::string& tsUrl, MoppLibConfiguration* moppConfiguration, MoppLibProxyConfiguration* proxyConfiguration) : m_tsUrl( tsUrl ), moppLibConfiguration( moppConfiguration ), proxyConfiguration( proxyConfiguration ) {} std::string TSLCache() const override { NSString *tslCachePath = [[MLFileManager sharedInstance] tslCachePath]; @@ -140,6 +142,23 @@ return std::string(); } } + + virtual std::string proxyHost() const override { + return std::string([proxyConfiguration.HOST UTF8String]); + } + + virtual std::string proxyPort() const override { + NSInteger port = [proxyConfiguration.PORT integerValue]; + return std::to_string(port); + } + + virtual std::string proxyUser() const override { + return std::string([proxyConfiguration.USERNAME UTF8String]); + } + + virtual std::string proxyPass() const override { + return std::string([proxyConfiguration.PASSWORD UTF8String]); + } std::vector stringsToX509Certs(NSArray *certBundle) const { __block std::vector x509Certs; @@ -268,7 +287,7 @@ + (MoppLibDigidocManager *)sharedInstance { return sharedInstance; } -- (void)setupWithSuccess:(VoidBlock)success andFailure:(FailureBlock)failure usingTestDigiDocService:(BOOL)useTestDDS andTSUrl:(NSString*)tsUrl withMoppConfiguration:(MoppLibConfiguration*)moppConfiguration { +- (void)setupWithSuccess:(VoidBlock)success andFailure:(FailureBlock)failure usingTestDigiDocService:(BOOL)useTestDDS andTSUrl:(NSString*)tsUrl withMoppConfiguration:(MoppLibConfiguration*)moppConfiguration andProxyConfiguration:(MoppLibProxyConfiguration*)proxyConfiguration { dispatch_async(dispatch_get_main_queue(), ^{ dispatch_semaphore_t sem = dispatch_semaphore_create(0); @@ -278,7 +297,7 @@ - (void)setupWithSuccess:(VoidBlock)success andFailure:(FailureBlock)failure usi std::string timestampUrl = tsUrl == nil ? [moppConfiguration.TSAURL cStringUsingEncoding:NSUTF8StringEncoding] : [tsUrl cStringUsingEncoding:NSUTF8StringEncoding]; - digidoc::Conf::init(new DigiDocConf(timestampUrl, moppConfiguration)); + digidoc::Conf::init(new DigiDocConf(timestampUrl, moppConfiguration, proxyConfiguration)); NSString *appInfo = [self userAgent]; std::string appInfoObjcString = std::string([appInfo UTF8String]); digidoc::initialize(appInfoObjcString, appInfoObjcString); diff --git a/MoppLib/MoppLib/PublicInterface/MoppLibManager.h b/MoppLib/MoppLib/PublicInterface/MoppLibManager.h index 6013ad3b2..154ee273f 100644 --- a/MoppLib/MoppLib/PublicInterface/MoppLibManager.h +++ b/MoppLib/MoppLib/PublicInterface/MoppLibManager.h @@ -26,6 +26,7 @@ #import "MoppLibConstants.h" #import "MOPPLibConfiguration.h" #import "MoppLibRoleAddressData.h" +#import "MoppLibProxyConfiguration.h" typedef NS_ENUM(NSUInteger, EIDType) { EIDTypeUnknown, @@ -46,7 +47,7 @@ typedef NS_ENUM(NSUInteger, EIDType) { * @param success Block to be called on successful completion of action. * @param failure Block to be called when action fails. Includes error. */ -- (void)setupWithSuccess:(VoidBlock)success andFailure:(FailureBlock)failure usingTestDigiDocService:(BOOL)useTestDDS andTSUrl:(NSString *)tsUrl withMoppConfiguration:(MoppLibConfiguration *)moppConfiguration; +- (void)setupWithSuccess:(VoidBlock)success andFailure:(FailureBlock)failure usingTestDigiDocService:(BOOL)useTestDDS andTSUrl:(NSString *)tsUrl withMoppConfiguration:(MoppLibConfiguration *)moppConfiguration andProxyConfiguration:(MoppLibProxyConfiguration*)proxyConfiguration; + (NSString *)prepareSignature:(NSString *)cert containerPath:(NSString *)containerPath roleData:(MoppLibRoleAddressData *)roleData; + (NSArray *)getDataToSign; diff --git a/MoppLib/MoppLib/PublicInterface/MoppLibManager.m b/MoppLib/MoppLib/PublicInterface/MoppLibManager.m index 046cde1f0..d69fbf942 100644 --- a/MoppLib/MoppLib/PublicInterface/MoppLibManager.m +++ b/MoppLib/MoppLib/PublicInterface/MoppLibManager.m @@ -35,8 +35,8 @@ + (MoppLibManager *)sharedInstance { return sharedInstance; } -- (void)setupWithSuccess:(VoidBlock)success andFailure:(FailureBlock)failure usingTestDigiDocService:(BOOL)useTestDDS andTSUrl:(NSString *)tsUrl withMoppConfiguration:(MoppLibConfiguration *)moppConfiguration { - [[MoppLibDigidocManager sharedInstance] setupWithSuccess:success andFailure:failure usingTestDigiDocService:useTestDDS andTSUrl:tsUrl withMoppConfiguration: moppConfiguration]; +- (void)setupWithSuccess:(VoidBlock)success andFailure:(FailureBlock)failure usingTestDigiDocService:(BOOL)useTestDDS andTSUrl:(NSString *)tsUrl withMoppConfiguration:(MoppLibConfiguration *)moppConfiguration andProxyConfiguration:(MoppLibProxyConfiguration*)proxyConfiguration { + [[MoppLibDigidocManager sharedInstance] setupWithSuccess:success andFailure:failure usingTestDigiDocService:useTestDDS andTSUrl:tsUrl withMoppConfiguration: moppConfiguration andProxyConfiguration: proxyConfiguration]; } + (NSString *)prepareSignature:(NSString *)cert containerPath:(NSString *)containerPath roleData:(MoppLibRoleAddressData *)roleData { diff --git a/MoppLib/MoppLib/PublicInterface/MoppLibProxyConfiguration.h b/MoppLib/MoppLib/PublicInterface/MoppLibProxyConfiguration.h new file mode 100644 index 000000000..27d6290dc --- /dev/null +++ b/MoppLib/MoppLib/PublicInterface/MoppLibProxyConfiguration.h @@ -0,0 +1,35 @@ +// +// MoppLibProxyConfiguration.h +// MoppLib +// +/* + * Copyright 2017 - 2023 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#import + +@interface MoppLibProxyConfiguration : NSObject + +@property (nonatomic, strong) NSString *HOST; +@property (nonatomic, strong) NSNumber *PORT; +@property (nonatomic, strong) NSString *USERNAME; +@property (nonatomic, strong) NSString *PASSWORD; + +- (id) initWithConfiguration:(NSString *)HOST PORT:(NSNumber *)PORT USERNAME:(NSString *)USERNAME PASSWORD:(NSString *)PASSWORD; + +@end diff --git a/MoppLib/MoppLib/PublicInterface/MoppLibProxyConfiguration.m b/MoppLib/MoppLib/PublicInterface/MoppLibProxyConfiguration.m new file mode 100644 index 000000000..511585862 --- /dev/null +++ b/MoppLib/MoppLib/PublicInterface/MoppLibProxyConfiguration.m @@ -0,0 +1,40 @@ +// +// MoppLibProxyConfiguration.m +// MoppLib +// +/* + * Copyright 2017 - 2023 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#import "MoppLibProxyConfiguration.h" + +@implementation MoppLibProxyConfiguration + +- (id) initWithConfiguration:(NSString *)HOST PORT:(NSNumber *)PORT USERNAME:(NSString *)USERNAME PASSWORD:(NSString *)PASSWORD { + self = [super init]; + if (self) { + self.HOST = HOST; + self.PORT = PORT; + self.USERNAME = USERNAME; + self.PASSWORD = PASSWORD; + } + + return self; +} + +@end diff --git a/SkSigningLib/SkSigningLib.xcodeproj/project.pbxproj b/SkSigningLib/SkSigningLib.xcodeproj/project.pbxproj index 5a6d8928f..bbe8af318 100644 --- a/SkSigningLib/SkSigningLib.xcodeproj/project.pbxproj +++ b/SkSigningLib/SkSigningLib.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ DF3D29B223F6A92A007181B8 /* SkSigningLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF3D29A823F6A929007181B8 /* SkSigningLib.framework */; }; DF3D29B723F6A92A007181B8 /* SkSigningLibTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF3D29B623F6A92A007181B8 /* SkSigningLibTests.swift */; }; DF3D29B923F6A92A007181B8 /* SkSigningLib.h in Headers */ = {isa = PBXBuildFile; fileRef = DF3D29AB23F6A929007181B8 /* SkSigningLib.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DF41B54D2B506F3B00594D01 /* ProxyUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF41B54C2B506F3B00594D01 /* ProxyUtil.swift */; }; + DF41B54E2B506F3B00594D01 /* Proxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF41B54B2B506F3A00594D01 /* Proxy.swift */; }; DF90EDE024223D6A0040FFE7 /* ControlCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF90EDDF24223D6A0040FFE7 /* ControlCode.swift */; }; DFBA32A2299EA5D400788A87 /* CancelRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFBA32A1299EA5D400788A87 /* CancelRequest.swift */; }; DFBA32A429A59B2500788A87 /* CancelRequestUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFBA32A329A59B2500788A87 /* CancelRequestUtil.swift */; }; @@ -65,6 +67,8 @@ DF3D29B123F6A929007181B8 /* SkSigningLibTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SkSigningLibTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DF3D29B623F6A92A007181B8 /* SkSigningLibTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SkSigningLibTests.swift; sourceTree = ""; }; DF3D29B823F6A92A007181B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DF41B54B2B506F3A00594D01 /* Proxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Proxy.swift; sourceTree = ""; }; + DF41B54C2B506F3B00594D01 /* ProxyUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyUtil.swift; sourceTree = ""; }; DF90EDDF24223D6A0040FFE7 /* ControlCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlCode.swift; sourceTree = ""; }; DFBA32A1299EA5D400788A87 /* CancelRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancelRequest.swift; sourceTree = ""; }; DFBA32A329A59B2500788A87 /* CancelRequestUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancelRequestUtil.swift; sourceTree = ""; }; @@ -139,6 +143,7 @@ DF04A88E23FEC7000086C9F9 /* Utilities */ = { isa = PBXGroup; children = ( + DF41B54F2B506F4000594D01 /* Proxy */, DF04A88F23FEC7130086C9F9 /* EncoderDecoder.swift */, DF90EDDF24223D6A0040FFE7 /* ControlCode.swift */, DF3653B1242BA07400C48AC5 /* Logging.swift */, @@ -226,6 +231,15 @@ path = SkSigningLibTests; sourceTree = ""; }; + DF41B54F2B506F4000594D01 /* Proxy */ = { + isa = PBXGroup; + children = ( + DF41B54C2B506F3B00594D01 /* ProxyUtil.swift */, + DF41B54B2B506F3A00594D01 /* Proxy.swift */, + ); + path = Proxy; + sourceTree = ""; + }; DFBA32A729A5AEC200788A87 /* Mobile-ID */ = { isa = PBXGroup; children = ( @@ -395,6 +409,7 @@ files = ( 4E59081A24B30B2C001B23A6 /* SIDCertificateRequestParameters.swift in Sources */, DFD204E7240D274E00EB8150 /* SessionResponse.swift in Sources */, + DF41B54D2B506F3B00594D01 /* ProxyUtil.swift in Sources */, DFD204EF240FD9FD00EB8150 /* SessionStatusResponse.swift in Sources */, DF04A88D23FEA6950086C9F9 /* RequestSignature.swift in Sources */, DF3653B2242BA07400C48AC5 /* Logging.swift in Sources */, @@ -411,6 +426,7 @@ 4E59081C24B30C62001B23A6 /* SIDSignatureRequestParameters.swift in Sources */, DFD204E4240D20B300EB8150 /* SessionRequestParameters.swift in Sources */, DF90EDE024223D6A0040FFE7 /* ControlCode.swift in Sources */, + DF41B54E2B506F3B00594D01 /* Proxy.swift in Sources */, DF04A89523FEDC810086C9F9 /* CertificateRequestParameters.swift in Sources */, DFD204F124100C5500EB8150 /* SessionResponseSignature.swift in Sources */, DFBA32A2299EA5D400788A87 /* CancelRequest.swift in Sources */, diff --git a/SkSigningLib/SkSigningLib.xcodeproj/xcuserdata/martenr.xcuserdatad/xcschemes/xcschememanagement.plist b/SkSigningLib/SkSigningLib.xcodeproj/xcuserdata/martenr.xcuserdatad/xcschemes/xcschememanagement.plist index c7f6093c1..feae86fb5 100644 --- a/SkSigningLib/SkSigningLib.xcodeproj/xcuserdata/martenr.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/SkSigningLib/SkSigningLib.xcodeproj/xcuserdata/martenr.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,7 +7,7 @@ SkSigningLib.xcscheme_^#shared#^_ orderHint - 17 + 27 diff --git a/SkSigningLib/SkSigningLib/Requests/Mobile-ID/RequestSession.swift b/SkSigningLib/SkSigningLib/Requests/Mobile-ID/RequestSession.swift index 778a002aa..be5cd906f 100644 --- a/SkSigningLib/SkSigningLib/Requests/Mobile-ID/RequestSession.swift +++ b/SkSigningLib/SkSigningLib/Requests/Mobile-ID/RequestSession.swift @@ -32,7 +32,7 @@ protocol SessionRequest { - requestParameters: Parameters that are sent to the service. - completionHandler: On request success, callbacks Result */ - func getSession(baseUrl: String, requestParameters: SessionRequestParameters, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void) + func getSession(baseUrl: String, requestParameters: SessionRequestParameters, trustedCertificates: [String]?, manualProxyConf: Proxy, completionHandler: @escaping (Result) -> Void) /** Gets session status info for Mobile-ID. This method invokes SIM toolkit @@ -43,7 +43,7 @@ protocol SessionRequest { - requestParameters: Parameters that are used in URL - completionHandler: On request success, callbacks Result */ - func getSessionStatus(baseUrl: String, process: PollingProcess, requestParameters: SessionStatusRequestParameters, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void) + func getSessionStatus(baseUrl: String, process: PollingProcess, requestParameters: SessionStatusRequestParameters, trustedCertificates: [String]?, manualProxyConf: Proxy, completionHandler: @escaping (Result) -> Void) } /** @@ -61,7 +61,7 @@ public class RequestSession: NSObject, URLSessionDelegate, SessionRequest { urlTask?.cancel() } - public func getSession(baseUrl: String, requestParameters: SessionRequestParameters, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void) { + public func getSession(baseUrl: String, requestParameters: SessionRequestParameters, trustedCertificates: [String]?, manualProxyConf: Proxy, completionHandler: @escaping (Result) -> Void) { guard let url = URL(string: "\(baseUrl)/signature") else { completionHandler(.failure(.invalidURL)) return @@ -87,11 +87,13 @@ public class RequestSession: NSObject, URLSessionDelegate, SessionRequest { "\tdisplayText: \(requestParameters.displayText ?? "")\n" ) - let urlSessionConfiguration: URLSessionConfiguration + var urlSessionConfiguration: URLSessionConfiguration let urlSession: URLSession if trustedCertificates != nil { urlSessionConfiguration = URLSessionConfiguration.default + ProxyUtil.configureURLSessionWithProxy(urlSessionConfiguration: &urlSessionConfiguration, manualProxyConf: manualProxyConf) + ProxyUtil.setProxyAuthorizationHeader(request: &request, urlSessionConfiguration: urlSessionConfiguration, manualProxyConf: manualProxyConf) urlSession = URLSession(configuration: urlSessionConfiguration, delegate: self, delegateQueue: nil) } else { urlSession = URLSession.shared @@ -136,7 +138,7 @@ public class RequestSession: NSObject, URLSessionDelegate, SessionRequest { CertificatePinning().certificatePinning(trustedCertificates: trustedCerts ?? [""], challenge: challenge, completionHandler: completionHandler) } - public func getSessionStatus(baseUrl: String, process: PollingProcess, requestParameters: SessionStatusRequestParameters, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void) { + public func getSessionStatus(baseUrl: String, process: PollingProcess, requestParameters: SessionStatusRequestParameters, trustedCertificates: [String]?, manualProxyConf: Proxy, completionHandler: @escaping (Result) -> Void) { guard let url = URL(string: "\(baseUrl)/signature/session/\(requestParameters.sessionId)?timeoutMs=\(requestParameters.timeoutMs ?? Constants.defaultTimeoutMs)") else { Logging.errorLog(forMethod: "RIA.MobileID - Session status", httpResponse: nil, error: .invalidURL, extraInfo: "Invalid URL \(baseUrl)/signature/session/\(requestParameters.sessionId)?timeoutMs=\(requestParameters.timeoutMs ?? Constants.defaultTimeoutMs)") return completionHandler(.failure(.invalidURL)) @@ -155,12 +157,14 @@ public class RequestSession: NSObject, URLSessionDelegate, SessionRequest { "\ttimeoutMs: \(String(requestParameters.timeoutMs ?? Constants.defaultTimeoutMs)) \n" ) - let urlSessionConfiguration: URLSessionConfiguration + var urlSessionConfiguration: URLSessionConfiguration let urlSession: URLSession if trustedCertificates != nil { urlSessionConfiguration = URLSessionConfiguration.default urlSessionConfiguration.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData + ProxyUtil.configureURLSessionWithProxy(urlSessionConfiguration: &urlSessionConfiguration, manualProxyConf: manualProxyConf) + ProxyUtil.setProxyAuthorizationHeader(request: &request, urlSessionConfiguration: urlSessionConfiguration, manualProxyConf: manualProxyConf) urlSession = URLSession(configuration: urlSessionConfiguration, delegate: self, delegateQueue: nil) } else { urlSession = URLSession.shared @@ -202,7 +206,7 @@ public class RequestSession: NSObject, URLSessionDelegate, SessionRequest { } else { // Poll again as SessionResponseState is not COMPLETE DispatchQueue.main.asyncAfter(deadline: .now() + Constants.defaultTimeoutS) { - self.getSessionStatus(baseUrl: baseUrl, process: process, requestParameters: requestParameters, trustedCertificates: trustedCertificates, completionHandler: completionHandler) + self.getSessionStatus(baseUrl: baseUrl, process: process, requestParameters: requestParameters, trustedCertificates: trustedCertificates, manualProxyConf: manualProxyConf, completionHandler: completionHandler) } } } else { diff --git a/SkSigningLib/SkSigningLib/Requests/Mobile-ID/RequestSignature.swift b/SkSigningLib/SkSigningLib/Requests/Mobile-ID/RequestSignature.swift index 39b91cf16..f96b6a4ac 100644 --- a/SkSigningLib/SkSigningLib/Requests/Mobile-ID/RequestSignature.swift +++ b/SkSigningLib/SkSigningLib/Requests/Mobile-ID/RequestSignature.swift @@ -33,7 +33,7 @@ protocol CertificateRequest { - requestParameters: Parameters that are sent to the service. Uses CertificateRequestParameters struct - completionHandler: On request response, callbacks Result */ - func getCertificate(baseUrl: String, requestParameters: CertificateRequestParameters, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void) + func getCertificate(baseUrl: String, requestParameters: CertificateRequestParameters, trustedCertificates: [String]?, manualProxyConf: Proxy, completionHandler: @escaping (Result) -> Void) } /** @@ -46,7 +46,7 @@ public class RequestSignature: NSObject, URLSessionDelegate, CertificateRequest private var trustedCerts: [String]? private weak var urlTask: URLSessionTask? - public func getCertificate(baseUrl: String, requestParameters: CertificateRequestParameters, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void) { + public func getCertificate(baseUrl: String, requestParameters: CertificateRequestParameters, trustedCertificates: [String]?, manualProxyConf: Proxy, completionHandler: @escaping (Result) -> Void) { guard UUID(uuidString: requestParameters.relyingPartyUUID) != nil else { completionHandler(.failure(.midInvalidAccessRights)); return } guard let url = URL(string: "\(baseUrl)/certificate") else { Logging.errorLog(forMethod: "RIA.MobileID - Certificate", httpResponse: nil, error: .invalidURL, extraInfo: "Invalid URL \(baseUrl)/certificate") @@ -72,11 +72,14 @@ public class RequestSignature: NSObject, URLSessionDelegate, CertificateRequest "\tnationalIdentityNumber: \(requestParameters.nationalIdentityNumber)\n" ) - let urlSessionConfiguration: URLSessionConfiguration + var urlSessionConfiguration: URLSessionConfiguration let urlSession: URLSession if trustedCertificates != nil { + urlSessionConfiguration = URLSessionConfiguration.default + ProxyUtil.configureURLSessionWithProxy(urlSessionConfiguration: &urlSessionConfiguration, manualProxyConf: manualProxyConf) + ProxyUtil.setProxyAuthorizationHeader(request: &request, urlSessionConfiguration: urlSessionConfiguration, manualProxyConf: manualProxyConf) urlSession = URLSession(configuration: urlSessionConfiguration, delegate: self, delegateQueue: nil) } else { urlSession = URLSession.shared diff --git a/SkSigningLib/SkSigningLib/Requests/Smart-ID/SIDRequest.swift b/SkSigningLib/SkSigningLib/Requests/Smart-ID/SIDRequest.swift index 49471c222..43c1cec85 100644 --- a/SkSigningLib/SkSigningLib/Requests/Smart-ID/SIDRequest.swift +++ b/SkSigningLib/SkSigningLib/Requests/Smart-ID/SIDRequest.swift @@ -31,7 +31,7 @@ protocol SIDRequestProtocol { - requestParameters: Parameters that are sent to the service. Uses SIDCertificateRequestParameters struct - completionHandler: On request success, callbacks Result */ - func getCertificate(baseUrl: String, country: String, nationalIdentityNumber: String, requestParameters: SIDCertificateRequestParameters, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void) + func getCertificate(baseUrl: String, country: String, nationalIdentityNumber: String, requestParameters: SIDCertificateRequestParameters, trustedCertificates: [String]?, manualProxyConf: Proxy, completionHandler: @escaping (Result) -> Void) /** Gets signature info for Smart-ID. @@ -42,7 +42,7 @@ protocol SIDRequestProtocol { - allowedInteractionsOrder: Interaction order. - completionHandler: On request success, callbacks Result */ - func getSignature(baseUrl: String, documentNumber: String, allowedInteractionsOrder: SIDSignatureRequestParametersV2?, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void) + func getSignature(baseUrl: String, documentNumber: String, allowedInteractionsOrder: SIDSignatureRequestParametersV2?, trustedCertificates: [String]?, manualProxyConf: Proxy, completionHandler: @escaping (Result) -> Void) /** Gets session status info for Smart-ID. @@ -53,7 +53,7 @@ protocol SIDRequestProtocol { - timeoutMs: TimeoutMs parameter that is used in URL - completionHandler: On request success, callbacks Result */ - func getSessionStatus(baseUrl: String, sessionId: String, timeoutMs: Int?, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void) + func getSessionStatus(baseUrl: String, sessionId: String, timeoutMs: Int?, trustedCertificates: [String]?, manualProxyConf: Proxy, completionHandler: @escaping (Result) -> Void) } /** @@ -65,23 +65,23 @@ public class SIDRequest: NSObject, URLSessionDelegate, SIDRequestProtocol { private var trustedCerts: [String]? private weak var urlTask: URLSessionTask? - public func getCertificate(baseUrl: String, country: String, nationalIdentityNumber: String, requestParameters: SIDCertificateRequestParameters, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void) { + public func getCertificate(baseUrl: String, country: String, nationalIdentityNumber: String, requestParameters: SIDCertificateRequestParameters, trustedCertificates: [String]?, manualProxyConf: Proxy, completionHandler: @escaping (Result) -> Void) { let url = "\(baseUrl)/certificatechoice/etsi/PNO\(country)-\(nationalIdentityNumber)" guard UUID(uuidString: requestParameters.relyingPartyUUID) != nil else { completionHandler(.failure(.sidInvalidAccessRights)); return } - exec(method: "Certificate", url: url, data: EncoderDecoder().encode(data: requestParameters), trustedCertificates: trustedCertificates, completionHandler: completionHandler) + exec(method: "Certificate", url: url, data: EncoderDecoder().encode(data: requestParameters), trustedCertificates: trustedCertificates, manualProxyConf: manualProxyConf, completionHandler: completionHandler) } - public func getSignature(baseUrl: String, documentNumber: String, allowedInteractionsOrder: SIDSignatureRequestParametersV2?, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void) { + public func getSignature(baseUrl: String, documentNumber: String, allowedInteractionsOrder: SIDSignatureRequestParametersV2?, trustedCertificates: [String]?, manualProxyConf: Proxy, completionHandler: @escaping (Result) -> Void) { let url = "\(baseUrl)/signature/document/\(documentNumber)" - exec(method: allowedInteractionsOrder?.relyingPartyName ?? "RIA.SmartID v2 - Signature", url: url, data: allowedInteractionsOrder?.asData, trustedCertificates: trustedCertificates, completionHandler: completionHandler) + exec(method: allowedInteractionsOrder?.relyingPartyName ?? "RIA.SmartID v2 - Signature", url: url, data: allowedInteractionsOrder?.asData, trustedCertificates: trustedCertificates, manualProxyConf: manualProxyConf, completionHandler: completionHandler) } - public func getSessionStatus(baseUrl: String, sessionId: String, timeoutMs: Int?, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void) { + public func getSessionStatus(baseUrl: String, sessionId: String, timeoutMs: Int?, trustedCertificates: [String]?, manualProxyConf: Proxy, completionHandler: @escaping (Result) -> Void) { let url = "\(baseUrl)/session/\(sessionId)?timeoutMs=\(timeoutMs ?? Constants.defaultTimeoutMs)" - exec(method: "Session", url: url, data: nil, trustedCertificates: trustedCertificates, completionHandler: completionHandler) + exec(method: "Session", url: url, data: nil, trustedCertificates: trustedCertificates, manualProxyConf: manualProxyConf, completionHandler: completionHandler) } - private func exec(method: String, url: String, data: Data?, trustedCertificates: [String]?, completionHandler: @escaping (Result) -> Void) { + private func exec(method: String, url: String, data: Data?, trustedCertificates: [String]?, manualProxyConf: Proxy, completionHandler: @escaping (Result) -> Void) { guard let _url = URL(string: url) else { Logging.errorLog(forMethod: "RIA.SmartID - \(method)", httpResponse: nil, error: .invalidURL, extraInfo: "Invalid URL \(url)") return completionHandler(.failure(.invalidURL)) @@ -98,12 +98,14 @@ public class SIDRequest: NSObject, URLSessionDelegate, SIDRequestProtocol { ) trustedCerts = trustedCertificates - let config = URLSessionConfiguration.default + var config = URLSessionConfiguration.default config.requestCachePolicy = .reloadIgnoringLocalCacheData config.urlCache = nil let urlSession: URLSession if trustedCertificates != nil { + ProxyUtil.configureURLSessionWithProxy(urlSessionConfiguration: &config, manualProxyConf: manualProxyConf) + ProxyUtil.setProxyAuthorizationHeader(request: &request, urlSessionConfiguration: config, manualProxyConf: manualProxyConf) urlSession = URLSession(configuration: config, delegate: self, delegateQueue: nil) } else { urlSession = URLSession.shared diff --git a/SkSigningLib/SkSigningLib/Utilities/Proxy/Proxy.swift b/SkSigningLib/SkSigningLib/Utilities/Proxy/Proxy.swift new file mode 100644 index 000000000..85c7ecca2 --- /dev/null +++ b/SkSigningLib/SkSigningLib/Utilities/Proxy/Proxy.swift @@ -0,0 +1,40 @@ +// +// Proxy.swift +// MoppApp +// +/* + * Copyright 2017 - 2023 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +import Foundation + +public struct Proxy { + public var host: String + public var port: Int + public var username: String + public var password: String + public var isSSLEnabled: Bool + + public init(host: String, port: Int, username: String, password: String, isSSLEnabled: Bool) { + self.host = host + self.port = port + self.username = username + self.password = password + self.isSSLEnabled = isSSLEnabled + } +} diff --git a/SkSigningLib/SkSigningLib/Utilities/Proxy/ProxyUtil.swift b/SkSigningLib/SkSigningLib/Utilities/Proxy/ProxyUtil.swift new file mode 100644 index 000000000..25f5089ac --- /dev/null +++ b/SkSigningLib/SkSigningLib/Utilities/Proxy/ProxyUtil.swift @@ -0,0 +1,94 @@ +// +// ProxyUtil.swift +// MoppApp +// +/* + * Copyright 2017 - 2023 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +import Foundation + +public enum ProxySetting: String, Codable { + case noProxy + case systemProxy + case manualProxy +} + +public class ProxyUtil { + + public static func createProxyConfiguration(proxySetting: ProxySetting, proxyHost: String?, proxyPort: Int?, proxyUsername: String?, proxyPassword: String?, isProxySSLEnabled: Bool) -> [AnyHashable: Any]? { + + // Use manual proxy settings + if proxySetting == .manualProxy, isProxySSLEnabled, let host = proxyHost, let port = proxyPort, !host.isEmpty, port > 0 { + let proxyConfiguration: [AnyHashable : Any] = [ + String(kCFNetworkProxiesHTTPEnable): 1, + String(kCFNetworkProxiesHTTPProxy): host, + String(kCFNetworkProxiesHTTPPort): port, + String(kCFProxyUsernameKey): proxyUsername ?? "", + String(kCFProxyPasswordKey): proxyPassword ?? "", + // Using deprecated keys because HTTPS urls may not be routed through a proxy + // https://developer.apple.com/forums/thread/19356?answerId=131709022#131709022 + String(kCFStreamPropertyHTTPSProxyHost): host, + String(kCFStreamPropertyHTTPSProxyPort): port + ] + + return proxyConfiguration + } + // Check if system proxy settings exist. iOS automatically handles proxy configuration with system settings + else if proxySetting == .systemProxy, isProxySSLEnabled, let proxySettingsUnmanaged = CFNetworkCopySystemProxySettings() { + let proxySettings = proxySettingsUnmanaged.takeRetainedValue() as? [AnyHashable : Any] + if let systemHost = proxySettings?[kCFNetworkProxiesHTTPProxy] as? String, let systemPort = proxySettings?[kCFNetworkProxiesHTTPPort] as? Int, !systemHost.isEmpty && systemPort > 0 { + return nil + } + } + + return [ + String(kCFNetworkProxiesHTTPEnable): 0 + ] + } + + public static func getProxyAuthorizationHeader(username: String, password: String) -> String? { + let credentials = "\(username):\(password)" + + if let credentialsData = credentials.data(using: .utf8) { + return credentialsData.base64EncodedString(options: []) + } + + return nil + } + + public static func setProxyAuthorizationHeader(request: inout URLRequest, urlSessionConfiguration: URLSessionConfiguration, manualProxyConf: Proxy) { + if let connectionProxyDictionary = urlSessionConfiguration.connectionProxyDictionary, + !connectionProxyDictionary.isEmpty, + !manualProxyConf.host.isEmpty, + let proxyHeader = ProxyUtil.getProxyAuthorizationHeader(username: manualProxyConf.username, password: manualProxyConf.password) { + + request.setValue("Basic \(proxyHeader)", forHTTPHeaderField: "Proxy-Authorization") + } + } + + public static func configureURLSessionWithProxy(urlSessionConfiguration: inout URLSessionConfiguration, manualProxyConf: Proxy) { + let proxyConfiguration = ProxyUtil.createProxyConfiguration( + proxySetting: .manualProxy, + proxyHost: manualProxyConf.host, proxyPort: manualProxyConf.port, proxyUsername: manualProxyConf.username, proxyPassword: manualProxyConf.password, isProxySSLEnabled: manualProxyConf.isSSLEnabled) + + if let proxyConf = proxyConfiguration { + urlSessionConfiguration.connectionProxyDictionary = proxyConf + } + } +}