Rewrite AutoFill extension
This commit is contained in:
parent
7e034d9c99
commit
40ac070232
13 changed files with 609 additions and 216 deletions
|
|
@ -101,8 +101,15 @@
|
|||
556EC3DA22335D3400934F9C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30C25DBF21F3599E00BB27BB /* InfoPlist.strings */; };
|
||||
556EC3DB22335D3D00934F9C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30C25DBF21F3599E00BB27BB /* InfoPlist.strings */; };
|
||||
8BA607EB4C9C8258741AC18C /* Pods_passExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14E955B67C88672AA3A40BA0 /* Pods_passExtension.framework */; };
|
||||
9A55C158259E785600FA8FD9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DC917BDD1E2E8231000FDF54 /* Assets.xcassets */; };
|
||||
9A55C15F259E785700FA8FD9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DC917BDD1E2E8231000FDF54 /* Assets.xcassets */; };
|
||||
9A55C185259E8C5600FA8FD9 /* PasswordsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A55C184259E8C5600FA8FD9 /* PasswordsViewController.swift */; };
|
||||
9A652414244BB33300DA0A41 /* UIAlertActionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A652413244BB33300DA0A41 /* UIAlertActionExtension.swift */; };
|
||||
9A8A8387402FCCCECB1232A4 /* Pods_passKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B2B2F844061EFA534FE9506 /* Pods_passKitTests.framework */; };
|
||||
9A8F9EBD259EA4C50027CE15 /* PasswordsTableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9EBC259EA4C50027CE15 /* PasswordsTableDataSource.swift */; };
|
||||
9A8F9ECC259ECB410027CE15 /* PasswordSelectionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9ECB259ECB410027CE15 /* PasswordSelectionDelegate.swift */; };
|
||||
9A8F9EE2259EDD520027CE15 /* PasswordTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9EE1259EDD520027CE15 /* PasswordTableViewCell.swift */; };
|
||||
9A8F9EF0259EE01A0027CE15 /* PasswordDecryptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A8F9EEF259EE01A0027CE15 /* PasswordDecryptor.swift */; };
|
||||
9ADC954124418A5F0005402E /* PasswordStoreTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADC954024418A5F0005402E /* PasswordStoreTest.swift */; };
|
||||
A20691F41F2A3D0E0096483D /* SecurePasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20691F31F2A3D0E0096483D /* SecurePasteboard.swift */; };
|
||||
A217ACE41E9BBBBD00A1A6CF /* GitConfigSettingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A217ACE31E9BBBBD00A1A6CF /* GitConfigSettingsTableViewController.swift */; };
|
||||
|
|
@ -363,7 +370,12 @@
|
|||
9A1EF0B424C50E780074FEAC /* passBetaAutoFillExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBetaAutoFillExtension.entitlements; sourceTree = "<group>"; };
|
||||
9A1EF0B524C50EE00074FEAC /* passBetaExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBetaExtension.entitlements; sourceTree = "<group>"; };
|
||||
9A1EF0B624C50FEA0074FEAC /* passBetaShortcuts.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = passBetaShortcuts.entitlements; sourceTree = "<group>"; };
|
||||
9A55C184259E8C5600FA8FD9 /* PasswordsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordsViewController.swift; sourceTree = "<group>"; };
|
||||
9A652413244BB33300DA0A41 /* UIAlertActionExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertActionExtension.swift; sourceTree = "<group>"; };
|
||||
9A8F9EBC259EA4C50027CE15 /* PasswordsTableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordsTableDataSource.swift; sourceTree = "<group>"; };
|
||||
9A8F9ECB259ECB410027CE15 /* PasswordSelectionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordSelectionDelegate.swift; sourceTree = "<group>"; };
|
||||
9A8F9EE1259EDD520027CE15 /* PasswordTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordTableViewCell.swift; sourceTree = "<group>"; };
|
||||
9A8F9EEF259EE01A0027CE15 /* PasswordDecryptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordDecryptor.swift; sourceTree = "<group>"; };
|
||||
9ADC954024418A5F0005402E /* PasswordStoreTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordStoreTest.swift; sourceTree = "<group>"; };
|
||||
A20691F31F2A3D0E0096483D /* SecurePasteboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecurePasteboard.swift; sourceTree = "<group>"; };
|
||||
A217ACE31E9BBBBD00A1A6CF /* GitConfigSettingsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = GitConfigSettingsTableViewController.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
|
|
@ -689,6 +701,31 @@
|
|||
path = Crypto;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9A8F9EBB259EA4A80027CE15 /* Services */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9A8F9EBC259EA4C50027CE15 /* PasswordsTableDataSource.swift */,
|
||||
9A8F9EEF259EE01A0027CE15 /* PasswordDecryptor.swift */,
|
||||
);
|
||||
path = Services;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9A8F9ECA259ECAFC0027CE15 /* Protocols */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9A8F9ECB259ECB410027CE15 /* PasswordSelectionDelegate.swift */,
|
||||
);
|
||||
path = Protocols;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9A8F9EE0259EDD390027CE15 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9A8F9EE1259EDD520027CE15 /* PasswordTableViewCell.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A2168A801EFD431A005EA873 /* Controllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -709,6 +746,9 @@
|
|||
A239F5972158C08C00576CBF /* passAutoFillExtension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9A8F9EE0259EDD390027CE15 /* Views */,
|
||||
9A8F9ECA259ECAFC0027CE15 /* Protocols */,
|
||||
9A8F9EBB259EA4A80027CE15 /* Services */,
|
||||
A239F5A921591E3700576CBF /* Controllers */,
|
||||
A239F59E2158C08C00576CBF /* passAutoFillExtension.entitlements */,
|
||||
9A1EF0B424C50E780074FEAC /* passBetaAutoFillExtension.entitlements */,
|
||||
|
|
@ -722,6 +762,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
30697C5221F63E0B0064FCAC /* CredentialProviderViewController.swift */,
|
||||
9A55C184259E8C5600FA8FD9 /* PasswordsViewController.swift */,
|
||||
30697C5121F63E0B0064FCAC /* PasscodeExtensionDisplay.swift */,
|
||||
);
|
||||
path = Controllers;
|
||||
|
|
@ -1231,6 +1272,7 @@
|
|||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9A55C15F259E785700FA8FD9 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -1242,6 +1284,7 @@
|
|||
556EC3D322335C5F00934F9C /* Localizable.strings in Resources */,
|
||||
556EC3D422335C5F00934F9C /* Localizable.stringsdict in Resources */,
|
||||
A239F59C2158C08C00576CBF /* MainInterface.storyboard in Resources */,
|
||||
9A55C158259E785600FA8FD9 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -1508,8 +1551,13 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9A8F9EE2259EDD520027CE15 /* PasswordTableViewCell.swift in Sources */,
|
||||
9A8F9ECC259ECB410027CE15 /* PasswordSelectionDelegate.swift in Sources */,
|
||||
30697C5421F63E0B0064FCAC /* CredentialProviderViewController.swift in Sources */,
|
||||
9A55C185259E8C5600FA8FD9 /* PasswordsViewController.swift in Sources */,
|
||||
9A8F9EBD259EA4C50027CE15 /* PasswordsTableDataSource.swift in Sources */,
|
||||
30697C5321F63E0B0064FCAC /* PasscodeExtensionDisplay.swift in Sources */,
|
||||
9A8F9EF0259EE01A0027CE15 /* PasswordDecryptor.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -1886,7 +1934,7 @@
|
|||
"$(SDKROOT)/usr/include/libxml2",
|
||||
"$(SRCROOT)/Carthage/Build/iOS/ObjectiveGit.framework/Headers/",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_SWIFT_FLAGS = "-D BETA";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforiosbeta;
|
||||
|
|
@ -2459,7 +2507,7 @@
|
|||
"$(SDKROOT)/usr/include/libxml2",
|
||||
"$(SRCROOT)/Carthage/Build/iOS/ObjectiveGit.framework/Headers/",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforios;
|
||||
|
|
@ -2524,7 +2572,7 @@
|
|||
"$(SDKROOT)/usr/include/libxml2",
|
||||
"$(SRCROOT)/Carthage/Build/iOS/ObjectiveGit.framework/Headers/",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforios;
|
||||
PRODUCT_NAME = Pass;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1230"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "A239F5942158C08B00576CBF"
|
||||
BuildableName = "passAutoFillExtension.appex"
|
||||
BlueprintName = "passAutoFillExtension"
|
||||
ReferencedContainer = "container:pass.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
|
||||
BuildableName = "Pass.app"
|
||||
BlueprintName = "pass"
|
||||
ReferencedContainer = "container:pass.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
|
||||
BuildableName = "Pass.app"
|
||||
BlueprintName = "pass"
|
||||
ReferencedContainer = "container:pass.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
|
||||
BuildableName = "Pass.app"
|
||||
BlueprintName = "pass"
|
||||
ReferencedContainer = "container:pass.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
100
pass.xcodeproj/xcshareddata/xcschemes/passExtension.xcscheme
Normal file
100
pass.xcodeproj/xcshareddata/xcschemes/passExtension.xcscheme
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1230"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "A26700231EEC466A00176B8A"
|
||||
BuildableName = "passExtension.appex"
|
||||
BlueprintName = "passExtension"
|
||||
ReferencedContainer = "container:pass.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
|
||||
BuildableName = "Pass.app"
|
||||
BlueprintName = "pass"
|
||||
ReferencedContainer = "container:pass.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<RemoteRunnable
|
||||
runnableDebuggingMode = "1"
|
||||
BundleIdentifier = "com.apple.mobilesafari"
|
||||
RemotePath = "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/Applications/MobileSafari.app">
|
||||
</RemoteRunnable>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
|
||||
BuildableName = "Pass.app"
|
||||
BlueprintName = "pass"
|
||||
ReferencedContainer = "container:pass.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
|
||||
BuildableName = "Pass.app"
|
||||
BlueprintName = "pass"
|
||||
ReferencedContainer = "container:pass.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
96
pass.xcodeproj/xcshareddata/xcschemes/passShortcuts.xcscheme
Normal file
96
pass.xcodeproj/xcshareddata/xcschemes/passShortcuts.xcscheme
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1230"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "30A69944240EED5E00B7D967"
|
||||
BuildableName = "passShortcuts.appex"
|
||||
BlueprintName = "passShortcuts"
|
||||
ReferencedContainer = "container:pass.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
|
||||
BuildableName = "Pass.app"
|
||||
BlueprintName = "pass"
|
||||
ReferencedContainer = "container:pass.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
|
||||
BuildableName = "Pass.app"
|
||||
BlueprintName = "pass"
|
||||
ReferencedContainer = "container:pass.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "DC917BD21E2E8231000FDF54"
|
||||
BuildableName = "Pass.app"
|
||||
BlueprintName = "pass"
|
||||
ReferencedContainer = "container:pass.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>PreviewsEnabled</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -1,31 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Aeb-0C-dDR">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="7M4-H6-S4B">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Password Store-->
|
||||
<scene sceneID="Uma-9u-xWV">
|
||||
<objects>
|
||||
<viewController id="Xki-Si-B7m" customClass="CredentialProviderViewController" customModule="passAutoFillExtension" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController id="Xki-Si-B7m" customClass="PasswordsViewController" customModule="passAutoFillExtension" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="BuU-Ak-iZz">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="XmI-l4-SgT">
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="XmI-l4-SgT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<searchBar key="tableHeaderView" contentMode="redraw" text="" id="MmN-WX-sur">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="56"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no"/>
|
||||
</searchBar>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="passwordTableViewCell" textLabel="U0x-8f-AET" detailTextLabel="kY1-Ac-C3d" style="IBUITableViewCellStyleValue1" id="fXA-SG-IOe" customClass="PasswordDetailTitleTableViewCell" customModule="pass">
|
||||
<rect key="frame" x="0.0" y="84" width="375" height="43.5"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="passwordTableViewCell" textLabel="U0x-8f-AET" detailTextLabel="kY1-Ac-C3d" style="IBUITableViewCellStyleValue1" id="fXA-SG-IOe" customClass="PasswordTableViewCell" customModule="passAutoFillExtension" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="28" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="fXA-SG-IOe" id="KPa-Az-i6V">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
|
|
@ -52,34 +50,52 @@
|
|||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="XmI-l4-SgT" secondAttribute="bottom" id="5FB-pZ-UB8"/>
|
||||
<constraint firstAttribute="trailing" secondItem="XmI-l4-SgT" secondAttribute="trailing" id="UU8-e6-4iA"/>
|
||||
<constraint firstItem="XmI-l4-SgT" firstAttribute="top" secondItem="BuU-Ak-iZz" secondAttribute="top" id="cXC-1s-cMI"/>
|
||||
<constraint firstItem="XmI-l4-SgT" firstAttribute="leading" secondItem="BuU-Ak-iZz" secondAttribute="leading" id="nvS-L5-BHk"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" title="Password Store" id="apM-bN-eca">
|
||||
<navigationItem key="navigationItem" title="Password Store" prompt="url" id="apM-bN-eca">
|
||||
<barButtonItem key="leftBarButtonItem" style="plain" systemItem="cancel" id="zCC-lm-1xr">
|
||||
<connections>
|
||||
<action selector="cancel:" destination="Xki-Si-B7m" id="3tK-C9-LKo"/>
|
||||
<action selector="cancel:" destination="Xki-Si-B7m" id="CgJ-49-QwT"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="searchBar" destination="MmN-WX-sur" id="KZk-g3-9Ox"/>
|
||||
<outlet property="tableView" destination="XmI-l4-SgT" id="2yd-Uj-96H"/>
|
||||
<outlet property="tableView" destination="XmI-l4-SgT" id="9k1-t7-Xnc"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="RwB-HB-TSk" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="992.79999999999995" y="26.53673163418291"/>
|
||||
</scene>
|
||||
<!--Credential Provider View Controller-->
|
||||
<scene sceneID="Ebu-3J-rOU">
|
||||
<objects>
|
||||
<viewController id="7M4-H6-S4B" customClass="CredentialProviderViewController" customModule="passAutoFillExtension" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="12l-99-QNG">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<containerView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jMU-nF-QHX">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<connections>
|
||||
<segue destination="Aeb-0C-dDR" kind="embed" id="9kf-tv-qXf"/>
|
||||
</connections>
|
||||
</containerView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="e5d-UG-xz5"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="9jE-qW-0uN" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-946.39999999999998" y="26.53673163418291"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="mg9-JA-x9T">
|
||||
<objects>
|
||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="Aeb-0C-dDR" sceneMemberID="viewController">
|
||||
<toolbarItems/>
|
||||
<navigationItem key="navigationItem" id="d0b-i7-rXf"/>
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" id="RMW-As-go6">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
|
|
@ -94,4 +110,9 @@
|
|||
<point key="canvasLocation" x="53.600000000000001" y="26.53673163418291"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
|
|
|||
|
|
@ -9,208 +9,44 @@
|
|||
import AuthenticationServices
|
||||
import passKit
|
||||
|
||||
class CredentialProviderViewController: ASCredentialProviderViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate {
|
||||
@IBOutlet var searchBar: UISearchBar!
|
||||
@IBOutlet var tableView: UITableView!
|
||||
|
||||
private let passwordStore = PasswordStore.shared
|
||||
private let keychain = AppKeychain.shared
|
||||
|
||||
private var searchActive = false
|
||||
private var passwordsTableEntries: [PasswordTableEntry] = []
|
||||
private var filteredPasswordsTableEntries: [PasswordTableEntry] = []
|
||||
|
||||
private lazy var passcodelock: PasscodeExtensionDisplay = {
|
||||
let passcodelock = PasscodeExtensionDisplay(extensionContext: self.extensionContext)
|
||||
return passcodelock
|
||||
}()
|
||||
|
||||
/*
|
||||
Prepare your UI to list available credentials for the user to choose from. The items in
|
||||
'serviceIdentifiers' describe the service the user is logging in to, so your extension can
|
||||
prioritize the most relevant credentials in the list.
|
||||
*/
|
||||
override func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier]) {
|
||||
// clean up the search bar
|
||||
guard !serviceIdentifiers.isEmpty else {
|
||||
searchBar.text = ""
|
||||
searchBar.becomeFirstResponder()
|
||||
searchBarSearchButtonClicked(searchBar)
|
||||
return
|
||||
class CredentialProviderViewController: ASCredentialProviderViewController {
|
||||
var passcodelock: PasscodeExtensionDisplay {
|
||||
PasscodeExtensionDisplay(extensionContext: self.extensionContext)
|
||||
}
|
||||
|
||||
// get the domain
|
||||
var identifier = serviceIdentifiers[0].identifier
|
||||
if !identifier.hasPrefix("http://"), !identifier.hasPrefix("https://") {
|
||||
identifier = "http://" + identifier
|
||||
}
|
||||
let url = URL(string: identifier)?.host ?? ""
|
||||
|
||||
// "click" search
|
||||
searchBar.text = url
|
||||
searchBar.becomeFirstResponder()
|
||||
searchBarSearchButtonClicked(searchBar)
|
||||
var embeddedNavigationController: UINavigationController {
|
||||
children.first as! UINavigationController
|
||||
}
|
||||
|
||||
/*
|
||||
Implement this method if your extension support
|
||||
s showing credentials in the QuickType bar.
|
||||
When the user selects a credential from your app, this method will be called with the
|
||||
ASPasswordCredentialIdentity your app has previously saved to the ASCredentialIdentityStore.
|
||||
Provide the password by completing the extension request with the associated ASPasswordCredential.
|
||||
If using the credential would require showing custom UI for authenticating the user, cancel
|
||||
the request with error code ASExtensionError.userInteractionRequired.
|
||||
|
||||
override func provideCredentialWithoutUserInteraction(for credentialIdentity: ASPasswordCredentialIdentity) {
|
||||
let databaseIsUnlocked = true
|
||||
if (databaseIsUnlocked) {
|
||||
let passwordCredential = ASPasswordCredential(user: "j_appleseed", password: "apple1234")
|
||||
self.extensionContext.completeRequest(withSelectedCredential: passwordCredential, completionHandler: nil)
|
||||
} else {
|
||||
self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code:ASExtensionError.userInteractionRequired.rawValue))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
Implement this method if provideCredentialWithoutUserInteraction(for:) can fail with
|
||||
ASExtensionError.userInteractionRequired. In this case, the system may present your extension's
|
||||
UI and call this method. Show appropriate UI for authenticating the user then provide the password
|
||||
by completing the extension request with the associated ASPasswordCredential.
|
||||
|
||||
override func prepareInterfaceToProvideCredential(for credentialIdentity: ASPasswordCredentialIdentity) {
|
||||
}
|
||||
*/
|
||||
|
||||
@IBAction
|
||||
private func cancel(_: AnyObject?) {
|
||||
extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code: ASExtensionError.userCanceled.rawValue))
|
||||
self.dismiss(animated: true)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
passcodelock.presentPasscodeLockIfNeeded(self)
|
||||
var passwordsViewController: PasswordsViewController {
|
||||
embeddedNavigationController.viewControllers.first as! PasswordsViewController
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
passcodelock.presentPasscodeLockIfNeeded(self)
|
||||
|
||||
// prepare
|
||||
searchBar.delegate = self
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
tableView.register(UINib(nibName: "PasswordWithFolderTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordWithFolderTableViewCell")
|
||||
|
||||
// initialize table entries
|
||||
initPasswordsTableEntries()
|
||||
let passwordsTableEntries = PasswordStore.shared.fetchPasswordEntityCoreData(withDir: false).compactMap { PasswordTableEntry($0) }
|
||||
let dataSource = PasswordsTableDataSource(entries: passwordsTableEntries)
|
||||
passwordsViewController.dataSource = dataSource
|
||||
passwordsViewController.selectionDelegate = self
|
||||
}
|
||||
|
||||
private func initPasswordsTableEntries() {
|
||||
filteredPasswordsTableEntries.removeAll()
|
||||
|
||||
let passwordEntities = passwordStore.fetchPasswordEntityCoreData(withDir: false)
|
||||
passwordsTableEntries = passwordEntities.compactMap {
|
||||
PasswordTableEntry($0)
|
||||
override func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier]) {
|
||||
let url = serviceIdentifiers.first.flatMap { URL(string: $0.identifier) }
|
||||
passwordsViewController.navigationItem.prompt = url?.host
|
||||
}
|
||||
}
|
||||
|
||||
// define cell contents
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath)
|
||||
let entry = getPasswordEntry(by: indexPath)
|
||||
if entry.passwordEntity.synced {
|
||||
cell.textLabel?.text = entry.title
|
||||
} else {
|
||||
cell.textLabel?.text = "↻ \(entry.title)"
|
||||
}
|
||||
cell.accessoryType = .none
|
||||
cell.detailTextLabel?.font = UIFont.preferredFont(forTextStyle: .footnote)
|
||||
cell.detailTextLabel?.text = entry.categoryText
|
||||
return cell
|
||||
}
|
||||
extension CredentialProviderViewController: PasswordSelectionDelegate {
|
||||
func selected(password: PasswordTableEntry) {
|
||||
let passwordEntity = password.passwordEntity
|
||||
|
||||
// select row -> extension returns (with username and password)
|
||||
func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let entry = getPasswordEntry(by: indexPath)
|
||||
|
||||
guard PGPAgent.shared.isPrepared else {
|
||||
Utils.alert(title: "CannotCopyPassword".localize(), message: "PgpKeyNotSet.".localize(), controller: self, completion: nil)
|
||||
return
|
||||
}
|
||||
|
||||
let passwordEntity = entry.passwordEntity
|
||||
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
||||
decryptPassword(passwordEntity: passwordEntity)
|
||||
}
|
||||
|
||||
private func decryptPassword(passwordEntity: PasswordEntity, keyID: String? = nil) {
|
||||
DispatchQueue.global(qos: .userInteractive).async {
|
||||
do {
|
||||
let requestPGPKeyPassphrase = Utils.createRequestPGPKeyPassphraseHandler(controller: self)
|
||||
let decryptedPassword = try self.passwordStore.decrypt(passwordEntity: passwordEntity, keyID: keyID, requestPGPKeyPassphrase: requestPGPKeyPassphrase)
|
||||
|
||||
let username = decryptedPassword.getUsernameForCompletion()
|
||||
let password = decryptedPassword.password
|
||||
DispatchQueue.main.async {
|
||||
decryptPassword(in: self, with: passwordEntity) { password in
|
||||
let username = password.getUsernameForCompletion()
|
||||
let password = password.password
|
||||
let passwordCredential = ASPasswordCredential(user: username, password: password)
|
||||
self.extensionContext.completeRequest(withSelectedCredential: passwordCredential)
|
||||
}
|
||||
} catch let AppError.pgpPrivateKeyNotFound(keyID: key) {
|
||||
DispatchQueue.main.async {
|
||||
let alert = UIAlertController(title: "CannotShowPassword".localize(), message: AppError.pgpPrivateKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction.cancelAndPopView(controller: self))
|
||||
let selectKey = UIAlertAction.selectKey(controller: self) { action in
|
||||
self.decryptPassword(passwordEntity: passwordEntity, keyID: action.title)
|
||||
}
|
||||
alert.addAction(selectKey)
|
||||
|
||||
self.present(alert, animated: true)
|
||||
}
|
||||
} catch {
|
||||
DispatchQueue.main.async {
|
||||
Utils.alert(title: "CannotCopyPassword".localize(), message: error.localizedDescription, controller: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func numberOfSectionsInTableView(tableView _: UITableView) -> Int {
|
||||
1
|
||||
}
|
||||
|
||||
func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int {
|
||||
if searchActive {
|
||||
return filteredPasswordsTableEntries.count
|
||||
}
|
||||
return passwordsTableEntries.count
|
||||
}
|
||||
|
||||
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
|
||||
searchBar.text = ""
|
||||
searchActive = false
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
|
||||
if let searchText = searchBar.text, searchText.isEmpty == false {
|
||||
filteredPasswordsTableEntries = passwordsTableEntries.filter { $0.match(searchText) }
|
||||
searchActive = true
|
||||
} else {
|
||||
searchActive = false
|
||||
}
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, textDidChange _: String) {
|
||||
searchBarSearchButtonClicked(searchBar)
|
||||
}
|
||||
|
||||
private func getPasswordEntry(by indexPath: IndexPath) -> PasswordTableEntry {
|
||||
if searchActive {
|
||||
return filteredPasswordsTableEntries[indexPath.row]
|
||||
} else {
|
||||
return passwordsTableEntries[indexPath.row]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ import passKit
|
|||
|
||||
// cancel means cancel the extension
|
||||
class PasscodeLockViewControllerForExtension: PasscodeLockViewController {
|
||||
var originalExtensionContest: ASCredentialProviderExtensionContext?
|
||||
var originalExtensionContext: ASCredentialProviderExtensionContext!
|
||||
|
||||
convenience init(extensionContext: ASCredentialProviderExtensionContext?) {
|
||||
convenience init(extensionContext: ASCredentialProviderExtensionContext) {
|
||||
self.init()
|
||||
self.originalExtensionContest = extensionContext
|
||||
self.originalExtensionContext = extensionContext
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
|
@ -27,7 +27,7 @@ class PasscodeLockViewControllerForExtension: PasscodeLockViewController {
|
|||
|
||||
@objc
|
||||
func cancelExtension() {
|
||||
originalExtensionContest?.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code: ASExtensionError.userCanceled.rawValue))
|
||||
originalExtensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code: ASExtensionError.userCanceled.rawValue))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ class PasscodeExtensionDisplay {
|
|||
private let passcodeLockVC: PasscodeLockViewControllerForExtension
|
||||
private let extensionContext: ASCredentialProviderExtensionContext?
|
||||
|
||||
init(extensionContext: ASCredentialProviderExtensionContext?) {
|
||||
init(extensionContext: ASCredentialProviderExtensionContext) {
|
||||
self.extensionContext = extensionContext
|
||||
self.passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext)
|
||||
passcodeLockVC.dismissCompletionCallback = { [weak self] in
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// PasswordsViewController.swift
|
||||
// passAutoFillExtension
|
||||
//
|
||||
// Created by Sun, Mingshen on 12/31/20.
|
||||
// Copyright © 2020 Bob Sun. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import AuthenticationServices
|
||||
import passKit
|
||||
|
||||
class PasswordsViewController: UIViewController {
|
||||
@IBOutlet var tableView: UITableView!
|
||||
|
||||
var dataSource: PasswordsTableDataSource!
|
||||
weak var selectionDelegate: PasswordSelectionDelegate?
|
||||
|
||||
var searchController: UISearchController {
|
||||
let uiSearchController = UISearchController(searchResultsController: nil)
|
||||
uiSearchController.searchBar.isTranslucent = true
|
||||
uiSearchController.obscuresBackgroundDuringPresentation = false
|
||||
uiSearchController.searchBar.sizeToFit()
|
||||
if #available(iOS 13.0, *) {
|
||||
uiSearchController.searchBar.searchTextField.clearButtonMode = .whileEditing
|
||||
}
|
||||
return uiSearchController
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
navigationItem.searchController = searchController
|
||||
navigationItem.hidesSearchBarWhenScrolling = false
|
||||
|
||||
searchController.searchBar.delegate = self
|
||||
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = dataSource
|
||||
}
|
||||
|
||||
@IBAction
|
||||
private func cancel(_: AnyObject?) {
|
||||
self.extensionContext?.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code: ASExtensionError.userCanceled.rawValue))
|
||||
self.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
extension PasswordsViewController: UISearchBarDelegate {
|
||||
func searchBar(_: UISearchBar, textDidChange searchText: String) {
|
||||
dataSource.showTableEntries(matching: searchText)
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
|
||||
searchBar.resignFirstResponder()
|
||||
}
|
||||
|
||||
func searchBarCancelButtonClicked(_: UISearchBar) {
|
||||
dataSource.showTableEntries(matching: "")
|
||||
tableView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
extension PasswordsViewController: UITableViewDelegate {
|
||||
func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
let entry = dataSource.filteredPasswordsTableEntries[indexPath.row]
|
||||
|
||||
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
|
||||
self.selectionDelegate?.selected(password: entry)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// PasswordSelectionDelegate.swift
|
||||
// passAutoFillExtension
|
||||
//
|
||||
// Created by Sun, Mingshen on 12/31/20.
|
||||
// Copyright © 2020 Bob Sun. All rights reserved.
|
||||
//
|
||||
|
||||
import passKit
|
||||
|
||||
protocol PasswordSelectionDelegate: AnyObject {
|
||||
func selected(password: PasswordTableEntry)
|
||||
}
|
||||
38
passAutoFillExtension/Services/PasswordDecryptor.swift
Normal file
38
passAutoFillExtension/Services/PasswordDecryptor.swift
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// PasswordDecryptor.swift
|
||||
// passAutoFillExtension
|
||||
//
|
||||
// Created by Sun, Mingshen on 12/31/20.
|
||||
// Copyright © 2020 Bob Sun. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import passKit
|
||||
|
||||
func decryptPassword(in controller: UIViewController, with passwordEntity: PasswordEntity, using keyID: String? = nil, completion: @escaping ((Password) -> Void)) {
|
||||
DispatchQueue.global(qos: .userInteractive).async {
|
||||
do {
|
||||
let requestPGPKeyPassphrase = Utils.createRequestPGPKeyPassphraseHandler(controller: controller)
|
||||
let decryptedPassword = try PasswordStore.shared.decrypt(passwordEntity: passwordEntity, keyID: keyID, requestPGPKeyPassphrase: requestPGPKeyPassphrase)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
completion(decryptedPassword)
|
||||
}
|
||||
} catch let AppError.pgpPrivateKeyNotFound(keyID: key) {
|
||||
DispatchQueue.main.async {
|
||||
let alert = UIAlertController(title: "CannotShowPassword".localize(), message: AppError.pgpPrivateKeyNotFound(keyID: key).localizedDescription, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction.cancelAndPopView(controller: controller))
|
||||
let selectKey = UIAlertAction.selectKey(controller: controller) { action in
|
||||
decryptPassword(in: controller, with: passwordEntity, using: action.title, completion: completion)
|
||||
}
|
||||
alert.addAction(selectKey)
|
||||
|
||||
controller.present(alert, animated: true)
|
||||
}
|
||||
} catch {
|
||||
DispatchQueue.main.async {
|
||||
Utils.alert(title: "CannotCopyPassword".localize(), message: error.localizedDescription, controller: controller)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// PasswordsTableDataSource.swift
|
||||
// passAutoFillExtension
|
||||
//
|
||||
// Created by Sun, Mingshen on 12/31/20.
|
||||
// Copyright © 2020 Bob Sun. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import passKit
|
||||
|
||||
class PasswordsTableDataSource: NSObject, UITableViewDataSource {
|
||||
var passwordTableEntries: [PasswordTableEntry]
|
||||
var filteredPasswordsTableEntries: [PasswordTableEntry]
|
||||
|
||||
init(entries: [PasswordTableEntry] = []) {
|
||||
passwordTableEntries = entries
|
||||
filteredPasswordsTableEntries = passwordTableEntries
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
filteredPasswordsTableEntries.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath) as! PasswordTableViewCell
|
||||
|
||||
let entry = filteredPasswordsTableEntries[indexPath.row]
|
||||
cell.configure(with: entry)
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
func showTableEntries(matching text: String) {
|
||||
guard !text.isEmpty else {
|
||||
filteredPasswordsTableEntries = passwordTableEntries
|
||||
return
|
||||
}
|
||||
|
||||
filteredPasswordsTableEntries = passwordTableEntries.filter { $0.match(text) }
|
||||
}
|
||||
}
|
||||
22
passAutoFillExtension/Views/PasswordTableViewCell.swift
Normal file
22
passAutoFillExtension/Views/PasswordTableViewCell.swift
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// PasswordCell.swift
|
||||
// passAutoFillExtension
|
||||
//
|
||||
// Created by Sun, Mingshen on 12/31/20.
|
||||
// Copyright © 2020 Bob Sun. All rights reserved.
|
||||
//
|
||||
|
||||
import passKit
|
||||
|
||||
class PasswordTableViewCell: UITableViewCell {
|
||||
func configure(with entry: PasswordTableEntry) {
|
||||
if entry.passwordEntity.synced {
|
||||
textLabel?.text = entry.title
|
||||
} else {
|
||||
textLabel?.text = "↻ \(entry.title)"
|
||||
}
|
||||
accessoryType = .none
|
||||
detailTextLabel?.font = UIFont.preferredFont(forTextStyle: .footnote)
|
||||
detailTextLabel?.text = entry.categoryText
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue