Improve edit password to make it consistent with Pass
This commit is contained in:
parent
86cb8a84cd
commit
055ea243a3
6 changed files with 136 additions and 103 deletions
|
|
@ -53,7 +53,7 @@ class EditPasswordTableViewController: PasswordEditorTableViewController {
|
||||||
if cellContents["additions"]! != "" {
|
if cellContents["additions"]! != "" {
|
||||||
plainText = "\(cellContents["password"]!)\n\(cellContents["additions"]!)"
|
plainText = "\(cellContents["password"]!)\n\(cellContents["additions"]!)"
|
||||||
} else {
|
} else {
|
||||||
plainText = "\(cellContents["password"]!)\n"
|
plainText = "\(cellContents["password"]!)"
|
||||||
}
|
}
|
||||||
let name = URL(string: cellContents["name"]!)!.lastPathComponent
|
let name = URL(string: cellContents["name"]!)!.lastPathComponent
|
||||||
let url = URL(string: cellContents["name"]!)!.appendingPathExtension("gpg")
|
let url = URL(string: cellContents["name"]!)!.appendingPathExtension("gpg")
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ class GitConfigSettingTableViewController: UITableViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
||||||
print("test should perform \(identifier)")
|
|
||||||
if identifier == "saveGitConfigSettingSegue" {
|
if identifier == "saveGitConfigSettingSegue" {
|
||||||
guard let name = nameTextField.text, !name.isEmpty else {
|
guard let name = nameTextField.text, !name.isEmpty else {
|
||||||
Utils.alert(title: "Cannot Save", message: "Please set name first.", controller: self, completion: nil)
|
Utils.alert(title: "Cannot Save", message: "Please set name first.", controller: self, completion: nil)
|
||||||
|
|
|
||||||
|
|
@ -212,28 +212,26 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction private func saveEditPassword(segue: UIStoryboardSegue) {
|
@IBAction private func saveEditPassword(segue: UIStoryboardSegue) {
|
||||||
if self.password!.changed {
|
if self.password!.changed != 0 {
|
||||||
SVProgressHUD.show(withStatus: "Saving")
|
SVProgressHUD.show(withStatus: "Saving")
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
do {
|
||||||
do {
|
self.passwordEntity = try self.passwordStore.edit(passwordEntity: self.passwordEntity!, password: self.password!)
|
||||||
self.passwordEntity = try self.passwordStore.update(passwordEntity: self.passwordEntity!, password: self.password!)
|
} catch {
|
||||||
} catch {
|
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
|
||||||
DispatchQueue.main.async {
|
|
||||||
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.setTableData()
|
|
||||||
self.tableView.reloadData()
|
|
||||||
SVProgressHUD.showSuccess(withStatus: "Success")
|
|
||||||
SVProgressHUD.dismiss(withDelay: 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
self.setTableData()
|
||||||
|
self.tableView.reloadData()
|
||||||
|
SVProgressHUD.showSuccess(withStatus: "Success")
|
||||||
|
SVProgressHUD.dismiss(withDelay: 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction private func deletePassword(segue: UIStoryboardSegue) {
|
@IBAction private func deletePassword(segue: UIStoryboardSegue) {
|
||||||
passwordStore.delete(passwordEntity: passwordEntity!)
|
do {
|
||||||
|
try passwordStore.delete(passwordEntity: passwordEntity!)
|
||||||
|
} catch {
|
||||||
|
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
|
||||||
|
}
|
||||||
let _ = navigationController?.popViewController(animated: true)
|
let _ = navigationController?.popViewController(animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -387,20 +385,14 @@ class PasswordDetailTableViewController: UITableViewController, UIGestureRecogni
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit the change of HOTP counter
|
// commit the change of HOTP counter
|
||||||
if password!.changed {
|
if password!.changed != 0 {
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
do {
|
||||||
do {
|
self.passwordEntity = try self.passwordStore.edit(passwordEntity: self.passwordEntity!, password: self.password!)
|
||||||
self.passwordEntity = try self.passwordStore.update(passwordEntity: self.passwordEntity!, password: self.password!)
|
} catch {
|
||||||
} catch {
|
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
|
||||||
DispatchQueue.main.async {
|
|
||||||
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
SVProgressHUD.showSuccess(withStatus: "Password Copied\nCounter Updated")
|
|
||||||
SVProgressHUD.dismiss(withDelay: 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
SVProgressHUD.showSuccess(withStatus: "Password Copied\nCounter Updated")
|
||||||
|
SVProgressHUD.dismiss(withDelay: 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,12 @@ struct AdditionField {
|
||||||
var content: String
|
var content: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PasswordChange: Int {
|
||||||
|
case path = 0x01
|
||||||
|
case content = 0x02
|
||||||
|
case none = 0x00
|
||||||
|
}
|
||||||
|
|
||||||
class Password {
|
class Password {
|
||||||
static let otpKeywords = ["otp_secret", "otp_type", "otp_algorithm", "otp_period", "otp_digits", "otp_counter", "otpauth"]
|
static let otpKeywords = ["otp_secret", "otp_type", "otp_algorithm", "otp_period", "otp_digits", "otp_counter", "otpauth"]
|
||||||
|
|
||||||
|
|
@ -32,7 +38,7 @@ class Password {
|
||||||
var password = ""
|
var password = ""
|
||||||
var additions = [String: String]()
|
var additions = [String: String]()
|
||||||
var additionKeys = [String]()
|
var additionKeys = [String]()
|
||||||
var changed = false
|
var changed: Int = 0
|
||||||
var plainText = ""
|
var plainText = ""
|
||||||
|
|
||||||
private var firstLineIsOTPField = false
|
private var firstLineIsOTPField = false
|
||||||
|
|
@ -62,8 +68,13 @@ class Password {
|
||||||
|
|
||||||
func updatePassword(name: String, url: URL?, plainText: String) {
|
func updatePassword(name: String, url: URL?, plainText: String) {
|
||||||
if self.plainText != plainText || self.url != url {
|
if self.plainText != plainText || self.url != url {
|
||||||
|
if self.plainText != plainText {
|
||||||
|
changed = changed|PasswordChange.content.rawValue
|
||||||
|
}
|
||||||
|
if self.url != url {
|
||||||
|
changed = changed|PasswordChange.path.rawValue
|
||||||
|
}
|
||||||
self.initEverything(name: name, url: url, plainText: plainText)
|
self.initEverything(name: name, url: url, plainText: plainText)
|
||||||
changed = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,4 +31,11 @@ extension PasswordEntity {
|
||||||
passwordCategoryArray.reverse()
|
passwordCategoryArray.reverse()
|
||||||
return passwordCategoryArray.joined(separator: " > ")
|
return passwordCategoryArray.joined(separator: " > ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getURL() -> URL? {
|
||||||
|
if let p = path {
|
||||||
|
return URL(string: p)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -509,43 +509,51 @@ class PasswordStore {
|
||||||
func updateRemoteRepo() {
|
func updateRemoteRepo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createAddCommitInRepository(message: String, path: String) -> GTCommit? {
|
private func gitAdd(path: String) throws {
|
||||||
do {
|
if let repo = storeRepository {
|
||||||
try storeRepository?.index().addFile(path)
|
try repo.index().addFile(path)
|
||||||
try storeRepository?.index().write()
|
try repo.index().write()
|
||||||
let newTree = try storeRepository!.index().writeTree()
|
}
|
||||||
let headReference = try storeRepository!.headReference()
|
}
|
||||||
let commitEnum = try GTEnumerator(repository: storeRepository!)
|
|
||||||
|
private func gitRm(path: String) throws {
|
||||||
|
if let repo = storeRepository {
|
||||||
|
var url = storeURL.appendingPathComponent(path)
|
||||||
|
Utils.removeFileIfExists(at: url)
|
||||||
|
let fm = FileManager.default
|
||||||
|
url.deleteLastPathComponent()
|
||||||
|
var count = try fm.contentsOfDirectory(atPath: url.path).count
|
||||||
|
while count == 0 {
|
||||||
|
Utils.removeFileIfExists(atPath: url.path)
|
||||||
|
url.deleteLastPathComponent()
|
||||||
|
count = try fm.contentsOfDirectory(atPath: url.path).count
|
||||||
|
}
|
||||||
|
try repo.index().removeFile(path)
|
||||||
|
try repo.index().write()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func gitMv(from: String, to: String) throws {
|
||||||
|
let fm = FileManager.default
|
||||||
|
try fm.moveItem(at: storeURL.appendingPathComponent(from), to: storeURL.appendingPathComponent(to))
|
||||||
|
try gitAdd(path: to)
|
||||||
|
try gitRm(path: from)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func gitCommit(message: String) throws -> GTCommit? {
|
||||||
|
if let repo = storeRepository {
|
||||||
|
let newTree = try repo.index().writeTree()
|
||||||
|
let headReference = try repo.headReference()
|
||||||
|
let commitEnum = try GTEnumerator(repository: repo)
|
||||||
try commitEnum.pushSHA(headReference.targetOID.sha!)
|
try commitEnum.pushSHA(headReference.targetOID.sha!)
|
||||||
let parent = commitEnum.nextObject() as! GTCommit
|
let parent = commitEnum.nextObject() as! GTCommit
|
||||||
let signature = gitSignatureForNow
|
let signature = gitSignatureForNow
|
||||||
let commit = try storeRepository!.createCommit(with: newTree, message: message, author: signature, committer: signature, parents: [parent], updatingReferenceNamed: headReference.name)
|
let commit = try repo.createCommit(with: newTree, message: message, author: signature, committer: signature, parents: [parent], updatingReferenceNamed: headReference.name)
|
||||||
return commit
|
return commit
|
||||||
} catch {
|
|
||||||
print(error)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createRemoveCommitInRepository(message: String, path: String) -> GTCommit? {
|
|
||||||
do {
|
|
||||||
try storeRepository?.index().removeFile(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
|
|
||||||
let signature = gitSignatureForNow
|
|
||||||
let commit = try storeRepository!.createCommit(with: newTree, message: message, author: signature, committer: signature, parents: [parent], updatingReferenceNamed: headReference.name)
|
|
||||||
return commit
|
|
||||||
} catch {
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private func getLocalBranch(withName branchName: String) -> GTBranch? {
|
private func getLocalBranch(withName branchName: String) -> GTBranch? {
|
||||||
do {
|
do {
|
||||||
let reference = GTBranch.localNamePrefix().appending(branchName)
|
let reference = GTBranch.localNamePrefix().appending(branchName)
|
||||||
|
|
@ -567,7 +575,11 @@ class PasswordStore {
|
||||||
try storeRepository?.push(masterBranch, to: remote, withOptions: options, progress: transferProgressBlock)
|
try storeRepository?.push(masterBranch, to: remote, withOptions: options, progress: transferProgressBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addPasswordEntities(password: Password) -> PasswordEntity? {
|
private func addPasswordEntities(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."])
|
||||||
|
}
|
||||||
|
|
||||||
var passwordURL = password.url!
|
var passwordURL = password.url!
|
||||||
var paths: [String] = []
|
var paths: [String] = []
|
||||||
while passwordURL.path != "." {
|
while passwordURL.path != "." {
|
||||||
|
|
@ -599,63 +611,75 @@ class PasswordStore {
|
||||||
|
|
||||||
private func insertPasswordEntity(name: String, path: String, parent: PasswordEntity?, synced: Bool = false, isDir: Bool = false) -> PasswordEntity? {
|
private func insertPasswordEntity(name: String, path: String, parent: PasswordEntity?, synced: Bool = false, isDir: Bool = false) -> PasswordEntity? {
|
||||||
var ret: PasswordEntity? = nil
|
var ret: PasswordEntity? = nil
|
||||||
DispatchQueue.main.sync {
|
if let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: self.context) as? PasswordEntity {
|
||||||
if let passwordEntity = NSEntityDescription.insertNewObject(forEntityName: "PasswordEntity", into: self.context) as? PasswordEntity {
|
passwordEntity.name = name
|
||||||
passwordEntity.name = name
|
passwordEntity.path = path
|
||||||
passwordEntity.path = path
|
passwordEntity.parent = parent
|
||||||
passwordEntity.parent = parent
|
passwordEntity.synced = synced
|
||||||
passwordEntity.synced = synced
|
passwordEntity.isDir = isDir
|
||||||
passwordEntity.isDir = isDir
|
do {
|
||||||
do {
|
try self.context.save()
|
||||||
try self.context.save()
|
ret = passwordEntity
|
||||||
ret = passwordEntity
|
} catch {
|
||||||
} catch {
|
fatalError("Failed to insert a PasswordEntity: \(error)")
|
||||||
fatalError("Failed to insert a PasswordEntity: \(error)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func add(password: Password) throws -> PasswordEntity? {
|
func add(password: Password) throws -> PasswordEntity? {
|
||||||
guard !passwordExisted(password: password) else {
|
let newPasswordEntity = try addPasswordEntities(password: password)
|
||||||
throw NSError(domain: "me.mssun.pass.error", code: 2, userInfo: [NSLocalizedDescriptionKey: "Cannot add password: password duplicated."])
|
|
||||||
}
|
|
||||||
let newPasswordEntity = addPasswordEntities(password: password)
|
|
||||||
print("new: \(newPasswordEntity!.path!)")
|
|
||||||
let saveURL = storeURL.appendingPathComponent(password.url!.path)
|
let saveURL = storeURL.appendingPathComponent(password.url!.path)
|
||||||
try self.encrypt(password: password).write(to: saveURL)
|
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)
|
try gitAdd(path: password.url!.path)
|
||||||
|
let _ = try gitCommit(message: "Add password for \(password.url!.deletingPathExtension().path) to store using Pass for iOS.")
|
||||||
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
||||||
return newPasswordEntity
|
return newPasswordEntity
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(passwordEntity: PasswordEntity, password: Password) throws -> PasswordEntity? {
|
func edit(passwordEntity: PasswordEntity, password: Password) throws -> PasswordEntity? {
|
||||||
delete(passwordEntity: passwordEntity)
|
var newPasswordEntity: PasswordEntity? = passwordEntity
|
||||||
return try add(password: password)
|
|
||||||
|
if password.changed&PasswordChange.content.rawValue != 0 {
|
||||||
|
let saveURL = storeURL.appendingPathComponent(password.url!.path)
|
||||||
|
try self.encrypt(password: password).write(to: saveURL)
|
||||||
|
try gitAdd(path: password.url!.path)
|
||||||
|
let _ = try gitCommit(message: "Edit password for \(password.url!.deletingPathExtension().path) to store using Pass for iOS.")
|
||||||
|
}
|
||||||
|
guard newPasswordEntity != nil else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if password.changed&PasswordChange.path.rawValue != 0 {
|
||||||
|
let oldPasswordURL = newPasswordEntity!.getURL()
|
||||||
|
try self.deletePasswordEntities(passwordEntity: newPasswordEntity!)
|
||||||
|
newPasswordEntity = try self.addPasswordEntities(password: password)
|
||||||
|
try gitMv(from: oldPasswordURL!.path, to: password.url!.path)
|
||||||
|
let _ = try gitCommit(message: "Rename \(oldPasswordURL!.deletingPathExtension().path) to \(password.url!.deletingPathExtension().path) using Pass for iOS.")
|
||||||
|
}
|
||||||
|
return newPasswordEntity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func deletePasswordEntities(passwordEntity: PasswordEntity) throws {
|
||||||
public func delete(passwordEntity: PasswordEntity) {
|
var current: PasswordEntity? = passwordEntity
|
||||||
DispatchQueue.main.async {
|
while current != nil && (current!.children!.count == 0 || !current!.isDir) {
|
||||||
let _ = self.createRemoveCommitInRepository(message: "Remove \(passwordEntity.nameWithCategory) from store using Pass for iOS", path: passwordEntity.path!)
|
let parent = current!.parent
|
||||||
var current: PasswordEntity? = passwordEntity
|
self.context.delete(current!)
|
||||||
while current != nil && (current!.children!.count == 0 || !current!.isDir) {
|
current = parent
|
||||||
Utils.removeFileIfExists(at: self.storeURL.appendingPathComponent(current!.path!))
|
do {
|
||||||
let parent = current!.parent
|
try self.context.save()
|
||||||
self.context.delete(current!)
|
} catch {
|
||||||
current = parent
|
fatalError("Failed to delete a PasswordEntity: \(error)")
|
||||||
do {
|
|
||||||
try self.context.save()
|
|
||||||
} catch {
|
|
||||||
fatalError("Failed to delete a PasswordEntity: \(error)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func delete(passwordEntity: PasswordEntity) throws {
|
||||||
|
try gitRm(path: passwordEntity.path!)
|
||||||
|
let _ = try gitCommit(message: "Remove \(passwordEntity.nameWithCategory) from store using Pass for iOS.")
|
||||||
|
try deletePasswordEntities(passwordEntity: passwordEntity)
|
||||||
|
NotificationCenter.default.post(name: .passwordStoreUpdated, object: nil)
|
||||||
|
}
|
||||||
|
|
||||||
func saveUpdated(passwordEntity: PasswordEntity) {
|
func saveUpdated(passwordEntity: PasswordEntity) {
|
||||||
do {
|
do {
|
||||||
try context.save()
|
try context.save()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue