patbef-iOS/Befund/Controllers/LoginController.swift

533 lines
22 KiB
Swift
Raw Normal View History

2024-01-29 16:20:42 +01:00
//
// LoginController.swift
// Befund
//
// Created by Artur Savitskiy on 27.07.22.
// Copyright © 2022 MVZ Dr. Stein und Kollegen. All rights reserved.
//
import Foundation
import LocalAuthentication
import UIKit
class LoginController: UIViewController, UITextFieldDelegate, UIAlertViewDelegate
{
2024-03-04 17:16:47 +01:00
private var Settings: Core.Models.Settings? = nil
2024-01-29 16:20:42 +01:00
@IBOutlet weak var loginPassword_PassworArea: UIView!
@IBOutlet weak var loginPassword_LaborLogoArea: UIView!
@IBOutlet weak var loginPasswordView_LaborLogo: UIImageView!
@IBOutlet weak var loginPasswordArea_InputArea: UIView!
@IBOutlet weak var loginPasswordView_txtPassword: UITextField!
@IBOutlet weak var loginPasswordArea_StatusArea: UIView!
@IBOutlet weak var loginPasswordView_Status: UILabel!
//MARK: *** PopupNewPGS - Buttons
@IBOutlet weak var loginPasswordArea_ButtonsArea: UIView!
@IBOutlet weak var loginPasswordView_BtnLogin: UIButton!
@IBOutlet weak var loginPasswordView_BtnReset: UIButton!
@IBOutlet weak var loginPasswordView_BtnSupport: UIButton!
@IBOutlet weak var passwordReset_Overlay: UIView!
@IBOutlet weak var passwordReset: UIView!
//MARK: *** passwordReset - PGS
@IBOutlet weak var passwordReset_Loading: UIActivityIndicatorView!
@IBOutlet weak var passwordReset_Status: UILabel!
//MARK: *** passwordReset - MasterPassword
@IBOutlet weak var passwordReset_MasterPassword: UIView!
@IBOutlet weak var passwordReset_PINView: UIView!
@IBOutlet weak var passwordReset_PINVNewView: UIView!
@IBOutlet weak var passwordReset_PasswordConfirmView: UIView!
@IBOutlet weak var passwordReset_PasswordView: UIView!
@IBOutlet weak var passwordReset_txtPassword: UITextField!
@IBOutlet weak var passwordReset_txtPasswordConfirm: UITextField!
@IBOutlet weak var passwordReset_txtPin: UITextField!
@IBOutlet weak var passwordReset_txtPinNew: UITextField!
@IBOutlet weak var passwordReset_lblPwdHinweis: UILabel!
//MARK: *** passwordReset - Buttons
@IBOutlet weak var passwordReset_BtnChange: UIButton!
@IBOutlet weak var passwordReset_BtnPinRequest: UIButton!
@IBOutlet weak var passwordReset_BtnCancel: UIButton!
override func viewDidLoad()
{
super.viewDidLoad()
self.Initialize()
}
override func loadView() {
super.loadView()
}
override func viewDidAppear(_ animated: Bool)
{
super.viewDidAppear(animated)
self.SetLabels()
}
/**
* Initializes view
*/
private func Initialize()
{
2024-03-04 17:16:47 +01:00
Settings = Core.Models.Settings.loadFromFile(atPath: Core.System.SettingsPath())
2024-01-29 16:20:42 +01:00
self.loginPasswordView_txtPassword.delegate = self
self.loginPasswordView_txtPassword.enablePasswordToggle()
self.passwordReset_Overlay.frame = self.view.frame
self.view.addSubview(self.passwordReset_Overlay)
self.passwordReset_txtPin.delegate = self
self.passwordReset_txtPin.returnKeyType = .next
self.passwordReset_txtPinNew.delegate = self
self.passwordReset_txtPinNew.returnKeyType = .next
self.passwordReset_txtPassword.delegate = self
self.passwordReset_txtPassword.enablePasswordToggle()
self.passwordReset_txtPassword.returnKeyType = .next
self.passwordReset_txtPasswordConfirm.delegate = self
self.passwordReset_txtPasswordConfirm.enablePasswordToggle()
self.passwordReset_txtPasswordConfirm.returnKeyType = .done
self.passwordReset_Overlay.isHidden = true
self.passwordReset_lblPwdHinweis.text = ""
self.passwordReset_lblPwdHinweis.isHidden = false
self.passwordReset_txtPassword.addTarget(self, action: #selector(self.ShowPWDHinweis), for: .editingDidBegin)
self.passwordReset_txtPassword.addTarget(self, action: #selector(self.HidePWDHinweis), for: .editingDidEnd)
self.passwordReset_txtPasswordConfirm.addTarget(self, action: #selector(self.ShowPWDHinweis), for: .editingDidBegin)
self.passwordReset_txtPasswordConfirm.addTarget(self, action: #selector(self.HidePWDHinweis), for: .editingDidEnd)
self.passwordReset_txtPinNew.addTarget(self, action: #selector(self.ShowPinHinweis), for: .editingDidBegin)
self.passwordReset_txtPinNew.addTarget(self, action: #selector(self.HidePWDHinweis), for: .editingDidEnd)
self.addDoneToolbar([self.passwordReset_txtPin, self.passwordReset_txtPinNew, self.passwordReset_txtPassword, self.passwordReset_txtPasswordConfirm])
self.hideKeyboardWhenTappedAround()
}
2024-03-04 17:16:47 +01:00
private func GetHost()-> Core.Https.Servers
{
return (Settings!.labor?.host ?? .DEVELOPMENT)
}
2024-01-29 16:20:42 +01:00
@objc func ShowPinHinweis()
{
self.passwordReset_Status.text = ""
self.passwordReset_lblPwdHinweis.text = Core.Lang.Get(key: "ERROR_PIN_LENGTH")
self.passwordReset_lblPwdHinweis.isHidden = false
}
@objc func ShowPWDHinweis()
{
self.passwordReset_Status.text = ""
self.passwordReset_lblPwdHinweis.text = Core.Lang.Get(key: "ERROR_ENTER_STRONG_PASSWORD")
self.passwordReset_lblPwdHinweis.isHidden = false
}
@objc func HidePWDHinweis()
{
self.passwordReset_lblPwdHinweis.text = ""
self.passwordReset_lblPwdHinweis.isHidden = true
}
private func SetLabels()
{
self.loginPasswordView_txtPassword.placeholder = Core.Lang.Get(key: "LBL_PASSWORD")
self.loginPasswordView_BtnReset.setTitle(Core.Lang.Get(key: "BTN_FORGOT"), for: .normal)
self.loginPasswordView_BtnLogin.setTitle(Core.Lang.Get(key: "BTN_LOGIN"), for: .normal)
self.loginPasswordView_BtnSupport.setTitle(Core.Lang.Get(key: "BTN_SUPPORT"), for: .normal)
self.passwordReset_BtnCancel.setTitle(Core.Lang.Get(key: "BTN_CANCEL"), for: .normal)
self.passwordReset_BtnChange.setTitle(Core.Lang.Get(key: "BTN_CHANGE"), for: .normal)
self.passwordReset_BtnPinRequest.setTitle(Core.Lang.Get(key: "BTN_PIN_REQUEST"), for: .normal)
self.passwordReset_txtPin.placeholder = Core.Lang.Get(key: "LBL_PIN")
self.passwordReset_txtPinNew.placeholder = Core.Lang.Get(key: "LBL_NEW_PIN")
self.passwordReset_txtPassword.text = Core.Lang.Get(key: "LBL_NEW_PASSWORD")
self.passwordReset_txtPasswordConfirm.text = Core.Lang.Get(key: "LBL_PASSWORD_CONFIRM")
2024-03-04 17:16:47 +01:00
self.loginPasswordView_LaborLogo.image = Settings?.labor?.logo
2024-01-29 16:20:42 +01:00
}
internal func textFieldShouldReturn(_ textField: UITextField) -> Bool {
self.switchBasedNextTextField(textField)
return true
}
private func switchBasedNextTextField(_ textField: UITextField) {
switch textField {
case self.passwordReset_txtPin:
self.passwordReset_txtPinNew.becomeFirstResponder()
case self.passwordReset_txtPinNew:
self.passwordReset_txtPassword.becomeFirstResponder()
case self.passwordReset_txtPassword:
self.passwordReset_txtPasswordConfirm.becomeFirstResponder()
default:
self.view.endEditing(true)
self.LoginPasswordView_BtnLoginClick("")
}
}
@IBAction func ButtonSupportOpenClick(_ sender: Any)
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let settingsSupportController = storyboard.instantiateViewController(identifier: "SupportController")
settingsSupportController.modalPresentationStyle = .fullScreen
let transition = CATransition()
transition.duration = 0.25
transition.type = .push
transition.subtype = .fromRight
self.view.window!.layer.add(transition, forKey: kCATransition)
self.present(settingsSupportController, animated: false)
}
@IBAction func LoginPasswordView_BtnLoginClick(_ sender: Any)
{
self.loginPasswordView_Status.text = ""
if(self.loginPasswordView_txtPassword.text?.isEmpty ?? true)
{
self.loginPasswordView_Status.text=Core.Lang.Get(key: "ERROR_INVALID_PASSWORD")
}
else
{
let password = self.loginPasswordView_txtPassword.text ?? ""
2024-03-04 17:16:47 +01:00
let encrypted_pwd = Core.Security.AES.Decrypt(value: Settings?.hashed_private_key ?? "", password: String( decoding:Core.Security.AES.GetKey(password: password), as: UTF8.self))
2024-01-29 16:20:42 +01:00
if(encrypted_pwd==nil)
{
self.loginPasswordView_Status.text=Core.Lang.Get(key: "ERROR_INVALID_PASSWORD")
}
else
{
AppDelegate.Session.DevicePassword = password
self.view.endEditing(true)
self.dismiss(animated: true, completion: nil)
}
}
}
private func PasswordActivity_HideLoading(message: String)
{
self.passwordReset_Loading.isHidden = true
if(!message.isEmpty)
{
self.passwordReset_Status.isHidden = false
self.passwordReset_Status.text = message
}
}
@IBAction func LoginPasswordView_BtnResetClick(_ sender: Any)
{
self.passwordReset_txtPin.text = ""
self.passwordReset_txtPinNew.text = ""
self.passwordReset_txtPassword.text = ""
self.passwordReset_txtPasswordConfirm.text = ""
self.passwordReset_Status.text = ""
self.passwordReset_Loading.isHidden = true
self.passwordReset_Loading.stopAnimating()
self.passwordReset.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
self.passwordReset_Overlay.alpha = 0.0
self.passwordReset_Overlay.isHidden = false
UIView.animate(withDuration: 0.24) {
self.passwordReset.transform = CGAffineTransform.identity
self.passwordReset_Overlay.alpha = 70.0
}
}
@IBAction func PasswordReset_BtnCancelClick(_ sender: Any)
{
self.HidePasswordResetPopup()
}
private func HidePasswordResetPopup()
{
self.view.endEditing(true)
UIView.animate(withDuration: 0.24, animations: {
self.passwordReset_Overlay.alpha = 0.0
self.passwordReset.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
}) {_ in
self.passwordReset_Overlay.isHidden = true
}
}
@IBAction func PasswordReset_BtnChangeClick(_ sender: Any)
{
self.ChangePassword()
}
func ChangePassword()
{
self.ResetPassword_ShowLoading()
let pin = (self.passwordReset_txtPin.text ?? "")
let pinNew = (self.passwordReset_txtPinNew.text ?? "")
if(pin.count == 0)
{
self.ResetPassword_HideLoading( message: Core.Lang.Get(key: "ERROR_ENTER_YOUR_PIN"))
}
else if(pin.count != 5)
{
self.ResetPassword_HideLoading( message: Core.Lang.Get(key: "ERROR_PIN_LENGTH"))
}
else if(pinNew.count == 0)
{
self.ResetPassword_HideLoading( message: Core.Lang.Get(key: "ERROR_ENTER_PIN"))
}
else if(pinNew.count != 5)
{
self.ResetPassword_HideLoading( message: Core.Lang.Get(key: "ERROR_PIN_LENGTH"))
}
else
{
2024-03-04 17:16:47 +01:00
let oldPasswordDecrypted = Core.Models.Request.ChangeVerificatorHashProvider.GetDecryptedOldPasswordByPin(settings: Settings!, pin: pin)
2024-01-29 16:20:42 +01:00
if(oldPasswordDecrypted == nil)
{
self.ResetPassword_HideLoading( message: Core.Lang.Get(key: "ERROR_INVALID_PASSWORD_RESET_PIN"))
}
else
{
let newPass = self.passwordReset_txtPassword?.text ?? ""
let confirmPass = self.passwordReset_txtPasswordConfirm?.text ?? ""
if(!Core.Models.Request.ChangeVerificatorHashProvider.IsPasswordStrong(password: newPass))
{
self.ResetPassword_HideLoading(message: Core.Lang.Get(key: "ERROR_ENTER_STRONG_PASSWORD"))
}
else if(newPass != confirmPass)
{
self.ResetPassword_HideLoading(message: Core.Lang.Get(key: "ERROR_CONFIRM_PASSWORD"))
}
else
{
2024-03-04 17:16:47 +01:00
let changeVerificatorHash = Core.Models.Request.ChangeVerificatorHashProvider.PrepareChangeVerificatorHash(settings: Settings!, oldPassword: oldPasswordDecrypted!, newPassword: newPass, newPin: pinNew)
2024-01-29 16:20:42 +01:00
let requestKeyPair = Core.Security.Curve25519.GenerateKeyPair()
2024-03-04 17:16:47 +01:00
Core.Https.Request.KeyExchangeAsync(host: GetHost(), keyPair: requestKeyPair,
2024-01-29 16:20:42 +01:00
onSuccess: {
publicKey in
let sharedKey = requestKeyPair.GetSharedKey(peerPublicKeyBase64: publicKey.key)
let encryptedRequest = Core.Models.Request.EncryptedRequest(descriptor: "ChangeVerificatorHash", contentObject: changeVerificatorHash, requestType: .REQUEST_VERIFICATOR_HASH, key: sharedKey!)
2024-03-04 17:16:47 +01:00
Core.Https.Request.EncryptedRequestAsync(host: self.GetHost(), controller: "results", action: "update_verificator_hash", request: encryptedRequest, serverPublicKey: publicKey, keyPair: requestKeyPair, onSuccess: {
2024-01-29 16:20:42 +01:00
encryptedResponse in
encryptedResponse.Decrypt(key: sharedKey!)
DispatchQueue.main.async
{
if(encryptedResponse.descriptor!.lowercased() == "success")
{
var errMsg: String? = nil
2024-03-04 17:16:47 +01:00
self.Settings = Core.Models.Request.ChangeVerificatorHashProvider.SaveChangedVerificatorBySuccess(settings: self.Settings!, oldPassword: oldPasswordDecrypted!, newPassword: newPass, pin: (changeVerificatorHash.pin ?? ""), errorMsg: &errMsg)
2024-01-29 16:20:42 +01:00
if(errMsg != nil)
{
self.PasswordActivity_HideLoading(message: errMsg!)
Core.Log.Critical(msg: "Could not save settings to the file", namespace: "SettingsController", method: "PasswordChange")
}
else
{
self.PasswordActivity_HideLoading(message: "")
DispatchQueue.main.asyncAfter(deadline: .now() + 1)
{
self.HidePasswordResetPopup()
self.ShowMessagePopup(title: "", message: Core.Lang.Get(key: "MSG_PASSWORD_HAS_BEEN_CHANGED").replacingOccurrences(of: "[PIN]", with: changeVerificatorHash.pin!))
}
}
}
else
{
self.PasswordActivity_HideLoading(message: encryptedResponse.descriptor!)
}
}
},
onError: {
error in
DispatchQueue.main.async
{
self.PasswordActivity_HideLoading(message: error)
Core.Log.Critical(msg: "Server not reachable", namespace: "LoginController", method: "ChangePassword")
}
})
},
onError: {
error in
DispatchQueue.main.async
{
self.PasswordActivity_HideLoading(message: error)
Core.Log.Critical(msg: "Server not reachable", namespace: "LoginController", method: "ChangePassword")
}
})
}
}
}
}
@IBAction func PasswordReset_BtnRequestPinClick(_ sender: Any)
{
self.RequestPIN()
}
func RequestPIN()
{
var authError: NSError?
let localAuthContext = LAContext()
if (localAuthContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &authError)) {
localAuthContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: Core.Lang.Get(key: "MSG_TOUCHID_INFO")) { success, evaluateError in
if success {
DispatchQueue.main.async
{
self.ResetPassword_ShowLoading()
}
self.RequestPINFromServer()
} else {
//TODO: User did not authenticate successfully, look at error and take appropriate action
guard let error = evaluateError else {
return
}
DispatchQueue.main.async
{
self.ResetPassword_HideLoading(message: error.localizedDescription)
}
}
}
} else {
guard let error = authError else {
return
}
DispatchQueue.main.async
{
self.ResetPassword_HideLoading(message: Core.Lang.Get(key: "ERROR_BIOMETRICAL_AUTHENTICATION_COULD_NOT_ACTIVATED"))
}
}
}
func ResetPassword_ShowLoading()
{
self.passwordReset_Status.text = ""
self.passwordReset_Status.isHidden = true
self.passwordReset_Loading.isHidden = false
self.passwordReset_Loading.startAnimating()
}
func ResetPassword_HideLoading(message: String)
{
self.passwordReset_Loading.isHidden = true
if(!message.isEmpty)
{
self.passwordReset_Status.text = message
self.passwordReset_Status.isHidden = false
}
}
func RequestPINFromServer()
{
let requestKeyPair = Core.Security.Curve25519.GenerateKeyPair()
2024-03-04 17:16:47 +01:00
Core.Https.Request.KeyExchangeAsync(host: GetHost(), keyPair: requestKeyPair,
2024-01-29 16:20:42 +01:00
onSuccess: {
publicKey in
let getPIN = Core.Models.Request.GetPIN()
2024-03-04 17:16:47 +01:00
getPIN.udid = self.Settings!.udid;
getPIN.verificator_hash = self.Settings!.verificator_hash;
2024-01-29 16:20:42 +01:00
let sharedKey = requestKeyPair.GetSharedKey(peerPublicKeyBase64: publicKey.key)
let encryptedRequest = Core.Models.Request.EncryptedRequest(descriptor: "PIN", contentObject: getPIN, requestType: .REQUEST_GET_PIN, key: sharedKey!)
2024-03-04 17:16:47 +01:00
Core.Https.Request.EncryptedRequestAsync(host: self.GetHost(), controller: "results", action: "pin", request: encryptedRequest, serverPublicKey: publicKey, keyPair: requestKeyPair, onSuccess: {
2024-01-29 16:20:42 +01:00
encryptedResponse in
let response = encryptedResponse.Decrypt(key: sharedKey!)
DispatchQueue.main.async
{
if(response != nil)
{
let pin = response as? Core.Models.Response.PIN;
if(pin != nil && pin!.code!.count > 0)
{
self.ResetPassword_HideLoading(message: Core.Lang.Get(key: "PIN_HAS_BEEN_REQUESTED"))
self.passwordReset_txtPin.text = pin!.code
}
else
{
let exception = response as? Core.Models.Response.Exception
if(exception != nil)
{
self.ResetPassword_HideLoading(message: Core.Lang.Get(key: "ERROR_COULD_NOT_GET_PIN") + ": " + (exception!.message ?? ""))
}
else
{
self.ResetPassword_HideLoading(message: Core.Lang.Get(key: "ERROR_COULD_NOT_GET_PIN"))
Core.Log.Error(msg: "Could not get pin from the server", namespace: "LoginActivity", method: "RequestPINFromServer")
}
}
}
else
{
self.ResetPassword_HideLoading(message: Core.Lang.Get(key: "ERROR_COULD_NOT_GET_PIN"))
Core.Log.Error(msg: "Could not get pin from the server", namespace: "LoginActivity", method: "RequestPINFromServer")
}
}
},
onError: {
error in
DispatchQueue.main.async
{
self.ResetPassword_HideLoading(message: Core.Lang.Get(key: "ERROR_COULD_NOT_GET_PIN"));
Core.Log.Error(msg: error, namespace: "LoginActivity", method: "RequestPINFromServer");
}
})
},
onError: {
error in
DispatchQueue.main.async
{
self.ResetPassword_HideLoading(message: Core.Lang.Get(key: "ERROR_COULD_NOT_GET_PIN"));
Core.Log.Error(msg: error, namespace: "LoginActivity", method: "RequestPINFromServer");
}
})
}
}