commit
6b3ce819db
7 changed files with 130 additions and 102 deletions
|
|
@ -50,7 +50,7 @@
|
|||
30697C5F21F674800064FCAC /* String+UtilitiesTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30697C5E21F674800064FCAC /* String+UtilitiesTest.swift */; };
|
||||
3087574F2343E42A00B971A2 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3087574E2343E42A00B971A2 /* Colors.swift */; };
|
||||
308C273A2279F9CB0016D0E2 /* SearchBarScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = 302202EE222F14E400555236 /* SearchBarScope.swift */; };
|
||||
30A1D29C21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift */; };
|
||||
30A1D29C21AF451E00E2D1F7 /* PasswordGeneratorFlavorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavorTest.swift */; };
|
||||
30A1D2A221B2BC6F00E2D1F7 /* TokenBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1D2A121B2BC6F00E2D1F7 /* TokenBuilder.swift */; };
|
||||
30A1D2A621B2D46100E2D1F7 /* OtpType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1D2A521B2D46100E2D1F7 /* OtpType.swift */; };
|
||||
30A1D2A821B2D53200E2D1F7 /* PasswordChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30A1D2A721B2D53200E2D1F7 /* PasswordChange.swift */; };
|
||||
|
|
@ -274,7 +274,7 @@
|
|||
30697C5221F63E0B0064FCAC /* CredentialProviderViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialProviderViewController.swift; sourceTree = "<group>"; };
|
||||
30697C5E21F674800064FCAC /* String+UtilitiesTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+UtilitiesTest.swift"; sourceTree = "<group>"; };
|
||||
3087574E2343E42A00B971A2 /* Colors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = "<group>"; };
|
||||
30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordGeneratorFlavourTest.swift; sourceTree = "<group>"; };
|
||||
30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavorTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordGeneratorFlavorTest.swift; sourceTree = "<group>"; };
|
||||
30A1D2A121B2BC6F00E2D1F7 /* TokenBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenBuilder.swift; sourceTree = "<group>"; };
|
||||
30A1D2A521B2D46100E2D1F7 /* OtpType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OtpType.swift; sourceTree = "<group>"; };
|
||||
30A1D2A721B2D53200E2D1F7 /* PasswordChange.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordChange.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -460,7 +460,7 @@
|
|||
301F6464216164670071A4CE /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift */,
|
||||
30A1D29B21AF451E00E2D1F7 /* PasswordGeneratorFlavorTest.swift */,
|
||||
3032328922C9FBA2009EBD9C /* KeyFileManagerTest.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
|
|
@ -1362,7 +1362,7 @@
|
|||
30BAC8C722E3BAAF00438475 /* TestPGPKeys.swift in Sources */,
|
||||
30A1D2AA21B32A0100E2D1F7 /* OtpTypeTest.swift in Sources */,
|
||||
301F6468216165290071A4CE /* ConstantsTest.swift in Sources */,
|
||||
30A1D29C21AF451E00E2D1F7 /* PasswordGeneratorFlavourTest.swift in Sources */,
|
||||
30A1D29C21AF451E00E2D1F7 /* PasswordGeneratorFlavorTest.swift in Sources */,
|
||||
A26075881EEC6F34005DB03E /* passKitTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
|||
|
|
@ -19,11 +19,9 @@ enum PasswordEditorCellKey {
|
|||
case type, title, content, placeholders
|
||||
}
|
||||
|
||||
class PasswordEditorTableViewController: UITableViewController, FillPasswordTableViewCellDelegate, PasswordSettingSliderTableViewCellDelegate, QRScannerControllerDelegate, UITextFieldDelegate, UITextViewDelegate, SFSafariViewControllerDelegate {
|
||||
class PasswordEditorTableViewController: UITableViewController {
|
||||
|
||||
var tableData = [
|
||||
[Dictionary<PasswordEditorCellKey, Any>]
|
||||
]()
|
||||
var tableData = [[Dictionary<PasswordEditorCellKey, Any>]]()
|
||||
var password: Password?
|
||||
|
||||
private var navigationItemTitle: String?
|
||||
|
|
@ -90,20 +88,26 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
tableView.estimatedRowHeight = 48
|
||||
self.tableView.sectionFooterHeight = UITableView.automaticDimension
|
||||
self.tableView.estimatedSectionFooterHeight = 0
|
||||
tableView.sectionFooterHeight = UITableView.automaticDimension
|
||||
tableView.estimatedSectionFooterHeight = 0
|
||||
|
||||
tableData = [
|
||||
[[.type: PasswordEditorCellType.nameCell, .title: "Name".localize(), .content: password?.namePath ?? ""]],
|
||||
[
|
||||
[.type: PasswordEditorCellType.fillPasswordCell, .title: "Password".localize(), .content: password?.password ?? ""]],
|
||||
[[.type: PasswordEditorCellType.additionsCell, .title: "Additions".localize(), .content: password?.additionsPlainText ?? ""]],
|
||||
[[.type: PasswordEditorCellType.scanQRCodeCell],
|
||||
[.type: PasswordEditorCellType.deletePasswordCell]]
|
||||
[.type: PasswordEditorCellType.nameCell, .title: "Name".localize(), .content: password?.namePath ?? ""],
|
||||
],
|
||||
[
|
||||
[.type: PasswordEditorCellType.fillPasswordCell, .title: "Password".localize(), .content: password?.password ?? ""],
|
||||
[.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"],
|
||||
[.type: PasswordEditorCellType.passwordFlavorCell],
|
||||
],
|
||||
[
|
||||
[.type: PasswordEditorCellType.additionsCell, .title: "Additions".localize(), .content: password?.additionsPlainText ?? ""],
|
||||
],
|
||||
[
|
||||
[.type: PasswordEditorCellType.scanQRCodeCell],
|
||||
[.type: PasswordEditorCellType.deletePasswordCell],
|
||||
]
|
||||
]
|
||||
|
||||
tableData[1].append([.type: PasswordEditorCellType.passwordLengthCell, .title: "passwordlength"])
|
||||
tableData[1].append([.type: PasswordEditorCellType.passwordFlavorCell])
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
|
|
@ -203,7 +207,7 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
}
|
||||
}
|
||||
|
||||
func showPasswordGeneratorFlavorActionSheet(sourceCell: UITableViewCell, tableView: UITableView) {
|
||||
private func showPasswordGeneratorFlavorActionSheet(sourceCell: UITableViewCell, tableView: UITableView) {
|
||||
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
|
||||
PasswordGeneratorFlavor.allCases.forEach { flavor in
|
||||
|
|
@ -226,23 +230,8 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
self.present(optionMenu, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
// generate password, copy to pasteboard, and set the cell
|
||||
// check whether the current password looks like an OTP field
|
||||
func generateAndCopyPassword() {
|
||||
if let currentPassword = fillPasswordCell?.getContent(), Constants.isOtpRelated(line: currentPassword) {
|
||||
let alert = UIAlertController(title: "Overwrite?".localize(), message: "OverwriteOtpConfiguration?".localize(), preferredStyle: UIAlertController.Style.alert)
|
||||
alert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertAction.Style.destructive, handler: {_ in
|
||||
self.generateAndCopyPasswordNoOtpCheck()
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: "Cancel".localize(), style: UIAlertAction.Style.cancel, handler: nil))
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
} else {
|
||||
self.generateAndCopyPasswordNoOtpCheck()
|
||||
}
|
||||
}
|
||||
|
||||
// generate the password, don't care whether the original line is otp
|
||||
func generateAndCopyPasswordNoOtpCheck() {
|
||||
private func generateAndCopyPasswordNoOtpCheck() {
|
||||
// show password settings (e.g., the length slider)
|
||||
showPasswordSettings()
|
||||
|
||||
|
|
@ -257,20 +246,14 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
}
|
||||
|
||||
// show password settings (e.g., the length slider)
|
||||
func showPasswordSettings() {
|
||||
private func showPasswordSettings() {
|
||||
if hidePasswordSettings == true {
|
||||
hidePasswordSettings = false
|
||||
tableView.reloadSections([passwordSection], with: .fade)
|
||||
}
|
||||
}
|
||||
|
||||
// show/hide password settings (e.g., the length slider)
|
||||
func showHidePasswordSettings() {
|
||||
hidePasswordSettings = !hidePasswordSettings
|
||||
tableView.reloadSections([passwordSection], with: .fade)
|
||||
}
|
||||
|
||||
func insertScannedOTPFields(_ otpauth: String) {
|
||||
private func insertScannedOTPFields(_ otpauth: String) {
|
||||
// update tableData
|
||||
var additionsString = ""
|
||||
if let additionsPlainText = (tableData[additionsSection][0][PasswordEditorCellKey.content] as? String)?.trimmed, additionsPlainText != "" {
|
||||
|
|
@ -284,20 +267,6 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
additionsCell?.setContent(content: additionsString)
|
||||
}
|
||||
|
||||
// MARK: - QRScannerControllerDelegate Methods
|
||||
func checkScannedOutput(line: String) -> (accept: Bool, message: String) {
|
||||
if let url = URL(string: line), let _ = Token(url: url) {
|
||||
return (accept: true, message: "ValidTokenUrl".localize())
|
||||
} else {
|
||||
return (accept: false, message: "InvalidTokenUrl".localize())
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - QRScannerControllerDelegate Methods
|
||||
func handleScannedOutput(line: String) {
|
||||
insertScannedOTPFields(line)
|
||||
}
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
if segue.identifier == "showQRScannerSegue" {
|
||||
if let navController = segue.destination as? UINavigationController {
|
||||
|
|
@ -310,31 +279,6 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
}
|
||||
}
|
||||
|
||||
// update tableData so to make sure reloadData() works correctly
|
||||
func textFieldDidEndEditing(_ textField: UITextField) {
|
||||
if textField == nameCell?.contentTextField {
|
||||
tableData[nameSection][0][PasswordEditorCellKey.content] = nameCell?.getContent()
|
||||
} else if textField == fillPasswordCell?.contentTextField {
|
||||
if let plainPassword = fillPasswordCell?.getContent() {
|
||||
tableData[passwordSection][0][PasswordEditorCellKey.content] = plainPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update tableData so to make sure reloadData() works correctly
|
||||
func textViewDidEndEditing(_ textView: UITextView) {
|
||||
if textView == additionsCell?.contentTextView {
|
||||
tableData[additionsSection][0][PasswordEditorCellKey.content] = additionsCell?.getContent()
|
||||
}
|
||||
}
|
||||
|
||||
func textFieldDidBeginEditing(_ textField: UITextField) {
|
||||
if textField == fillPasswordCell?.contentTextField {
|
||||
// show password generation settings automatically
|
||||
showPasswordSettings()
|
||||
}
|
||||
}
|
||||
|
||||
func getNameURL() -> (String, URL) {
|
||||
let encodedName = (nameCell?.getContent()?.stringByAddingPercentEncodingForRFC3986())!
|
||||
let name = URL(string: encodedName)!.lastPathComponent
|
||||
|
|
@ -375,6 +319,54 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - FillPasswordTableViewCellDelegate
|
||||
extension PasswordEditorTableViewController: FillPasswordTableViewCellDelegate {
|
||||
|
||||
// generate password, copy to pasteboard, and set the cell
|
||||
// check whether the current password looks like an OTP field
|
||||
func generateAndCopyPassword() {
|
||||
if let currentPassword = fillPasswordCell?.getContent(), Constants.isOtpRelated(line: currentPassword) {
|
||||
let alert = UIAlertController(title: "Overwrite?".localize(), message: "OverwriteOtpConfiguration?".localize(), preferredStyle: UIAlertController.Style.alert)
|
||||
alert.addAction(UIAlertAction(title: "Yes".localize(), style: UIAlertAction.Style.destructive, handler: {_ in
|
||||
self.generateAndCopyPasswordNoOtpCheck()
|
||||
}))
|
||||
alert.addAction(UIAlertAction(title: "Cancel".localize(), style: UIAlertAction.Style.cancel, handler: nil))
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
} else {
|
||||
self.generateAndCopyPasswordNoOtpCheck()
|
||||
}
|
||||
}
|
||||
|
||||
// show/hide password settings (e.g., the length slider)
|
||||
func showHidePasswordSettings() {
|
||||
hidePasswordSettings = !hidePasswordSettings
|
||||
tableView.reloadSections([passwordSection], with: .fade)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - PasswordSettingSliderTableViewCellDelegate
|
||||
extension PasswordEditorTableViewController: PasswordSettingSliderTableViewCellDelegate {}
|
||||
|
||||
// MARK: - QRScannerControllerDelegate
|
||||
extension PasswordEditorTableViewController: QRScannerControllerDelegate {
|
||||
|
||||
func checkScannedOutput(line: String) -> (accept: Bool, message: String) {
|
||||
if let url = URL(string: line), let _ = Token(url: url) {
|
||||
return (accept: true, message: "ValidTokenUrl".localize())
|
||||
} else {
|
||||
return (accept: false, message: "InvalidTokenUrl".localize())
|
||||
}
|
||||
}
|
||||
|
||||
func handleScannedOutput(line: String) {
|
||||
insertScannedOTPFields(line)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SFSafariViewControllerDelegate
|
||||
extension PasswordEditorTableViewController: SFSafariViewControllerDelegate {
|
||||
|
||||
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
|
||||
let copiedLinesSplit = UIPasteboard.general.string?.components(separatedBy: CharacterSet.whitespacesAndNewlines).filter({ !$0.isEmpty })
|
||||
|
|
@ -395,3 +387,36 @@ class PasswordEditorTableViewController: UITableViewController, FillPasswordTabl
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITextFieldDelegate
|
||||
extension PasswordEditorTableViewController: UITextFieldDelegate {
|
||||
|
||||
// update tableData so to make sure reloadData() works correctly
|
||||
func textFieldDidEndEditing(_ textField: UITextField) {
|
||||
if textField == nameCell?.contentTextField {
|
||||
tableData[nameSection][0][PasswordEditorCellKey.content] = nameCell?.getContent()
|
||||
} else if textField == fillPasswordCell?.contentTextField {
|
||||
if let plainPassword = fillPasswordCell?.getContent() {
|
||||
tableData[passwordSection][0][PasswordEditorCellKey.content] = plainPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func textFieldDidBeginEditing(_ textField: UITextField) {
|
||||
if textField == fillPasswordCell?.contentTextField {
|
||||
// show password generation settings automatically
|
||||
showPasswordSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITextViewDelegate
|
||||
extension PasswordEditorTableViewController: UITextViewDelegate {
|
||||
|
||||
// update tableData so to make sure reloadData() works correctly
|
||||
func textViewDidEndEditing(_ textView: UITextView) {
|
||||
if textView == additionsCell?.contentTextView {
|
||||
tableData[additionsSection][0][PasswordEditorCellKey.content] = additionsCell?.getContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@
|
|||
"Random" = "Zufällig";
|
||||
"RandomString" = "Zufällige Zeichenkette";
|
||||
"ApplesKeychainStyle" = "Apples Keychain-Style";
|
||||
"XKCD" = "XKCD";
|
||||
"XKCDStyle" = "XKCD-Style";
|
||||
|
||||
// Git
|
||||
"FailedToFetchPasswords" = "Abrufen von Passwörtern fehlgeschlagen";
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ public enum GitAuthenticationMethod: String, DefaultsSerializable {
|
|||
case password, key
|
||||
}
|
||||
|
||||
extension SearchBarScope: DefaultsSerializable {}
|
||||
extension PasswordGeneratorFlavor: DefaultsSerializable {}
|
||||
|
||||
public extension DefaultsKeys {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// PasswordGeneratorFlavour.swift
|
||||
// PasswordGeneratorFlavor.swift
|
||||
// passKit
|
||||
//
|
||||
// Created by Danny Moesch on 28.11.18.
|
||||
|
|
@ -13,6 +13,20 @@ public enum PasswordGeneratorFlavor: String {
|
|||
case random = "Random"
|
||||
case xkcd = "XKCD"
|
||||
|
||||
private static let words: [String] = {
|
||||
let bundle = Bundle(identifier: Globals.passKitBundleIdentifier)!
|
||||
return ["eff_long_wordlist", "eff_short_wordlist"]
|
||||
.map { name -> String in
|
||||
guard let asset = NSDataAsset(name: name, bundle: bundle),
|
||||
let data = String(data: asset.data, encoding: .utf8) else {
|
||||
return ""
|
||||
}
|
||||
return data
|
||||
}
|
||||
.joined(separator: "\n")
|
||||
.splitByNewline()
|
||||
}()
|
||||
|
||||
public var localized: String {
|
||||
return rawValue.localize()
|
||||
}
|
||||
|
|
@ -44,9 +58,9 @@ public enum PasswordGeneratorFlavor: String {
|
|||
case .apple:
|
||||
return Keychain.generatePassword()
|
||||
case .random:
|
||||
return PasswordGeneratorFlavor.generateRandom(length: length)
|
||||
return Self.generateRandom(length: length)
|
||||
case .xkcd:
|
||||
return PasswordGeneratorFlavor.generateXKCD(length: length)
|
||||
return Self.generateXKCD(length: length)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,22 +70,6 @@ public enum PasswordGeneratorFlavor: String {
|
|||
}
|
||||
|
||||
private static func generateXKCD(length: Int) -> String {
|
||||
// Get the word list
|
||||
let bundle = Bundle(identifier: Globals.passKitBundleIdentifier)!
|
||||
let wordlistNames = [
|
||||
"eff_long_wordlist",
|
||||
"eff_short_wordlist"
|
||||
]
|
||||
let data = wordlistNames.map{ name -> String in
|
||||
guard let asset = NSDataAsset(name: name, bundle: bundle),
|
||||
let data = String(data: asset.data, encoding: .utf8) else {
|
||||
return ""
|
||||
}
|
||||
return data
|
||||
}
|
||||
let words = data.joined(separator: "\n").splitByNewline()
|
||||
|
||||
// Generate a password
|
||||
let delimiters = "0123456789!@#$%^&*_+-="
|
||||
var password = ""
|
||||
(0..<length).forEach { _ in
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import SwiftyUserDefaults
|
||||
|
||||
public enum SearchBarScope: Int, CaseIterable, DefaultsSerializable {
|
||||
public enum SearchBarScope: Int {
|
||||
case current
|
||||
case all
|
||||
|
||||
|
|
@ -21,3 +21,5 @@ public enum SearchBarScope: Int, CaseIterable, DefaultsSerializable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SearchBarScope: CaseIterable {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue