Support folder operations

Example:
  - add: a/b/c/d
  - delete: a/b/c/d
  - move: a/b/c/d -> a/b/c/d/e
This commit is contained in:
Bob Sun 2017-04-23 10:03:09 -07:00
parent b1ef8a343f
commit d8ecd1e889
No known key found for this signature in database
GPG key ID: 1F86BA2052FED3B4
8 changed files with 213 additions and 141 deletions

View file

@ -42,14 +42,6 @@ class AddPasswordTableViewController: PasswordEditorTableViewController {
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
return false
}
// check "/"
guard nameCell.getContent()!.contains("/") == false else {
let alertTitle = "Cannot Add Password"
let alertMessage = "Illegal character."
Utils.alert(title: alertTitle, message: alertMessage, controller: self, completion: nil)
return false
}
}
return true
}
@ -73,7 +65,9 @@ class AddPasswordTableViewController: PasswordEditorTableViewController {
} else {
plainText = "\(cellContents["password"]!)"
}
password = Password(name: cellContents["name"]!, plainText: plainText)
let name = URL(string: cellContents["name"]!)!.lastPathComponent
let url = URL(string: cellContents["name"]!)!.appendingPathExtension("gpg")
password = Password(name: name, url: url, plainText: plainText)
}
}
}

View file

@ -11,7 +11,7 @@ import UIKit
class EditPasswordTableViewController: PasswordEditorTableViewController {
override func viewDidLoad() {
tableData = [
[[.type: PasswordEditorCellType.textFieldCell, .title: "name", .content: password!.name]],
[[.type: PasswordEditorCellType.textFieldCell, .title: "name", .content: password!.namePath]],
[[.type: PasswordEditorCellType.fillPasswordCell, .title: "password", .content: password!.password],
[.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"]],
[[.type: PasswordEditorCellType.textViewCell, .title: "additions", .content: password!.getAdditionsPlainText()]],
@ -23,15 +23,8 @@ class EditPasswordTableViewController: PasswordEditorTableViewController {
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "saveEditPasswordSegue" {
if let nameCell = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? ContentTableViewCell {
if nameCell.getContent() != password?.name {
let alertTitle = "Cannot Save Edit"
let alertMessage = "Editing name is not supported."
Utils.alert(title: alertTitle, message: alertMessage, controller: self) {
nameCell.setContent(content: self.password!.name)
}
return false
}
if let _ = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? ContentTableViewCell {
// TODO: do some checks here
}
}
return true
@ -54,9 +47,13 @@ class EditPasswordTableViewController: PasswordEditorTableViewController {
if cellContents["additions"]! != "" {
plainText = "\(cellContents["password"]!)\n\(cellContents["additions"]!)"
} else {
plainText = "\(cellContents["password"]!)"
plainText = "\(cellContents["password"]!)\n"
}
let name = URL(string: cellContents["name"]!)!.lastPathComponent
let url = URL(string: cellContents["name"]!)!.appendingPathExtension("gpg")
if password!.plainText != plainText || password!.url!.path != url.path {
password!.updatePassword(name: name, url: url, plainText: plainText)
}
password!.updatePassword(name: cellContents["name"]!, plainText: plainText)
}
}

View file

@ -30,7 +30,7 @@ class OTPScannerController: QRScannerController {
if accept == true {
captureSession?.stopRunning()
scannedOTP = scannedString
tempPassword = Password(name: "empty", plainText: scannedString)
tempPassword = Password(name: "empty", url: nil, plainText: scannedString)
// set scannerOutput
setupOneTimePasswordMessage()
} else {

View file

@ -14,7 +14,6 @@ import SVProgressHUD
class PasswordDetailTableViewController: UITableViewController, UIGestureRecognizerDelegate {
var passwordEntity: PasswordEntity?
private var password: Password?
private var passwordCategoryText = ""
private var passwordImage: UIImage?
private var oneTimePasswordIndexPath : IndexPath?
private var shouldPopCurrentView = false
@ -77,7 +76,6 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
tableView.register(UINib(nibName: "LabelTableViewCell", bundle: nil), forCellReuseIdentifier: "labelCell")
tableView.register(UINib(nibName: "PasswordDetailTitleTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordDetailTitleTableViewCell")
passwordCategoryText = passwordEntity!.getCategoryText()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(PasswordDetailTableViewController.tapMenu(recognizer:)))
tapGesture.cancelsTouchesInView = false
tableView.addGestureRecognizer(tapGesture)
@ -96,24 +94,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
let image = UIImage(data: imageData as Data)
passwordImage = image
}
var passphrase = ""
if Defaults[.isRememberPassphraseOn] && self.passwordStore.pgpKeyPassphrase != nil {
passphrase = self.passwordStore.pgpKeyPassphrase!
self.decryptThenShowPassword(passphrase: passphrase)
} else {
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
passphrase = alert.textFields!.first!.text!
self.decryptThenShowPassword(passphrase: passphrase)
}))
alert.addTextField(configurationHandler: {(textField: UITextField!) in
textField.text = ""
textField.isSecureTextEntry = true
})
self.present(alert, animated: true, completion: nil)
}
self.decryptThenShowPassword()
self.setupOneTimePasswordAutoRefresh()
// pop the current view because this password might be "discarded"
@ -137,14 +118,33 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
}
private func decryptThenShowPassword(passphrase: String) {
private func requestPGPKeyPassphrase() -> String {
let sem = DispatchSemaphore(value: 0)
var passphrase = ""
DispatchQueue.main.async {
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
passphrase = alert.textFields!.first!.text!
sem.signal()
}))
alert.addTextField(configurationHandler: {(textField: UITextField!) in
textField.text = ""
textField.isSecureTextEntry = true
})
self.present(alert, animated: true, completion: nil)
}
let _ = sem.wait(timeout: DispatchTime.distantFuture)
if Defaults[.isRememberPassphraseOn] {
self.passwordStore.pgpKeyPassphrase = passphrase
}
return passphrase
}
private func decryptThenShowPassword() {
DispatchQueue.global(qos: .userInitiated).async {
// decrypt password
do {
self.password = try self.passwordEntity!.decrypt(passphrase: passphrase)!
self.password = try self.passwordStore.decrypt(passwordEntity: self.passwordEntity!, requestPGPKeyPassphrase: self.requestPGPKeyPassphrase)
} catch {
DispatchQueue.main.async {
let alert = UIAlertController(title: "Cannot Show Password", message: error.localizedDescription, preferredStyle: UIAlertControllerStyle.alert)
@ -215,14 +215,14 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
if self.password!.changed {
SVProgressHUD.show(withStatus: "Saving")
DispatchQueue.global(qos: .userInitiated).async {
self.passwordStore.update(passwordEntity: self.passwordEntity!, password: self.password!, progressBlock: { progress in
do {
self.passwordEntity = try self.passwordStore.update(passwordEntity: self.passwordEntity!, password: self.password!)
} catch {
DispatchQueue.main.async {
SVProgressHUD.showProgress(progress, status: "Encrypting")
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
}
})
}
DispatchQueue.main.async {
self.passwordEntity!.synced = false
self.passwordStore.saveUpdated(passwordEntity: self.passwordEntity!)
self.setTableData()
self.tableView.reloadData()
SVProgressHUD.showSuccess(withStatus: "Success")
@ -233,7 +233,6 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
}
@IBAction private func deletePassword(segue: UIStoryboardSegue) {
print("delete")
passwordStore.delete(passwordEntity: passwordEntity!)
let _ = navigationController?.popViewController(animated: true)
}
@ -390,10 +389,14 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
// commit the change of HOTP counter
if password!.changed {
DispatchQueue.global(qos: .userInitiated).async {
self.passwordStore.update(passwordEntity: self.passwordEntity!, password: self.password!, progressBlock: {_ in })
do {
self.passwordEntity = try self.passwordStore.update(passwordEntity: self.passwordEntity!, password: self.password!)
} catch {
DispatchQueue.main.async {
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
}
}
DispatchQueue.main.async {
self.passwordEntity!.synced = false
self.passwordStore.saveUpdated(passwordEntity: self.passwordEntity!)
SVProgressHUD.showSuccess(withStatus: "Password Copied\nCounter Updated")
SVProgressHUD.dismiss(withDelay: 1)
}
@ -435,7 +438,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
cell.nameLabel.text = passwordName
}
}
cell.categoryLabel.text = passwordCategoryText
cell.categoryLabel.text = passwordEntity!.getCategoryText()
cell.selectionStyle = .none
return cell
case .main, .addition:
@ -465,7 +468,7 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
footerLabel.numberOfLines = 0
footerLabel.font = UIFont.preferredFont(forTextStyle: .footnote)
footerLabel.textColor = UIColor.gray
let dateString = self.passwordStore.getLatestUpdateInfo(filename: (passwordEntity?.path)!)
let dateString = self.passwordStore.getLatestUpdateInfo(filename: password!.url!.path)
footerLabel.text = "Last Updated: \(dateString)"
view.addSubview(footerLabel)
return view

View file

@ -112,11 +112,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
SVProgressHUD.show(withStatus: "Saving")
DispatchQueue.global(qos: .userInitiated).async {
do {
try self.passwordStore.add(password: controller.password!, progressBlock: { progress in
DispatchQueue.main.async {
SVProgressHUD.showProgress(progress, status: "Encrypting")
}
})
let _ = try self.passwordStore.add(password: controller.password!)
DispatchQueue.main.async {
// will trigger reloadTableView() by a notification
SVProgressHUD.showSuccess(withStatus: "Done")
@ -323,15 +319,19 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
}
let password = getPasswordEntry(by: indexPath).passwordEntity!
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
decryptThenCopyPassword(passwordEntity: password)
}
private func requestPGPKeyPassphrase() -> String {
let sem = DispatchSemaphore(value: 0)
var passphrase = ""
if Defaults[.isRememberPassphraseOn] && self.passwordStore.pgpKeyPassphrase != nil {
passphrase = self.passwordStore.pgpKeyPassphrase!
self.decryptThenCopyPassword(passwordEntity: password, passphrase: passphrase)
} else {
DispatchQueue.main.async {
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your PGP secret key.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
passphrase = alert.textFields!.first!.text!
self.decryptThenCopyPassword(passwordEntity: password, passphrase: passphrase)
sem.signal()
}))
alert.addTextField(configurationHandler: {(textField: UITextField!) in
textField.text = ""
@ -339,17 +339,21 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
})
self.present(alert, animated: true, completion: nil)
}
let _ = sem.wait(timeout: DispatchTime.distantFuture)
if Defaults[.isRememberPassphraseOn] {
self.passwordStore.pgpKeyPassphrase = passphrase
}
return passphrase
}
private func decryptThenCopyPassword(passwordEntity: PasswordEntity, passphrase: String) {
private func decryptThenCopyPassword(passwordEntity: PasswordEntity) {
SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.setDefaultStyle(.dark)
SVProgressHUD.show(withStatus: "Decrypting")
DispatchQueue.global(qos: .userInteractive).async {
var decryptedPassword: Password?
do {
decryptedPassword = try passwordEntity.decrypt(passphrase: passphrase)!
decryptedPassword = try self.passwordStore.decrypt(passwordEntity: passwordEntity, requestPGPKeyPassphrase: self.requestPGPKeyPassphrase)
DispatchQueue.main.async {
Utils.copyToPasteboard(textToCopy: decryptedPassword?.password)
SVProgressHUD.showSuccess(withStatus: "Password copied, and will be cleared in 45 seconds.")

View file

@ -20,6 +20,15 @@ class Password {
static let otpKeywords = ["otp_secret", "otp_type", "otp_algorithm", "otp_period", "otp_digits", "otp_counter", "otpauth"]
var name = ""
var url: URL?
var namePath: String {
get {
if url == nil {
return ""
}
return url!.deletingPathExtension().path
}
}
var password = ""
var additions = [String: String]()
var additionKeys = [String]()
@ -47,19 +56,20 @@ class Password {
}
}
init(name: String, plainText: String) {
self.initEverything(name: name, plainText: plainText)
init(name: String, url: URL?, plainText: String) {
self.initEverything(name: name, url: url, plainText: plainText)
}
func updatePassword(name: String, plainText: String) {
if self.plainText != plainText {
self.initEverything(name: name, plainText: plainText)
func updatePassword(name: String, url: URL?, plainText: String) {
if self.plainText != plainText || self.url != url {
self.initEverything(name: name, url: url, plainText: plainText)
changed = true
}
}
private func initEverything(name: String, plainText: String) {
private func initEverything(name: String, url: URL?, plainText: String) {
self.name = name
self.url = url
self.plainText = plainText
self.additions.removeAll()
self.additionKeys.removeAll()
@ -322,7 +332,7 @@ class Password {
if newOtpauth != nil {
lines.append(newOtpauth!)
}
self.updatePassword(name: self.name, plainText: lines.joined(separator: "\n"))
self.updatePassword(name: self.name, url: self.url, plainText: lines.joined(separator: "\n"))
// get and return the password
return self.otpToken?.currentPassword

View file

@ -21,24 +21,6 @@ extension PasswordEntity {
}
}
func decrypt(passphrase: String) throws -> Password? {
var password: Password?
let encryptedDataPath = URL(fileURLWithPath: "\(Globals.repositoryPath)/\(path!)")
let encryptedData = try Data(contentsOf: encryptedDataPath)
let decryptedData = try PasswordStore.shared.pgp.decryptData(encryptedData, passphrase: passphrase)
let plainText = String(data: decryptedData, encoding: .utf8) ?? ""
password = Password(name: name!, plainText: plainText)
return password
}
func encrypt(password: Password) throws -> Data {
name = password.name
let plainData = password.getPlainData()
let pgp = PasswordStore.shared.pgp
let encryptedData = try pgp.encryptData(plainData, usingPublicKey: pgp.getKeysOf(.public)[0], armored: Defaults[.encryptInArmored])
return encryptedData
}
func getCategoryText() -> String {
var parentEntity = parent
var passwordCategoryArray: [String] = []

View file

@ -284,10 +284,9 @@ class PasswordStore {
}
func passwordExisted(password: Password) -> Bool {
print(password.name)
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do {
passwordEntityFetchRequest.predicate = NSPredicate(format: "name = %@", password.name)
passwordEntityFetchRequest.predicate = NSPredicate(format: "name = %@ and path = %@", password.name, password.url!.path)
let count = try context.count(for: passwordEntityFetchRequest)
if count > 0 {
return true
@ -300,6 +299,32 @@ class PasswordStore {
return true
}
func passwordEntityExisted(path: String) -> Bool {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do {
passwordEntityFetchRequest.predicate = NSPredicate(format: "path = %@", path)
let count = try context.count(for: passwordEntityFetchRequest)
if count > 0 {
return true
} else {
return false
}
} catch {
fatalError("Failed to fetch password entities: \(error)")
}
return true
}
func getPasswordEntity(by path: String) -> PasswordEntity? {
let passwordEntityFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "PasswordEntity")
do {
passwordEntityFetchRequest.predicate = NSPredicate(format: "path = %@", path)
return try context.fetch(passwordEntityFetchRequest).first as? PasswordEntity
} catch {
fatalError("Failed to fetch password entities: \(error)")
}
}
func cloneRepository(remoteRepoURL: URL,
credential: GitCredential,
transferProgressBlock: @escaping (UnsafePointer<git_transfer_progress>, UnsafeMutablePointer<ObjCBool>) -> Void,
@ -511,19 +536,17 @@ class PasswordStore {
func updateRemoteRepo() {
}
func createAddCommitInRepository(message: String, fileData: Data, filename: String, progressBlock: (_ progress: Float) -> Void) -> GTCommit? {
func createAddCommitInRepository(message: String, path: String) -> GTCommit? {
do {
try storeRepository?.index().add(fileData, withPath: filename)
try storeRepository?.index().addFile(path)
try storeRepository?.index().write()
let newTree = try storeRepository!.index().writeTree()
let headReference = try storeRepository!.headReference()
let commitEnum = try GTEnumerator(repository: storeRepository!)
try commitEnum.pushSHA(headReference.targetOID.sha!)
let parent = commitEnum.nextObject() as! GTCommit
progressBlock(0.5)
let signature = gitSignatureForNow
let commit = try storeRepository!.createCommit(with: newTree, message: message, author: signature, committer: signature, parents: [parent], updatingReferenceNamed: headReference.name)
progressBlock(0.7)
return commit
} catch {
print(error)
@ -571,57 +594,93 @@ class PasswordStore {
try storeRepository?.push(masterBranch, to: remote, withOptions: options, progress: transferProgressBlock)
}
func add(password: Password, progressBlock: (_ progress: Float) -> Void) throws {
progressBlock(0.0)
private func addPasswordEntities(password: Password) -> PasswordEntity? {
var passwordURL = password.url!
var paths: [String] = []
while passwordURL.path != "." {
paths.append(passwordURL.path)
passwordURL = passwordURL.deletingLastPathComponent()
}
paths.reverse()
var parentPasswordEntity: PasswordEntity? = nil
for path in paths {
if let passwordEntity = getPasswordEntity(by: path) {
parentPasswordEntity = passwordEntity
} else {
if path.hasSuffix(".gpg") {
return insertPasswordEntity(name: URL(string: path)!.deletingPathExtension().lastPathComponent, path: path, parent: parentPasswordEntity, synced: false, isDir: false)
} else {
parentPasswordEntity = insertPasswordEntity(name: URL(string: path)!.lastPathComponent, path: path, parent: parentPasswordEntity, synced: false, isDir: true)
let fm = FileManager.default
let saveURL = storeURL.appendingPathComponent(path)
do {
try fm.createDirectory(at: saveURL, withIntermediateDirectories: false, attributes: nil)
} catch {
print(error)
}
}
}
}
return nil
}
private func insertPasswordEntity(name: String, path: String, parent: PasswordEntity?, synced: Bool = false, isDir: Bool = false) -> PasswordEntity? {
var ret: PasswordEntity? = nil
DispatchQueue.main.sync {
if let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: self.context) as? PasswordEntity {
passwordEntity.name = name
passwordEntity.path = path
passwordEntity.parent = parent
passwordEntity.synced = synced
passwordEntity.isDir = isDir
do {
try self.context.save()
ret = passwordEntity
} catch {
fatalError("Failed to insert a PasswordEntity: \(error)")
}
}
}
return ret
}
func add(password: Password) throws -> PasswordEntity? {
guard !passwordExisted(password: password) else {
throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot add password: password duplicated."])
}
let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: context) as! PasswordEntity
do {
let encryptedData = try passwordEntity.encrypt(password: password)
progressBlock(0.3)
let saveURL = storeURL.appendingPathComponent("\(password.name).gpg")
try encryptedData.write(to: saveURL)
passwordEntity.name = password.name
passwordEntity.path = "\(password.name).gpg"
passwordEntity.parent = nil
passwordEntity.synced = false
passwordEntity.isDir = false
try context.save()
print(saveURL.path)
let _ = createAddCommitInRepository(message: "Add password for \(passwordEntity.nameWithCategory) to store using Pass for iOS.", fileData: encryptedData, filename: saveURL.lastPathComponent, progressBlock: progressBlock)
progressBlock(1.0)
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
} catch {
print(error)
}
let newPasswordEntity = addPasswordEntities(password: password)
print("new: \(newPasswordEntity!.path!)")
let saveURL = storeURL.appendingPathComponent(password.url!.path)
try self.encrypt(password: password).write(to: saveURL)
let _ = createAddCommitInRepository(message: "Add password for \(password.url!.deletingPathExtension().path) to store using Pass for iOS.", path: password.url!.path)
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
return newPasswordEntity
}
func update(passwordEntity: PasswordEntity, password: Password, progressBlock: (_ progress: Float) -> Void) {
progressBlock(0.0)
do {
let encryptedData = try passwordEntity.encrypt(password: password)
let saveURL = storeURL.appendingPathComponent(passwordEntity.path!)
try encryptedData.write(to: saveURL)
progressBlock(0.3)
let _ = createAddCommitInRepository(message: "Edit password for \(passwordEntity.nameWithCategory) using Pass for iOS.", fileData: encryptedData, filename: saveURL.lastPathComponent, progressBlock: progressBlock)
progressBlock(1.0)
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
} catch {
print(error)
}
func update(passwordEntity: PasswordEntity, password: Password) throws -> PasswordEntity? {
delete(passwordEntity: passwordEntity)
return try add(password: password)
}
public func delete(passwordEntity: PasswordEntity) {
Utils.removeFileIfExists(at: storeURL.appendingPathComponent(passwordEntity.path!))
let _ = createRemoveCommitInRepository(message: "Remove \(passwordEntity.nameWithCategory) from store using Pass for iOS", path: passwordEntity.path!)
context.delete(passwordEntity)
do {
try context.save()
} catch {
fatalError("Failed to delete a PasswordEntity: \(error)")
DispatchQueue.main.async {
let _ = self.createRemoveCommitInRepository(message: "Remove \(passwordEntity.nameWithCategory) from store using Pass for iOS", path: passwordEntity.path!)
var current: PasswordEntity? = passwordEntity
while current != nil && (current!.children!.count == 0 || !current!.isDir) {
Utils.removeFileIfExists(at: self.storeURL.appendingPathComponent(current!.path!))
let parent = current!.parent
self.context.delete(current!)
current = parent
do {
try self.context.save()
} catch {
fatalError("Failed to delete a PasswordEntity: \(error)")
}
}
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
}
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
}
func saveUpdated(passwordEntity: PasswordEntity) {
@ -741,4 +800,27 @@ class PasswordStore {
// get a list of local commits
return try storeRepository?.localCommitsRelative(toRemoteBranch: remoteMasterBranch)
}
func decrypt(passwordEntity: PasswordEntity, requestPGPKeyPassphrase: () -> String) throws -> Password? {
var password: Password?
let encryptedDataPath = URL(fileURLWithPath: "\(Globals.repositoryPath)/\(passwordEntity.path!)")
let encryptedData = try Data(contentsOf: encryptedDataPath)
var passphrase = self.pgpKeyPassphrase
if passphrase == nil {
passphrase = requestPGPKeyPassphrase()
}
let decryptedData = try PasswordStore.shared.pgp.decryptData(encryptedData, passphrase: passphrase)
let plainText = String(data: decryptedData, encoding: .utf8) ?? ""
password = Password(name: passwordEntity.name!, url: URL(string: passwordEntity.path!), plainText: plainText)
return password
}
func encrypt(password: Password) throws -> Data {
let plainData = password.getPlainData()
let pgp = PasswordStore.shared.pgp
let encryptedData = try pgp.encryptData(plainData, usingPublicKey: pgp.getKeysOf(.public)[0], armored: Defaults[.encryptInArmored])
return encryptedData
}
}