Skip to content
Snippets Groups Projects
Commit 16774fd2 authored by Bruno Muniz's avatar Bruno Muniz :apple:
Browse files

Refactor sftp service

parent 9f3daf5b
No related branches found
No related tags found
2 merge requests!54Releasing 1.1.4,!42Adding SFTP as a service to backup/restore
......@@ -216,7 +216,7 @@ extension BackupService {
}
if sftpService.isAuthorized() {
sftpService.fetchMetadata({ [weak settings] in
sftpService.fetchMetadata { [weak settings] in
guard let settings = settings else { return }
guard let metadata = try? $0.get()?.backup else {
......@@ -229,7 +229,7 @@ extension BackupService {
date: metadata.date,
size: metadata.size
)
})
}
}
if dropboxService.isAuthorized() {
......
import Shout
import Socket
import Keychain
import Foundation
import DependencyInjection
public struct SFTPAuthenticator {
public var authenticate: (String, String, String) throws -> Void
public func callAsFunction(host: String, username: String, password: String) throws {
try authenticate(host, username, password)
}
}
extension SFTPAuthenticator {
static let mock = SFTPAuthenticator { host, username, password in
print("^^^ Requested authentication on sftp service.")
print("^^^ Host: \(host)")
print("^^^ Username: \(username)")
print("^^^ Password: \(password)")
}
static let live = SFTPAuthenticator { host, username, password in
do {
try SSH.connect(
host: host,
port: 22,
username: username,
authMethod: SSHPassword(password)) { ssh in
_ = try ssh.openSftp()
let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling
try keychain.store(key: .host, value: host)
try keychain.store(key: .pwd, value: password)
try keychain.store(key: .username, value: username)
}
} catch {
if let error = error as? SSHError {
print(error.kind)
print(error.message)
print(error.description)
} else if let error = error as? Socket.Error {
print(error.errorCode)
print(error.description)
print(error.errorReason)
print(error.localizedDescription)
} else {
print(error.localizedDescription)
}
throw error
}
}
}
......@@ -4,7 +4,9 @@ import Keychain
import Foundation
import DependencyInjection
public struct SFTPServiceBackupDownloader {
public typealias SFTPDownloadResult = (Result<Data, Error>) -> Void
public struct SFTPDownloader {
public var download: (String, @escaping SFTPDownloadResult) -> Void
public func callAsFunction(path: String, completion: @escaping SFTPDownloadResult) {
......@@ -12,13 +14,13 @@ public struct SFTPServiceBackupDownloader {
}
}
extension SFTPServiceBackupDownloader {
static let mock = SFTPServiceBackupDownloader { path, _ in
extension SFTPDownloader {
static let mock = SFTPDownloader { path, _ in
print("^^^ Requested backup download on sftp service.")
print("^^^ Path: \(path)")
}
static let live = SFTPServiceBackupDownloader { path, completion in
static let live = SFTPDownloader { path, completion in
DispatchQueue.global().async {
do {
let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling
......
import Shout
import Socket
import Models
import Keychain
import Foundation
import DependencyInjection
public typealias SFTPFetchResult = (Result<RestoreSettings?, Error>) -> Void
public struct SFTPFetcher {
public var fetch: (@escaping SFTPFetchResult) -> Void
public func callAsFunction(completion: @escaping SFTPFetchResult) {
fetch(completion)
}
}
extension SFTPFetcher {
static let mock = SFTPFetcher { _ in
print("^^^ Requested backup metadata on sftp service.")
}
static let live = SFTPFetcher { completion in
DispatchQueue.global().async {
do {
let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling
let host = try keychain.get(key: .host)
let password = try keychain.get(key: .pwd)
let username = try keychain.get(key: .username)
let ssh = try SSH(host: host!, port: 22)
try ssh.authenticate(username: username!, password: password!)
let sftp = try ssh.openSftp()
if let files = try? sftp.listFiles(in: "backup"),
let backup = files.filter({ file in file.0 == "backup.xxm" }).first {
completion(.success(.init(
backup: .init(
id: "backup/backup.xxm",
date: backup.value.lastModified,
size: Float(backup.value.size)
),
cloudService: .sftp
)))
return
}
completion(.success(nil))
} catch {
if let error = error as? SSHError {
print(error.kind)
print(error.message)
print(error.description)
} else if let error = error as? Socket.Error {
print(error.errorCode)
print(error.description)
print(error.errorReason)
print(error.localizedDescription)
} else {
print(error.localizedDescription)
}
completion(.failure(error))
}
}
}
}
import Shout
import Socket
import Models
import Keychain
import Foundation
import DependencyInjection
public struct SFTPServiceBackupUploader {
public typealias SFTPUploadResult = (Result<Backup, Error>) -> Void
public struct SFTPUploader {
public var upload: (URL, @escaping SFTPUploadResult) -> Void
public func callAsFunction(url: URL, completion: @escaping SFTPUploadResult) {
......@@ -12,15 +15,15 @@ public struct SFTPServiceBackupUploader {
}
}
extension SFTPServiceBackupUploader {
static let mock = SFTPServiceBackupUploader(
extension SFTPUploader {
static let mock = SFTPUploader(
upload: { url, _ in
print("^^^ Requested upload on sftp service")
print("^^^ URL path: \(url.path)")
}
)
static let live = SFTPServiceBackupUploader { url, completion in
static let live = SFTPUploader { url, completion in
DispatchQueue.global().async {
do {
let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling
......
import UIKit
import Shout
import Socket
import Models
import Combine
import Keychain
import Foundation
import Presentation
import DependencyInjection
public typealias SFTPDownloadResult = (Result<Data, Error>) -> Void
public typealias SFTPUploadResult = (Result<Backup, Error>) -> Void
public typealias SFTPFetchResult = (Result<RestoreSettings?, Error>) -> Void
public typealias SFTPAuthorizationParams = (UIViewController, () -> Void)
public struct SFTPService {
public var isAuthorized: () -> Bool
public var fetchMetadata: (@escaping SFTPFetchResult) -> Void
public var uploadBackup: SFTPServiceBackupUploader
public var fetchMetadata: SFTPFetcher
public var uploadBackup: SFTPUploader
public var authorizeFlow: (SFTPAuthorizationParams) -> Void
public var authenticate: (String, String, String) throws -> Void
public var downloadBackup: SFTPServiceBackupDownloader
public var authenticate: SFTPAuthenticator
public var downloadBackup: SFTPDownloader
}
public extension SFTPService {
static var mock = SFTPService(
isAuthorized: {
print("^^^ Requested auth status on sftp service")
return true
},
fetchMetadata: { completion in
print("^^^ Requested backup metadata on sftp service.")
completion(.success(nil))
},
isAuthorized: { true },
fetchMetadata: .mock,
uploadBackup: .mock,
authorizeFlow: { (_, completion) in
print("^^^ Requested authorizing flow on sftp service.")
completion()
},
authenticate: { host, username, password in
print("^^^ Requested authentication on sftp service.")
print("^^^ Host: \(host)")
print("^^^ Username: \(username)")
print("^^^ Password: \(password)")
},
authorizeFlow: { (_, completion) in completion() },
authenticate: .mock,
downloadBackup: .mock
)
......@@ -57,87 +35,13 @@ public extension SFTPService {
return false
},
fetchMetadata: { completion in
DispatchQueue.global().async {
do {
let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling
let host = try keychain.get(key: .host)
let password = try keychain.get(key: .pwd)
let username = try keychain.get(key: .username)
let ssh = try SSH(host: host!, port: 22)
try ssh.authenticate(username: username!, password: password!)
let sftp = try ssh.openSftp()
if let files = try? sftp.listFiles(in: "backup"),
let backup = files.filter({ file in file.0 == "backup.xxm" }).first {
completion(.success(.init(
backup: .init(
id: "backup/backup.xxm",
date: backup.value.lastModified,
size: Float(backup.value.size)
),
cloudService: .sftp
)))
return
}
completion(.success(nil))
} catch {
if let error = error as? SSHError {
print(error.kind)
print(error.message)
print(error.description)
} else if let error = error as? Socket.Error {
print(error.errorCode)
print(error.description)
print(error.errorReason)
print(error.localizedDescription)
} else {
print(error.localizedDescription)
}
completion(.failure(error))
}
}
},
fetchMetadata: .live,
uploadBackup: .live ,
authorizeFlow: { controller, completion in
var pushPresenter: Presenting = PushPresenter()
pushPresenter.present(SFTPController(completion), from: controller)
},
authenticate: { host, username, password in
do {
try SSH.connect(
host: host,
port: 22,
username: username,
authMethod: SSHPassword(password)) { ssh in
_ = try ssh.openSftp()
let keychain = try DependencyInjection.Container.shared.resolve() as KeychainHandling
try keychain.store(key: .host, value: host)
try keychain.store(key: .pwd, value: password)
try keychain.store(key: .username, value: username)
}
} catch {
if let error = error as? SSHError {
print(error.kind)
print(error.message)
print(error.description)
} else if let error = error as? Socket.Error {
print(error.errorCode)
print(error.description)
print(error.errorReason)
print(error.localizedDescription)
} else {
print(error.localizedDescription)
}
throw error
}
},
authenticate: .live,
downloadBackup: .live
)
}
......@@ -54,7 +54,12 @@ final class SFTPViewModel {
DispatchQueue.global().async { [weak self] in
guard let self = self else { return }
do {
try self.service.authenticate(host, username, password)
try self.service.authenticate(
host: host,
username: username,
password: password
)
self.hudSubject.send(.none)
self.authSubject.send(())
} catch {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment