mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 15:22:39 +00:00
Add AuthenticatedSessionViewControllerFactory to set up a authenticated flow for a given CS API request
This commit is contained in:
parent
26995effcb
commit
c162cb6a78
5 changed files with 197 additions and 2 deletions
|
@ -10,6 +10,7 @@ Improvements:
|
|||
* Key backup: Connect/restore backup created with SSSS (#3124).
|
||||
* E2E by default: Disable it if the HS admin disabled it (#3305).
|
||||
* Key backup: Add secure backup creation flow (#3344).
|
||||
* Add AuthenticatedSessionViewControllerFactory to set up a authenticated flow for a given CS API request.
|
||||
|
||||
Bug fix:
|
||||
* CallVC: Declined calls now properly reset call view controller, thanks to @Legi429 (#2877).
|
||||
|
|
|
@ -119,6 +119,7 @@
|
|||
32F6B96D2270623100BBA352 /* KeyVerificationDataLoadingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F6B9672270623100BBA352 /* KeyVerificationDataLoadingViewModel.swift */; };
|
||||
32F6B96E2270623100BBA352 /* KeyVerificationDataLoadingViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F6B9682270623100BBA352 /* KeyVerificationDataLoadingViewModelType.swift */; };
|
||||
32FDC1CD2386CD390084717A /* RiotSettingIntegrationProvisioning.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FDC1CC2386CD390084717A /* RiotSettingIntegrationProvisioning.swift */; };
|
||||
32FEFA9924A528FD005237F6 /* AuthenticatedSessionViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32FEFA9824A528FD005237F6 /* AuthenticatedSessionViewControllerFactory.swift */; };
|
||||
3AF393339D2D566CE14AC200 /* Pods_RiotTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 129EB7E27E7E4AC3F5F098F5 /* Pods_RiotTests.framework */; };
|
||||
405FD41D306133A48D9B5AA1 /* Pods_RiotPods_Riot.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1ACF09217ADF1D7E7A35BC02 /* Pods_RiotPods_Riot.framework */; };
|
||||
670966FEFE120D865FD8A5B6 /* Pods_RiotPods_SiriIntents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51187E952D5CECF6D6F5A28E /* Pods_RiotPods_SiriIntents.framework */; };
|
||||
|
@ -951,6 +952,7 @@
|
|||
32F6B9672270623100BBA352 /* KeyVerificationDataLoadingViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationDataLoadingViewModel.swift; sourceTree = "<group>"; };
|
||||
32F6B9682270623100BBA352 /* KeyVerificationDataLoadingViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyVerificationDataLoadingViewModelType.swift; sourceTree = "<group>"; };
|
||||
32FDC1CC2386CD390084717A /* RiotSettingIntegrationProvisioning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RiotSettingIntegrationProvisioning.swift; sourceTree = "<group>"; };
|
||||
32FEFA9824A528FD005237F6 /* AuthenticatedSessionViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticatedSessionViewControllerFactory.swift; sourceTree = "<group>"; };
|
||||
3942DD65EBEB7AE647C6392A /* Pods-RiotPods-SiriIntents.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotPods-SiriIntents.debug.xcconfig"; path = "Target Support Files/Pods-RiotPods-SiriIntents/Pods-RiotPods-SiriIntents.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
3D78489021AC9E6400B98A7D /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
3D78489121AC9E6500B98A7D /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
|
@ -2200,6 +2202,14 @@
|
|||
path = Modal;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
32FEFA9724A52861005237F6 /* AuthenticatedSession */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
32FEFA9824A528FD005237F6 /* AuthenticatedSessionViewControllerFactory.swift */,
|
||||
);
|
||||
path = AuthenticatedSession;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4220F60B660591FD80AF3428 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -3014,6 +3024,7 @@
|
|||
B1B5567620EE6C4C00210D55 /* Modules */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
32FEFA9724A52861005237F6 /* AuthenticatedSession */,
|
||||
B1B556EA20EE6C4C00210D55 /* Main */,
|
||||
B1B556CA20EE6C4C00210D55 /* TabBar */,
|
||||
B1B556F920EE6C4C00210D55 /* Authentication */,
|
||||
|
@ -5456,6 +5467,7 @@
|
|||
3232AB4F2256558300AD6A5C /* TemplateScreenViewController.swift in Sources */,
|
||||
B1B558FC20EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleBubbleCell.m in Sources */,
|
||||
B1B5572920EE6C4D00210D55 /* RoomFilesViewController.m in Sources */,
|
||||
32FEFA9924A528FD005237F6 /* AuthenticatedSessionViewControllerFactory.swift in Sources */,
|
||||
B1BEE74B23E093260003A4CB /* UserVerificationSessionStatusViewAction.swift in Sources */,
|
||||
B1098C1021ED07E4000DDA48 /* Presentable.swift in Sources */,
|
||||
B1BEE73923DF44A60003A4CB /* UserVerificationSessionsStatusViewController.swift in Sources */,
|
||||
|
|
|
@ -605,6 +605,8 @@
|
|||
"manage_session_not_trusted" = "Not trusted";
|
||||
"manage_session_sign_out" = "Sign out of this session";
|
||||
|
||||
// AuthenticatedSessionViewControllerFactory
|
||||
"authenticated_session_flow_not_supported" = "This app does not support the authentication mechanism on your homeserver.";
|
||||
|
||||
// Identity server settings
|
||||
"identity_server_settings_title" = "Identity Server";
|
||||
|
|
|
@ -310,6 +310,10 @@ internal enum VectorL10n {
|
|||
internal static var authUsernameInUse: String {
|
||||
return VectorL10n.tr("Vector", "auth_username_in_use")
|
||||
}
|
||||
/// This app does not support the authentication mechanism on your homeserver.
|
||||
internal static var authenticatedSessionFlowNotSupported: String {
|
||||
return VectorL10n.tr("Vector", "authenticated_session_flow_not_supported")
|
||||
}
|
||||
/// Back
|
||||
internal static var back: String {
|
||||
return VectorL10n.tr("Vector", "back")
|
||||
|
@ -1394,7 +1398,7 @@ internal enum VectorL10n {
|
|||
internal static var keyBackupRecoverTitle: String {
|
||||
return VectorL10n.tr("Vector", "key_backup_recover_title")
|
||||
}
|
||||
/// Safeguard against losing access to encrypted messages & data
|
||||
/// Start using Key Backup
|
||||
internal static var keyBackupSetupBannerSubtitle: String {
|
||||
return VectorL10n.tr("Vector", "key_backup_setup_banner_subtitle")
|
||||
}
|
||||
|
@ -3066,7 +3070,7 @@ internal enum VectorL10n {
|
|||
internal static var secureKeyBackupSetupCancelAlertTitle: String {
|
||||
return VectorL10n.tr("Vector", "secure_key_backup_setup_cancel_alert_title")
|
||||
}
|
||||
/// Safe guard against losing access to encrypted messages & data by backing up encryption keys on your server.
|
||||
/// Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.
|
||||
internal static var secureKeyBackupSetupIntroInfo: String {
|
||||
return VectorL10n.tr("Vector", "secure_key_backup_setup_intro_info")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
Copyright 2020 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
|
||||
|
||||
enum AuthenticatedSessionViewControllerFactoryErrorCode: Int {
|
||||
case flowNotSupported = 0
|
||||
}
|
||||
|
||||
/// This class creates view controllers that can handle an authentication flow for given requests.
|
||||
@objcMembers
|
||||
final class AuthenticatedSessionViewControllerFactory: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
static let errorDomain = "AuthenticatedSessionViewControllerFactoryErrorDomain"
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession) {
|
||||
self.session = session
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Public methods
|
||||
|
||||
/// Create a view controller to handle an authentication flow for a given request.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - path: the request path.
|
||||
/// - httpMethod: the request http method.
|
||||
/// - title: the title to use in the view controller.
|
||||
/// - message: the information to display in the view controller.
|
||||
/// - onViewController: the block called when the view controller is ready. The caller must display it.
|
||||
/// - onAuthenticated: the block called when the user finished to enter their credentials.
|
||||
/// - onCancelled: the block called when the user cancelled the authentication.
|
||||
/// - onFailure: the blocked called on error.
|
||||
func viewController(forPath path: String,
|
||||
httpMethod: String,
|
||||
title: String?,
|
||||
message: String?,
|
||||
onViewController: @escaping (UIViewController) -> Void,
|
||||
onAuthenticated: @escaping (NSDictionary) -> Void,
|
||||
onCancelled: @escaping () -> Void,
|
||||
onFailure: @escaping (NSError) -> Void) {
|
||||
|
||||
// Get the authentication flow required for this API
|
||||
session.matrixRestClient.authSessionForRequest(withMethod: httpMethod, path: path, parameters: [:], success: { [weak self] (authenticationSession) in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let authenticationSession = authenticationSession, let flows = authenticationSession.flows else {
|
||||
onFailure(self.unsupportedFlowError())
|
||||
return
|
||||
}
|
||||
|
||||
// Return the corresponding VC
|
||||
if self.hasPasswordFlow(inFlows: flows) {
|
||||
let authViewController = self.createPasswordViewController(title: title,
|
||||
message: message,
|
||||
authenticationSession: authenticationSession,
|
||||
onAuthenticated: onAuthenticated,
|
||||
onCancelled: onCancelled,
|
||||
onFailure: onFailure)
|
||||
onViewController(authViewController)
|
||||
} else {
|
||||
// Flow not supported yet
|
||||
onFailure(self.unsupportedFlowError())
|
||||
}
|
||||
|
||||
}, failure: { (error) in
|
||||
guard let error = error as NSError? else {
|
||||
return
|
||||
}
|
||||
|
||||
onFailure(error)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
private func unsupportedFlowError() -> NSError {
|
||||
return NSError(domain: AuthenticatedSessionViewControllerFactory.errorDomain,
|
||||
code: AuthenticatedSessionViewControllerFactoryErrorCode.flowNotSupported.rawValue,
|
||||
userInfo: [NSLocalizedDescriptionKey: VectorL10n.authenticatedSessionFlowNotSupported])
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Password flow
|
||||
|
||||
private func hasPasswordFlow(inFlows flows: [MXLoginFlow]) -> Bool {
|
||||
for flow in flows {
|
||||
if flow.type == kMXLoginFlowTypePassword || flow.stages.contains(kMXLoginFlowTypePassword) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private func createPasswordViewController(
|
||||
title: String?,
|
||||
message: String?,
|
||||
authenticationSession: MXAuthenticationSession,
|
||||
onAuthenticated: @escaping (NSDictionary) -> Void,
|
||||
onCancelled: @escaping () -> Void,
|
||||
onFailure: @escaping (NSError) -> Void) -> UIViewController {
|
||||
|
||||
// Use a simple UIAlertController as before
|
||||
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
|
||||
alertController.addTextField { (textField) in
|
||||
textField.isSecureTextEntry = true
|
||||
textField.placeholder = VectorL10n.authPasswordPlaceholder
|
||||
textField.keyboardType = .default
|
||||
}
|
||||
|
||||
alertController.addAction(UIAlertAction(title: Bundle.mxk_localizedString(forKey: "cancel"), style: .cancel, handler: { _ in
|
||||
onCancelled()
|
||||
}))
|
||||
|
||||
alertController.addAction(UIAlertAction(title: Bundle.mxk_localizedString(forKey: "ok"), style: .default, handler: { _ in
|
||||
|
||||
guard let password = alertController.textFields?.first?.text else {
|
||||
// Should not happen
|
||||
return
|
||||
}
|
||||
|
||||
guard let authParams = self.createAuthParams(password: password, authenticationSession: authenticationSession) else {
|
||||
onFailure(self.unsupportedFlowError())
|
||||
return
|
||||
}
|
||||
|
||||
onAuthenticated(authParams)
|
||||
}))
|
||||
|
||||
return alertController
|
||||
}
|
||||
|
||||
private func createAuthParams(password: String,
|
||||
authenticationSession: MXAuthenticationSession) -> NSDictionary? {
|
||||
guard let userId = self.session.myUserId, let session = authenticationSession.session else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return [
|
||||
"type": kMXLoginFlowTypePassword,
|
||||
"session": session,
|
||||
"user": userId,
|
||||
"password": password
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue