mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-20 00:24:43 +02:00
Fix pbxproj conflicts.
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// AuthenticatedEndpointRequest represents authenticated API endpoint request.
|
||||
@objcMembers
|
||||
class AuthenticatedEndpointRequest: NSObject {
|
||||
|
||||
let path: String
|
||||
let httpMethod: String
|
||||
|
||||
init(path: String, httpMethod: String) {
|
||||
self.path = path
|
||||
self.httpMethod = httpMethod
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// Build authentication parameters depending on login type
|
||||
final class AuthenticationParametersBuilder {
|
||||
|
||||
func buildPasswordParameters(sessionId: String,
|
||||
userId: String,
|
||||
password: String) -> [String: Any]? {
|
||||
return [
|
||||
"type": MXLoginFlowType.password.identifier,
|
||||
"session": sessionId,
|
||||
"user": userId,
|
||||
"password": password
|
||||
]
|
||||
}
|
||||
|
||||
func buildTokenParameters(with loginToken: String) -> [String: Any] {
|
||||
return [
|
||||
"type": MXLoginFlowType.token.identifier,
|
||||
"token": loginToken
|
||||
]
|
||||
}
|
||||
|
||||
func buildOAuthParameters(with sessionId: String) -> [String: Any] {
|
||||
return [
|
||||
"session": sessionId
|
||||
]
|
||||
}
|
||||
}
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
/// UserInteractiveAuthenticationService errors
|
||||
enum UserInteractiveAuthenticationServiceError: Int, Error {
|
||||
case flowNotSupported = 0
|
||||
}
|
||||
|
||||
extension UserInteractiveAuthenticationServiceError: CustomNSError {
|
||||
static let errorDomain = "UserInteractiveAuthenticationServiceErrorDomain"
|
||||
|
||||
var errorCode: Int {
|
||||
return self.rawValue
|
||||
}
|
||||
|
||||
var errorUserInfo: [String: Any] {
|
||||
let userInfo: [String: Any]
|
||||
|
||||
switch self {
|
||||
case .flowNotSupported:
|
||||
userInfo = [NSLocalizedDescriptionKey: VectorL10n.authenticatedSessionFlowNotSupported]
|
||||
}
|
||||
return userInfo
|
||||
}
|
||||
}
|
||||
|
||||
/// AuthenticatedEndpointStatus indicates the success status after checking if an API endpoint needs authentication
|
||||
enum AuthenticatedEndpointStatus {
|
||||
case authenticationNotNeeded
|
||||
case authenticationNeeded(_ authenticationSession: MXAuthenticationSession)
|
||||
}
|
||||
|
||||
/// UserInteractiveAuthenticationService enables to check if an API endpoint needs authentication.
|
||||
@objcMembers
|
||||
final class UserInteractiveAuthenticationService: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
|
||||
// MARK: Public
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession) {
|
||||
self.session = session
|
||||
super.init()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
/// Check if API endpoint requires authentication and get authentication session if needed
|
||||
/// - Parameters:
|
||||
/// - request: Authenticated API endpoint request.
|
||||
/// - completion: The closure executed the operation completes. AuthenticatedEndpointStatus indicates if authentication is needed or not.
|
||||
/// - Returns: A `MXHTTPOperation` instance.
|
||||
@discardableResult
|
||||
func authenticatedEndpointStatus(for request: AuthenticatedEndpointRequest, completion: @escaping (Result<AuthenticatedEndpointStatus, Error>) -> Void) -> MXHTTPOperation {
|
||||
|
||||
return self.authenticationSession(with: request) { (authenticationSession) in
|
||||
|
||||
let status: AuthenticatedEndpointStatus
|
||||
|
||||
if let authenticationSession = authenticationSession {
|
||||
status = .authenticationNeeded(authenticationSession)
|
||||
} else {
|
||||
status = .authenticationNotNeeded
|
||||
}
|
||||
|
||||
completion(.success(status))
|
||||
} failure: { (error) in
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if API endpoint requires authentication.
|
||||
/// - Parameters:
|
||||
/// - request: Authenticated API endpoint request.
|
||||
/// - completion: The closure executed the operation completes.
|
||||
/// - Returns: A `MXHTTPOperation` instance.
|
||||
@discardableResult
|
||||
func canAuthenticate(with request: AuthenticatedEndpointRequest, completion: @escaping (Result<Bool, Error>) -> Void) -> MXHTTPOperation {
|
||||
|
||||
return self.authenticatedEndpointStatus(for: request) { (result) in
|
||||
switch result {
|
||||
case .success(let authenticationSessionResult):
|
||||
let canAuthenticate: Bool
|
||||
|
||||
switch authenticationSessionResult {
|
||||
case .authenticationNotNeeded:
|
||||
canAuthenticate = true
|
||||
case .authenticationNeeded(let authenticationSession):
|
||||
canAuthenticate = self.canAuthenticate(with: authenticationSession)
|
||||
}
|
||||
|
||||
completion(.success(canAuthenticate))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if API endpoint requires authentication.
|
||||
/// This method is compatible with Objective-C
|
||||
/// - Parameters:
|
||||
/// - request: Authenticated API endpoint request.
|
||||
/// - success: The closure executed the operation succeed. Get an MXAuthenticationSession if authentication is required or nil if there is no authentication needed.
|
||||
/// - failure: The closure executed the operation fails.
|
||||
/// - Returns: A `MXHTTPOperation` instance.
|
||||
@discardableResult
|
||||
func authenticationSession(with request: AuthenticatedEndpointRequest,
|
||||
success: @escaping (MXAuthenticationSession?) -> Void,
|
||||
failure: @escaping (Error) -> Void) -> MXHTTPOperation {
|
||||
// Get the authentication flow required for this API
|
||||
return self.session.matrixRestClient.authSessionForRequest(withMethod: request.httpMethod, path: request.path, parameters: [:], success: { [weak self] (authenticationSession) in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if let authenticationSession = authenticationSession {
|
||||
|
||||
if let flows = authenticationSession.flows {
|
||||
// Check if a supported flow exists
|
||||
if self.isThereAKnownFlow(inFlows: flows) {
|
||||
success(authenticationSession)
|
||||
} else if self.firstUncompletedStageAuthenticationFallbackURL(for: authenticationSession) != nil {
|
||||
success(authenticationSession)
|
||||
} else {
|
||||
// Flow not supported
|
||||
failure(UserInteractiveAuthenticationServiceError.flowNotSupported)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No need to authenticate
|
||||
success(nil)
|
||||
}
|
||||
}, failure: { (error) in
|
||||
guard let error = error else {
|
||||
return
|
||||
}
|
||||
failure(error)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the authentication fallback URL for the first uncompleted stage found.
|
||||
/// - Parameter authenticationSession: An authentication session for a given request.
|
||||
/// - Returns: The fallback URL for the first uncompleted stage found.
|
||||
func firstUncompletedStageAuthenticationFallbackURL(for authenticationSession: MXAuthenticationSession) -> URL? {
|
||||
guard let sessiondId = authenticationSession.session, let firstUncompletedStageIdentifier = self.firstUncompletedFlowIdentifier(in: authenticationSession) else {
|
||||
return nil
|
||||
}
|
||||
return self.authenticationFallbackURL(for: firstUncompletedStageIdentifier, sessionId: sessiondId)
|
||||
}
|
||||
|
||||
/// Build UIA fallback authentication URL for a given stage (https://matrix.org/docs/spec/client_server/latest#fallback)
|
||||
/// - Parameters:
|
||||
/// - flowIdentifier: The login type to authenticate with.
|
||||
/// - sessionId: The the ID of the session given by the homeserver.
|
||||
/// - Returns: a `MXHTTPOperation` instance.
|
||||
func authenticationFallbackURL(for flowIdentifier: String, sessionId: String) -> URL? {
|
||||
guard let homeserverStringURL = self.session.matrixRestClient.credentials.homeServer, let homeserverURL = URL(string: homeserverStringURL) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let fallbackPath = "\(kMXAPIPrefixPathR0)/auth/\(flowIdentifier)/fallback/web?session=\(sessionId)"
|
||||
|
||||
return URL(string: fallbackPath, relativeTo: homeserverURL)
|
||||
}
|
||||
|
||||
/// Find the first uncompleted login flow stage in a MXauthenticationSession.
|
||||
/// - Parameter authenticationSession: An authentication session for a given request.
|
||||
/// - Returns: Uncompleted login flow stage identifier.
|
||||
func firstUncompletedFlowIdentifier(in authenticationSession: MXAuthenticationSession) -> String? {
|
||||
|
||||
let completedStages = authenticationSession.completed ?? []
|
||||
|
||||
guard let flows = authenticationSession.flows else {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove nil values
|
||||
let allNonNullStages = flows.compactMap { $0.stages }
|
||||
|
||||
// Make a flat array of all stages
|
||||
let allStages: [String] = allNonNullStages.flatMap { $0 }
|
||||
|
||||
// Keep stages order
|
||||
let uncompletedStages = NSMutableOrderedSet(array: allStages)
|
||||
|
||||
// Keep uncompleted stages
|
||||
let completedStagesSet = NSOrderedSet(array: completedStages)
|
||||
uncompletedStages.minus(completedStagesSet)
|
||||
|
||||
let firstUncompletedFlowIdentifier = uncompletedStages.firstObject as? String
|
||||
return firstUncompletedFlowIdentifier
|
||||
}
|
||||
|
||||
/// Check if an array of login flows contains "m.login.password" flow.
|
||||
func hasPasswordFlow(inFlows flows: [MXLoginFlow]) -> Bool {
|
||||
for flow in flows {
|
||||
if flow.type == MXLoginFlowType.password.identifier || flow.stages.contains(MXLoginFlowType.password.identifier) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func isThereAKnownFlow(inFlows flows: [MXLoginFlow]) -> Bool {
|
||||
return self.hasPasswordFlow(inFlows: flows)
|
||||
}
|
||||
|
||||
private func canAuthenticate(with authenticationSession: MXAuthenticationSession) -> Bool {
|
||||
|
||||
if self.isThereAKnownFlow(inFlows: authenticationSession.flows) {
|
||||
return true
|
||||
}
|
||||
|
||||
if self.firstUncompletedStageAuthenticationFallbackURL(for: authenticationSession) != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
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
|
||||
|
||||
/// This class creates view controllers that can handle an authentication flow for given requests.
|
||||
@objcMembers
|
||||
final class UserInteractiveAuthenticationViewControllerFactory: NSObject {
|
||||
|
||||
// MARK: - Password flow
|
||||
|
||||
/// Create a view controller to handle a password authentication.
|
||||
/// - Parameters:
|
||||
/// - title: the title to use in the view controller.
|
||||
/// - message: the information to display in the view controller.
|
||||
/// - onPasswordEntered: the closure called when the enter the password.
|
||||
/// - onCancelled: the closure called when the user cancelled the authentication.
|
||||
/// - Returns: the password authentication view controller
|
||||
func createPasswordViewController(
|
||||
title: String?,
|
||||
message: String?,
|
||||
onPasswordEntered: @escaping (String) -> Void,
|
||||
onCancelled: @escaping () -> 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
|
||||
}
|
||||
onPasswordEntered(password)
|
||||
}))
|
||||
|
||||
return alertController
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user