mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 03:48:27 +00:00
Add a certificate validator hook. (#3069)
* Add a certificate validator hook. * General tidy up of AppHooks. * Don't worry about the generic call links.
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class AppHooks: AppHooksProtocol {
|
||||
#if IS_MAIN_APP
|
||||
private(set) var appSettingsHook: AppSettingsHookProtocol = DefaultAppSettingsHook()
|
||||
func registerAppSettingsHook(_ hook: AppSettingsHookProtocol) {
|
||||
appSettingsHook = hook
|
||||
}
|
||||
|
||||
private(set) var bugReportHook: BugReportHookProtocol = DefaultBugReportHook()
|
||||
func registerBugReportHook(_ hook: BugReportHookProtocol) {
|
||||
bugReportHook = hook
|
||||
}
|
||||
|
||||
private(set) var certificateValidatorHook: CertificateValidatorHookProtocol = DefaultCertificateValidator()
|
||||
func registerCertificateValidatorHook(_ hook: CertificateValidatorHookProtocol) {
|
||||
certificateValidatorHook = hook
|
||||
}
|
||||
#endif
|
||||
|
||||
private(set) var clientBuilderHook: ClientBuilderHookProtocol = DefaultClientBuilderHook()
|
||||
func registerClientBuilderHook(_ hook: ClientBuilderHookProtocol) {
|
||||
clientBuilderHook = hook
|
||||
}
|
||||
}
|
||||
|
||||
protocol AppHooksProtocol {
|
||||
func configure()
|
||||
}
|
||||
|
||||
extension AppHooksProtocol {
|
||||
func configure() { }
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol AppSettingsHookProtocol {
|
||||
func configure(_ appSettings: AppSettings) -> AppSettings
|
||||
}
|
||||
|
||||
struct DefaultAppSettingsHook: AppSettingsHookProtocol {
|
||||
func configure(_ appSettings: AppSettings) -> AppSettings { appSettings }
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol BugReportHookProtocol {
|
||||
func update(_ bugReport: BugReport) -> BugReport
|
||||
}
|
||||
|
||||
struct DefaultBugReportHook: BugReportHookProtocol {
|
||||
func update(_ bugReport: BugReport) -> BugReport { bugReport }
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol CertificateValidatorHookProtocol {
|
||||
func respondTo(_ challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?)
|
||||
}
|
||||
|
||||
struct DefaultCertificateValidator: CertificateValidatorHookProtocol {
|
||||
func respondTo(_ challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) {
|
||||
(.performDefaultHandling, nil)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import MatrixRustSDK
|
||||
|
||||
protocol ClientBuilderHookProtocol {
|
||||
func configure(_ builder: ClientBuilder) -> ClientBuilder
|
||||
}
|
||||
|
||||
struct DefaultClientBuilderHook: ClientBuilderHookProtocol {
|
||||
func configure(_ builder: ClientBuilder) -> ClientBuilder { builder }
|
||||
}
|
||||
@@ -74,7 +74,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
windowManager = WindowManager(appDelegate: appDelegate)
|
||||
appMediator = AppMediator(windowManager: windowManager)
|
||||
|
||||
let appSettings = appHooks.runAppSettingsHook(AppSettings())
|
||||
let appSettings = appHooks.appSettingsHook.configure(AppSettings())
|
||||
|
||||
MXLog.configure(logLevel: appSettings.logLevel)
|
||||
|
||||
@@ -534,6 +534,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
roomTimelineControllerFactory: RoomTimelineControllerFactory(),
|
||||
appMediator: appMediator,
|
||||
appSettings: appSettings,
|
||||
appHooks: appHooks,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
notificationManager: notificationManager,
|
||||
isNewLogin: isNewLogin)
|
||||
|
||||
@@ -32,6 +32,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
private let elementCallService: ElementCallServiceProtocol
|
||||
private let appMediator: AppMediatorProtocol
|
||||
private let appSettings: AppSettings
|
||||
private let appHooks: AppHooks
|
||||
private let analytics: AnalyticsService
|
||||
private let notificationManager: NotificationManagerProtocol
|
||||
|
||||
@@ -74,6 +75,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol,
|
||||
appMediator: AppMediatorProtocol,
|
||||
appSettings: AppSettings,
|
||||
appHooks: AppHooks,
|
||||
analytics: AnalyticsService,
|
||||
notificationManager: NotificationManagerProtocol,
|
||||
isNewLogin: Bool) {
|
||||
@@ -85,6 +87,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
self.roomTimelineControllerFactory = roomTimelineControllerFactory
|
||||
self.appMediator = appMediator
|
||||
self.appSettings = appSettings
|
||||
self.appHooks = appHooks
|
||||
self.analytics = analytics
|
||||
self.notificationManager = notificationManager
|
||||
|
||||
@@ -561,7 +564,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
clientID: InfoPlistReader.main.bundleIdentifier,
|
||||
elementCallBaseURL: appSettings.elementCallBaseURL,
|
||||
elementCallBaseURLOverride: appSettings.elementCallBaseURLOverride,
|
||||
colorScheme: colorScheme))
|
||||
colorScheme: colorScheme,
|
||||
appHooks: appHooks))
|
||||
|
||||
callScreenCoordinator.actions
|
||||
.sink { [weak self] action in
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
// MARK: Registration
|
||||
|
||||
class AppHooks: AppHooksProtocol {
|
||||
#if IS_MAIN_APP
|
||||
private var appSettingsHook: AppSettingsHookProtocol?
|
||||
func registerAppSettingsHook(_ hook: AppSettingsHookProtocol) {
|
||||
appSettingsHook = hook
|
||||
}
|
||||
|
||||
func runAppSettingsHook(_ appSettings: AppSettings) -> AppSettings {
|
||||
guard let appSettingsHook else { return appSettings }
|
||||
return appSettingsHook.run(appSettings: appSettings)
|
||||
}
|
||||
|
||||
private var bugReportHook: BugReportHookProtocol?
|
||||
func registerBugReportHook(_ hook: BugReportHookProtocol) {
|
||||
bugReportHook = hook
|
||||
}
|
||||
|
||||
func runBugReportHook(_ bugReport: BugReport) -> BugReport {
|
||||
guard let bugReportHook else { return bugReport }
|
||||
return bugReportHook.run(bugReport: bugReport)
|
||||
}
|
||||
#endif
|
||||
|
||||
private var clientBuilderHook: ClientBuilderHookProtocol?
|
||||
func registerClientBuilderHook(_ hook: ClientBuilderHookProtocol) {
|
||||
clientBuilderHook = hook
|
||||
}
|
||||
|
||||
func runClientBuilderHook(_ clientBuilder: ClientBuilder) -> ClientBuilder {
|
||||
guard let clientBuilderHook else { return clientBuilder }
|
||||
return clientBuilderHook.run(builder: clientBuilder)
|
||||
}
|
||||
}
|
||||
|
||||
protocol AppHooksProtocol {
|
||||
func configure()
|
||||
}
|
||||
|
||||
extension AppHooksProtocol {
|
||||
func configure() { }
|
||||
}
|
||||
|
||||
// MARK: Protocols
|
||||
|
||||
#if IS_MAIN_APP
|
||||
protocol AppSettingsHookProtocol {
|
||||
func run(appSettings: AppSettings) -> AppSettings
|
||||
}
|
||||
|
||||
protocol BugReportHookProtocol {
|
||||
func run(bugReport: BugReport) -> BugReport
|
||||
}
|
||||
#endif
|
||||
|
||||
protocol ClientBuilderHookProtocol {
|
||||
func run(builder: ClientBuilder) -> ClientBuilder
|
||||
}
|
||||
@@ -42,6 +42,6 @@ extension ClientBuilder {
|
||||
builder = builder.proxy(url: httpProxy)
|
||||
}
|
||||
|
||||
return appHooks.runClientBuilderHook(builder)
|
||||
return appHooks.clientBuilderHook.configure(builder)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ struct CallScreenCoordinatorParameters {
|
||||
let elementCallBaseURL: URL
|
||||
let elementCallBaseURLOverride: URL?
|
||||
let colorScheme: ColorScheme
|
||||
let appHooks: AppHooks
|
||||
}
|
||||
|
||||
enum CallScreenCoordinatorAction {
|
||||
@@ -47,7 +48,8 @@ final class CallScreenCoordinator: CoordinatorProtocol {
|
||||
clientID: parameters.clientID,
|
||||
elementCallBaseURL: parameters.elementCallBaseURL,
|
||||
elementCallBaseURLOverride: parameters.elementCallBaseURLOverride,
|
||||
colorScheme: parameters.colorScheme)
|
||||
colorScheme: parameters.colorScheme,
|
||||
appHooks: parameters.appHooks)
|
||||
}
|
||||
|
||||
func start() {
|
||||
|
||||
@@ -24,6 +24,9 @@ struct CallScreenViewState: BindableState {
|
||||
let messageHandler: String
|
||||
let script: String?
|
||||
var url: URL?
|
||||
|
||||
let certificateValidator: CertificateValidatorHookProtocol
|
||||
|
||||
var bindings = Bindings()
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,8 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
|
||||
clientID: String,
|
||||
elementCallBaseURL: URL,
|
||||
elementCallBaseURLOverride: URL?,
|
||||
colorScheme: ColorScheme) {
|
||||
colorScheme: ColorScheme,
|
||||
appHooks: AppHooks) {
|
||||
guard let deviceID = clientProxy.deviceID else { fatalError("Missing device ID for the call.") }
|
||||
|
||||
self.elementCallService = elementCallService
|
||||
@@ -52,7 +53,8 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
|
||||
widgetDriver = roomProxy.elementCallWidgetDriver(deviceID: deviceID)
|
||||
|
||||
super.init(initialViewState: CallScreenViewState(messageHandler: Self.eventHandlerName,
|
||||
script: Self.eventHandlerInjectionScript))
|
||||
script: Self.eventHandlerInjectionScript,
|
||||
certificateValidator: appHooks.certificateValidatorHook))
|
||||
|
||||
state.bindings.javaScriptMessageHandler = { [weak self] message in
|
||||
guard let self,
|
||||
|
||||
@@ -54,6 +54,7 @@ private struct WebView: UIViewRepresentable {
|
||||
@MainActor
|
||||
class Coordinator: NSObject, WKUIDelegate, WKNavigationDelegate {
|
||||
private weak var viewModelContext: CallScreenViewModel.Context?
|
||||
private let certificateValidator: CertificateValidatorHookProtocol
|
||||
|
||||
private(set) var webView: WKWebView!
|
||||
|
||||
@@ -61,6 +62,7 @@ private struct WebView: UIViewRepresentable {
|
||||
|
||||
init(viewModelContext: CallScreenViewModel.Context) {
|
||||
self.viewModelContext = viewModelContext
|
||||
certificateValidator = viewModelContext.viewState.certificateValidator
|
||||
|
||||
super.init()
|
||||
|
||||
@@ -131,6 +133,10 @@ private struct WebView: UIViewRepresentable {
|
||||
|
||||
// MARK: - WKNavigationDelegate
|
||||
|
||||
func webView(_ webView: WKWebView, respondTo challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) {
|
||||
await certificateValidator.respondTo(challenge)
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction) async -> WKNavigationActionPolicy {
|
||||
// Allow any content from the main URL.
|
||||
if navigationAction.request.url?.host == url.host {
|
||||
@@ -193,7 +199,8 @@ struct CallScreen_Previews: PreviewProvider {
|
||||
clientID: "io.element.elementx",
|
||||
elementCallBaseURL: "https://call.element.io",
|
||||
elementCallBaseURLOverride: nil,
|
||||
colorScheme: .light)
|
||||
colorScheme: .light,
|
||||
appHooks: AppHooks())
|
||||
}()
|
||||
|
||||
static var previews: some View {
|
||||
|
||||
@@ -58,7 +58,7 @@ class BugReportService: NSObject, BugReportServiceProtocol {
|
||||
// swiftlint:disable:next cyclomatic_complexity
|
||||
func submitBugReport(_ bugReport: BugReport,
|
||||
progressListener: CurrentValueSubject<Double, Never>) async -> Result<SubmitBugReportResponse, BugReportServiceError> {
|
||||
let bugReport = appHooks.runBugReportHook(bugReport)
|
||||
let bugReport = appHooks.bugReportHook.update(bugReport)
|
||||
|
||||
var params = [
|
||||
MultipartFormData(key: "text", type: .text(value: bugReport.text)),
|
||||
|
||||
@@ -532,6 +532,7 @@ class MockScreen: Identifiable {
|
||||
roomTimelineControllerFactory: RoomTimelineControllerFactoryMock(configuration: .init()),
|
||||
appMediator: appMediator,
|
||||
appSettings: appSettings,
|
||||
appHooks: AppHooks(),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
notificationManager: NotificationManagerMock(),
|
||||
isNewLogin: false)
|
||||
@@ -648,6 +649,7 @@ class MockScreen: Identifiable {
|
||||
roomTimelineControllerFactory: RoomTimelineControllerFactoryMock(configuration: .init(timelineController: timelineController)),
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: appSettings,
|
||||
appHooks: AppHooks(),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
notificationManager: NotificationManagerMock(),
|
||||
isNewLogin: false)
|
||||
|
||||
Reference in New Issue
Block a user