Skip to content
Snippets Groups Projects
Commit 449d0370 authored by cpilaszewicz's avatar cpilaszewicz
Browse files

Extract model from viewModel

parent 847a9d96
No related branches found
No related tags found
4 merge requests!58Onboarding screens swift ui merge dev,!55WIP: Resolve "UI for gamifcation & stats",!47Provisional fix for OpenSSL 1.1+, needs testing,!39Resolve "Restructure Authentication"
......@@ -164,6 +164,7 @@
8428A8841F436ACC007649A5 /* GamificationElements.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8428A8541F4369CF007649A5 /* GamificationElements.xcassets */; };
976EEBED240D47C3006FE574 /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 976EEBEC240D47C3006FE574 /* AuthenticationViewModel.swift */; };
9771AA8C241161190023A096 /* MailAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9771AA8B241161190023A096 /* MailAccount.swift */; };
97AACD2424178C230078A68E /* AuthenticationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97AACD2324178C230078A68E /* AuthenticationModel.swift */; };
97B2F4D6240D321B000DB34E /* AuthenticationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97B2F4D5240D321B000DB34E /* AuthenticationScreen.swift */; };
988C9C5D240D507A006213F0 /* PhishingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 988C9C5C240D507A006213F0 /* PhishingTests.swift */; };
A102AA8A1EDDB4F40024B457 /* videoOnboarding2.m4v in Resources */ = {isa = PBXBuildFile; fileRef = A102AA891EDDB4E80024B457 /* videoOnboarding2.m4v */; };
......@@ -451,6 +452,7 @@
94EE54279AB591E0CAB8EFD8 /* Pods_enzevalos_iphone.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_enzevalos_iphone.framework; sourceTree = BUILT_PRODUCTS_DIR; };
976EEBEC240D47C3006FE574 /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = "<group>"; };
9771AA8B241161190023A096 /* MailAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailAccount.swift; sourceTree = "<group>"; };
97AACD2324178C230078A68E /* AuthenticationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationModel.swift; sourceTree = "<group>"; };
97B2F4D5240D321B000DB34E /* AuthenticationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationScreen.swift; sourceTree = "<group>"; };
988C9C5C240D507A006213F0 /* PhishingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhishingTests.swift; sourceTree = "<group>"; };
9A132EDE8BCA06ACDB505C22 /* Pods-enzevalos_iphoneUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-enzevalos_iphoneUITests.debug.xcconfig"; path = "../enzevalos_iphone_workspace/Pods/Target Support Files/Pods-enzevalos_iphoneUITests/Pods-enzevalos_iphoneUITests.debug.xcconfig"; sourceTree = "<group>"; };
......@@ -945,6 +947,7 @@
97B2F4D5240D321B000DB34E /* AuthenticationScreen.swift */,
976EEBEC240D47C3006FE574 /* AuthenticationViewModel.swift */,
9771AA8B241161190023A096 /* MailAccount.swift */,
97AACD2324178C230078A68E /* AuthenticationModel.swift */,
);
name = authentication;
sourceTree = "<group>";
......@@ -1712,6 +1715,7 @@
479C649A21F45DAF00A01071 /* HideShowPasswordTextField.swift in Sources */,
47CEAC98222541B40075B7DC /* MailSession.swift in Sources */,
F119D2901E364B59001D732A /* AnimatedSendIcon.swift in Sources */,
97AACD2424178C230078A68E /* AuthenticationModel.swift in Sources */,
4707096D1F8F9F4900657F41 /* ExportViewController.swift in Sources */,
F12060801DA540FE00F6EF37 /* RefreshControlExtension.swift in Sources */,
4751C6FC2344C8D1006B2A4D /* KeyTableViewController.swift in Sources */,
......
//
// AuthenticationModel.swift
// enzevalos_iphone
//
// Created by SWP Usable Security für Smartphones on 10.03.20.
// Copyright © 2020 fu-berlin. All rights reserved.
//
import Foundation
import Combine
class AuthenticationModel: NSObject {
enum AuthenticationResult {case Success, Timeout, Error}
static let instance: AuthenticationModel = AuthenticationModel()
var extendedValidation: Bool = false
private var currentIMAP: MailSession?
private var currentSMTP: MailSession?
var imapCallback: AuthenticationCallback? = nil
var smtpCallback: AuthenticationCallback? = nil
var imapConfigurationSuccessful = false
var smtpConfigurationSuccessful = false
var dispatchGroup = DispatchGroup()
/**
Start asynchronous tasks for checking IMAP and SMTP configuration with a *timeoutDelay* after which the tasks are not getting consumed anymore.
*/
func checkConfig(mailAccount: MailAccount, extendedValidation: Bool) -> Future<AuthenticationResult, Never> {
self.extendedValidation = extendedValidation
var future: Future<AuthenticationResult, Never>
dispatchGroup = DispatchGroup()
imapCallback = AuthenticationCallback(callback: onImapCompleted)
let imapItem = DispatchWorkItem { self.checkIMAPConfig(mailAccount: mailAccount, self.imapCallback! ) }
dispatchGroup.enter()
DispatchQueue.global().async(execute: imapItem)
smtpCallback = AuthenticationCallback(callback: onSmtpCompleted)
let smtpItem = DispatchWorkItem { self.checkSMTPConfig(mailAccount: mailAccount, self.smtpCallback!) }
dispatchGroup.enter()
DispatchQueue.global().async(execute: smtpItem)
future = Future { promise in
DispatchQueue.global().async {
let timeoutDelay: DispatchTimeInterval = DispatchTimeInterval.seconds(10)
let result = self.dispatchGroup.wait(timeout: DispatchTime.now() + timeoutDelay)
DispatchQueue.main.async {
promise(.success(self.onTimeout(timeoutResult: result)))
print("promise sent")
}
}
}
return future
}
func checkIMAPConfig(mailAccount: MailAccount, _ callback: AuthenticationCallback) {
imapConfigurationSuccessful = false
let mailSession: MailSession = setupIMAPSession(mailAccount: mailAccount, callback: callback)
currentIMAP = mailSession
if extendedValidation && mailSession.startTestingServerConfig(){
imapConfigurationSuccessful = true
} else {
if !mailSession.hasJsonFile && mailSession.startLongSearchOfServerConfig(hostFromAdr: false){
imapConfigurationSuccessful = true
} else {
if mailSession.startTestingServerConfigFromList() || mailSession.startLongSearchOfServerConfig(hostFromAdr: true){
imapConfigurationSuccessful = true
}
}
}
}
func checkSMTPConfig(mailAccount: MailAccount, _ callback: AuthenticationCallback) {
smtpConfigurationSuccessful = false
let mailSession: MailSession = setupSMTPSession(mailAccount: mailAccount, callback: callback)
currentSMTP = mailSession
if extendedValidation && mailSession.startTestingServerConfig() {
smtpConfigurationSuccessful = true
} else {
if !mailSession.hasJsonFile && mailSession.startLongSearchOfServerConfig(hostFromAdr: false) {
smtpConfigurationSuccessful = true
} else {
if mailSession.startTestingServerConfigFromList() || mailSession.startLongSearchOfServerConfig(hostFromAdr: true){
smtpConfigurationSuccessful = true
}
}
}
}
private func setupIMAPSession(mailAccount: MailAccount, callback: AuthenticationCallback) -> MailSession {
let mailSession = MailSession(configSession: SessionType.IMAP, mailAddress: mailAccount.emailAddress, password: mailAccount.password, username: mailAccount.username)
if extendedValidation {
let imapConnValue = mailAccount.imapEncryption
mailSession.setServer(hostname: mailAccount.imapServer, port: UInt32(mailAccount.imapPort), connType: imapConnValue, authType: mailAccount.authType)
}
let listenerIMAP = Listener(callback: callback, mailAccount: mailAccount)
mailSession.addListener(listener: listenerIMAP)
return mailSession
}
private func setupSMTPSession(mailAccount: MailAccount, callback: AuthenticationCallback) -> MailSession {
let mailSession = MailSession(configSession: SessionType.SMTP, mailAddress: mailAccount.emailAddress, password: mailAccount.password, username: mailAccount.username)
if extendedValidation {
let smtpConnValue = mailAccount.smtpEncryption
mailSession.setServer(hostname: mailAccount.smtpServer, port: UInt32(mailAccount.smtpPort), connType: smtpConnValue, authType: mailAccount.authType)
}
let listenerSMTP = Listener(callback: callback, mailAccount: mailAccount)
mailSession.addListener(listener: listenerSMTP)
return mailSession
}
func onTimeout(timeoutResult: DispatchTimeoutResult) -> AuthenticationResult {
imapCallback?.callback = nil
smtpCallback?.callback = nil
if timeoutResult == .success {
if imapConfigurationSuccessful && smtpConfigurationSuccessful {
return AuthenticationResult.Success
} else {
return AuthenticationResult.Error
}
} else {
return AuthenticationResult.Timeout
}
}
func onImapCompleted(imapWorks: Bool, _ login: String, _ password: String) {
if imapWorks {
_ = currentIMAP?.storeToUserDefaults()
imapConfigurationSuccessful = true
} else {
imapConfigurationSuccessful = false
}
dispatchGroup.leave()
}
func onSmtpCompleted(smtpWorks: Bool, _ login: String, _ password: String) {
if smtpWorks {
_ = currentSMTP?.storeToUserDefaults()
smtpConfigurationSuccessful = true
} else {
smtpConfigurationSuccessful = false
}
dispatchGroup.leave()
return
}
class Listener: MailSessionListener {
let callback: AuthenticationCallback
let mailAccount: MailAccount
init(callback: AuthenticationCallback, mailAccount: MailAccount) {
self.callback = callback
self.mailAccount = mailAccount
}
func testFinish(result: Bool) {
callback.onResult(worked: result, login: mailAccount.emailAddress, password: mailAccount.password)
}
}
}
class AuthenticationCallback {
var callback: ((Bool, String, String) -> Void)?
init(callback: @escaping (Bool, String, String) -> Void) {
self.callback = callback
}
func onResult(worked: Bool, login: String, password: String) {
if let callback = callback {
callback(worked, login, password)
}
}
}
......@@ -19,7 +19,7 @@ struct AuthenticationScreen: View {
@State private var imapEncryption = 0
@State private var smtpEncryption = 0
@ObservedObject private var viewModel = AuthenticationViewModel()
@ObservedObject private var viewModel = AuthenticationViewModel(authenticationModel: AuthenticationModel.instance)
var encryptionOptions = ["Plaintext", "StartTLS", "TLS/SSL"]
var body: some View {
......
......@@ -7,100 +7,51 @@
//
import Foundation
import Combine
class AuthenticationViewModel : ObservableObject {
@Published var errorMessage: String?
@Published var isDetailedAuthentication: Bool = false
var isGoogleAuth = false
var imapConfigurationSuccessful = false
var smtpConfigurationSuccessful = false
private var currentIMAP: MailSession?
private var currentSMTP: MailSession?
var imapCallback: AuthenticationCallback? = nil
var smtpCallback: AuthenticationCallback? = nil
var dispatchGroup = DispatchGroup()
let model: AuthenticationModel
var cancellable: AnyCancellable?
init(authenticationModel: AuthenticationModel) {
self.model = authenticationModel
}
func validate(_ login: String, _ password: String) -> Void {
let mailAccount = MailAccount(emailAddress: login, password: password)
checkConfig(mailAccount: mailAccount)
}
func detailValidation(_ login: String, _ password: String, _ username: String, _ imapServer: String, _ imapPort: String, _ imapEncryption: Int, _ smtpServer: String, _ smtpPort: String, _ smtpEncryption: Int) -> Void {
let mailAccount = MailAccount(emailAddress: login, password: password, username: username, imapServer: imapServer, imapPort: Int(imapPort)!, imapEncryption: 1 << imapEncryption, smtpServer: smtpServer, smtpPort: Int(smtpPort)!, smtpEncryption: 1 << smtpEncryption)
checkConfig(mailAccount: mailAccount)
}
/**
Start asynchronous tasks for checking IMAP and SMTP configuration with a *timeoutDelay* after which the tasks are not getting consumed anymore.
*/
fileprivate func checkConfig(mailAccount: MailAccount) {
dispatchGroup = DispatchGroup()
imapCallback = AuthenticationCallback(callback: onImapCompleted)
let imapItem = DispatchWorkItem { self.checkIMAPConfig(mailAccount: mailAccount, self.imapCallback! ) }
dispatchGroup.enter()
DispatchQueue.global().async(execute: imapItem)
smtpCallback = AuthenticationCallback(callback: onSmtpCompleted)
let smtpItem = DispatchWorkItem { self.checkSMTPConfig(mailAccount: mailAccount, self.smtpCallback!) }
dispatchGroup.enter()
DispatchQueue.global().async(execute: smtpItem)
DispatchQueue.global().async {
let timeoutDelay: DispatchTimeInterval = DispatchTimeInterval.seconds(10)
let result = self.dispatchGroup.wait(timeout: DispatchTime.now() + timeoutDelay)
DispatchQueue.main.async {
self.onTimeout(timeoutResult: result)
cancellable = model.checkConfig(mailAccount: mailAccount, extendedValidation: false).sink { promise in
switch promise {
case AuthenticationModel.AuthenticationResult.Success :
self.authenticationSucceed()
case AuthenticationModel.AuthenticationResult.Error :
self.authenticationFailed(error: MailServerConnectionError.AuthenticationError)
case AuthenticationModel.AuthenticationResult.Timeout :
self.timeoutNotification(result: promise)
}
}
}
func checkIMAPConfig(mailAccount: MailAccount, _ callback: AuthenticationCallback) {
imapConfigurationSuccessful = false
let mailSession: MailSession = setupIMAPSession(mailAccount: mailAccount, callback: callback)
currentIMAP = mailSession
if (isGoogleAuth || isDetailedAuthentication) && mailSession.startTestingServerConfig(){
imapConfigurationSuccessful = true
} else {
if !mailSession.hasJsonFile && mailSession.startLongSearchOfServerConfig(hostFromAdr: true){
imapConfigurationSuccessful = true
} else {
if mailSession.startTestingServerConfigFromList() || mailSession.startLongSearchOfServerConfig(hostFromAdr: true){
imapConfigurationSuccessful = true
}
}
}
}
func checkSMTPConfig(mailAccount: MailAccount, _ callback: AuthenticationCallback) {
smtpConfigurationSuccessful = false
let mailSession: MailSession = setupSMTPSession(mailAccount: mailAccount, callback: callback)
currentSMTP = mailSession
if (isGoogleAuth || isDetailedAuthentication) && mailSession.startTestingServerConfig() {
smtpConfigurationSuccessful = true
} else {
if !mailSession.hasJsonFile && mailSession.startLongSearchOfServerConfig(hostFromAdr: !isDetailedAuthentication) {
smtpConfigurationSuccessful = true
} else {
if mailSession.startTestingServerConfigFromList() || mailSession.startLongSearchOfServerConfig(hostFromAdr: true){
smtpConfigurationSuccessful = true
}
func detailValidation(_ login: String, _ password: String, _ username: String, _ imapServer: String, _ imapPort: String, _ imapEncryption: Int, _ smtpServer: String, _ smtpPort: String, _ smtpEncryption: Int) -> Void {
let mailAccount = MailAccount(emailAddress: login, password: password, username: username, imapServer: imapServer, imapPort: Int(imapPort)!, imapEncryption: 1 << imapEncryption, smtpServer: smtpServer, smtpPort: Int(smtpPort)!, smtpEncryption: 1 << smtpEncryption)
cancellable = model.checkConfig(mailAccount: mailAccount, extendedValidation: true).sink { promise in
switch promise {
case AuthenticationModel.AuthenticationResult.Success :
self.authenticationSucceed()
case AuthenticationModel.AuthenticationResult.Error :
self.authenticationFailed(error: MailServerConnectionError.AuthenticationError)
case AuthenticationModel.AuthenticationResult.Timeout :
self.timeoutNotification(result: promise)
}
}
}
func startGoogleOauth() {
isGoogleAuth = true
googleLogin()
}
func googleLogin() {
guard let vc = AppDelegate.getAppDelegate().window?.rootViewController else {
print("No view controller!")
return
......@@ -110,112 +61,33 @@ class AuthenticationViewModel : ObservableObject {
EmailHelper.singleton().doEmailLoginIfRequired(onVC: vc, completionBlock: {
guard let userEmail = EmailHelper.singleton().authorization?.userEmail, EmailHelper.singleton().authorization?.canAuthorize() ?? false else {
print("Google authetication failed")
self.isGoogleAuth = false
return
}
let mailAccount = MailAccount(emailAddress: userEmail.lowercased(), password: "", username: userEmail.lowercased(), imapServer: "imap.gmail.com", imapPort: 993, imapEncryption: 2, smtpServer: "smtp.gmail.com", smtpPort: 587, smtpEncryption: 1, authType: MCOAuthType.xoAuth2.rawValue)
self.checkConfig(mailAccount: mailAccount)
self.cancellable = self.model.checkConfig(mailAccount: mailAccount, extendedValidation: false).sink { promise in
switch promise {
case AuthenticationModel.AuthenticationResult.Success :
self.authenticationSucceed()
case AuthenticationModel.AuthenticationResult.Error :
self.authenticationFailed(error: MailServerConnectionError.AuthenticationError)
case AuthenticationModel.AuthenticationResult.Timeout :
self.timeoutNotification(result: promise)
}
}
})
}
private func setupIMAPSession(mailAccount: MailAccount, callback: AuthenticationCallback) -> MailSession {
let mailSession = MailSession(configSession: SessionType.IMAP, mailAddress: mailAccount.emailAddress, password: mailAccount.password, username: mailAccount.username)
if (isGoogleAuth || isDetailedAuthentication) {
let imapConnValue = mailAccount.imapEncryption
mailSession.setServer(hostname: mailAccount.imapServer, port: UInt32(mailAccount.imapPort), connType: imapConnValue, authType: mailAccount.authType)
}
let listenerIMAP = Listener(callback: callback, mailAccount: mailAccount)
mailSession.addListener(listener: listenerIMAP)
return mailSession
}
private func setupSMTPSession(mailAccount: MailAccount, callback: AuthenticationCallback) -> MailSession {
let mailSession = MailSession(configSession: SessionType.SMTP, mailAddress: mailAccount.emailAddress, password: mailAccount.password, username: mailAccount.username)
if (isGoogleAuth || isDetailedAuthentication) {
let smtpConnValue = mailAccount.smtpEncryption
mailSession.setServer(hostname: mailAccount.smtpServer, port: UInt32(mailAccount.smtpPort), connType: smtpConnValue, authType: mailAccount.authType)
}
let listenerSMTP = Listener(callback: callback, mailAccount: mailAccount)
mailSession.addListener(listener: listenerSMTP)
return mailSession
func authenticationSucceed() {
AppDelegate.getAppDelegate().credentialsWork()
}
func authenticationFailed() {
var error = MailServerConnectionError.AuthenticationError
if let smtp = currentIMAP, let e = MailServerConnectionError.findPrioError(errors: smtp.errors) {
error = e
}
if let imap = currentIMAP, let e = MailServerConnectionError.findPrioError(errors: imap.errors) {
error = e
}
func authenticationFailed(error: MailServerConnectionError) {
errorMessage = NSLocalizedString(error.localizedUIBodyString, comment: "")
}
func timeoutNotification() {
func timeoutNotification(result: AuthenticationModel.AuthenticationResult) {
print("promise arrived")
errorMessage = NSLocalizedString(MailServerConnectionError.TimeoutError.localizedUIBodyString, comment: "")
}
func onTimeout(timeoutResult: DispatchTimeoutResult) {
imapCallback?.callback = nil
smtpCallback?.callback = nil
if timeoutResult == .success {
if imapConfigurationSuccessful && smtpConfigurationSuccessful {
AppDelegate.getAppDelegate().credentialsWork()
} else {
authenticationFailed()
}
} else {
timeoutNotification()
}
}
func onImapCompleted(imapWorks: Bool, _ login: String, _ password: String) {
if imapWorks {
_ = currentIMAP?.storeToUserDefaults()
imapConfigurationSuccessful = true
} else {
imapConfigurationSuccessful = false
}
dispatchGroup.leave()
}
func onSmtpCompleted(smtpWorks: Bool, _ login: String, _ password: String) {
if smtpWorks {
_ = currentSMTP?.storeToUserDefaults()
smtpConfigurationSuccessful = true
} else {
smtpConfigurationSuccessful = false
}
dispatchGroup.leave()
return
}
class Listener: MailSessionListener {
let callback: AuthenticationCallback
let mailAccount: MailAccount
init(callback: AuthenticationCallback, mailAccount: MailAccount) {
self.callback = callback
self.mailAccount = mailAccount
}
func testFinish(result: Bool) {
callback.onResult(worked: result, login: mailAccount.emailAddress, password: mailAccount.password)
}
}
}
class AuthenticationCallback {
var callback: ((Bool, String, String) -> Void)?
init(callback: @escaping (Bool, String, String) -> Void) {
self.callback = callback
}
func onResult(worked: Bool, login: String, password: String) {
if let callback = callback {
callback(worked, login, password)
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment