Rewrite AutoFill extension

This commit is contained in:
Mingshen Sun 2020-12-31 21:46:50 -08:00
parent 7e034d9c99
commit 40ac070232
No known key found for this signature in database
GPG key ID: 1F86BA2052FED3B4
13 changed files with 609 additions and 216 deletions

View file

@ -101,8 +101,15 @@
556EC3DA22335D3400934F9C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30C25DBF21F3599E00BB27BB /* InfoPlist.strings */; }; 556EC3DA22335D3400934F9C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 30C25DBF21F3599E00BB27BB /* InfoPlist.strings */; };
556EC3DB22335D3D00934F9C /* 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 */; }; 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 */; }; 9A652414244BB33300DA0A41 /* UIAlertActionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A652413244BB33300DA0A41 /* UIAlertActionExtension.swift */; };
9A8A8387402FCCCECB1232A4 /* Pods_passKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B2B2F844061EFA534FE9506 /* Pods_passKitTests.framework */; }; 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 */; }; 9ADC954124418A5F0005402E /* PasswordStoreTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ADC954024418A5F0005402E /* PasswordStoreTest.swift */; };
A20691F41F2A3D0E0096483D /* SecurePasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20691F31F2A3D0E0096483D /* SecurePasteboard.swift */; }; A20691F41F2A3D0E0096483D /* SecurePasteboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20691F31F2A3D0E0096483D /* SecurePasteboard.swift */; };
A217ACE41E9BBBBD00A1A6CF /* GitConfigSettingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A217ACE31E9BBBBD00A1A6CF /* GitConfigSettingsTableViewController.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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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; }; 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; path = Crypto;
sourceTree = "<group>"; 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 */ = { A2168A801EFD431A005EA873 /* Controllers */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -709,6 +746,9 @@
A239F5972158C08C00576CBF /* passAutoFillExtension */ = { A239F5972158C08C00576CBF /* passAutoFillExtension */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
9A8F9EE0259EDD390027CE15 /* Views */,
9A8F9ECA259ECAFC0027CE15 /* Protocols */,
9A8F9EBB259EA4A80027CE15 /* Services */,
A239F5A921591E3700576CBF /* Controllers */, A239F5A921591E3700576CBF /* Controllers */,
A239F59E2158C08C00576CBF /* passAutoFillExtension.entitlements */, A239F59E2158C08C00576CBF /* passAutoFillExtension.entitlements */,
9A1EF0B424C50E780074FEAC /* passBetaAutoFillExtension.entitlements */, 9A1EF0B424C50E780074FEAC /* passBetaAutoFillExtension.entitlements */,
@ -722,6 +762,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
30697C5221F63E0B0064FCAC /* CredentialProviderViewController.swift */, 30697C5221F63E0B0064FCAC /* CredentialProviderViewController.swift */,
9A55C184259E8C5600FA8FD9 /* PasswordsViewController.swift */,
30697C5121F63E0B0064FCAC /* PasscodeExtensionDisplay.swift */, 30697C5121F63E0B0064FCAC /* PasscodeExtensionDisplay.swift */,
); );
path = Controllers; path = Controllers;
@ -1231,6 +1272,7 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
9A55C15F259E785700FA8FD9 /* Assets.xcassets in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -1242,6 +1284,7 @@
556EC3D322335C5F00934F9C /* Localizable.strings in Resources */, 556EC3D322335C5F00934F9C /* Localizable.strings in Resources */,
556EC3D422335C5F00934F9C /* Localizable.stringsdict in Resources */, 556EC3D422335C5F00934F9C /* Localizable.stringsdict in Resources */,
A239F59C2158C08C00576CBF /* MainInterface.storyboard in Resources */, A239F59C2158C08C00576CBF /* MainInterface.storyboard in Resources */,
9A55C158259E785600FA8FD9 /* Assets.xcassets in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -1508,8 +1551,13 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
9A8F9EE2259EDD520027CE15 /* PasswordTableViewCell.swift in Sources */,
9A8F9ECC259ECB410027CE15 /* PasswordSelectionDelegate.swift in Sources */,
30697C5421F63E0B0064FCAC /* CredentialProviderViewController.swift in Sources */, 30697C5421F63E0B0064FCAC /* CredentialProviderViewController.swift in Sources */,
9A55C185259E8C5600FA8FD9 /* PasswordsViewController.swift in Sources */,
9A8F9EBD259EA4C50027CE15 /* PasswordsTableDataSource.swift in Sources */,
30697C5321F63E0B0064FCAC /* PasscodeExtensionDisplay.swift in Sources */, 30697C5321F63E0B0064FCAC /* PasscodeExtensionDisplay.swift in Sources */,
9A8F9EF0259EE01A0027CE15 /* PasswordDecryptor.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -1886,7 +1934,7 @@
"$(SDKROOT)/usr/include/libxml2", "$(SDKROOT)/usr/include/libxml2",
"$(SRCROOT)/Carthage/Build/iOS/ObjectiveGit.framework/Headers/", "$(SRCROOT)/Carthage/Build/iOS/ObjectiveGit.framework/Headers/",
); );
IPHONEOS_DEPLOYMENT_TARGET = 10.2; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
OTHER_SWIFT_FLAGS = "-D BETA"; OTHER_SWIFT_FLAGS = "-D BETA";
PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforiosbeta; PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforiosbeta;
@ -2459,7 +2507,7 @@
"$(SDKROOT)/usr/include/libxml2", "$(SDKROOT)/usr/include/libxml2",
"$(SRCROOT)/Carthage/Build/iOS/ObjectiveGit.framework/Headers/", "$(SRCROOT)/Carthage/Build/iOS/ObjectiveGit.framework/Headers/",
); );
IPHONEOS_DEPLOYMENT_TARGET = 10.2; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforios; PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforios;
@ -2524,7 +2572,7 @@
"$(SDKROOT)/usr/include/libxml2", "$(SDKROOT)/usr/include/libxml2",
"$(SRCROOT)/Carthage/Build/iOS/ObjectiveGit.framework/Headers/", "$(SRCROOT)/Carthage/Build/iOS/ObjectiveGit.framework/Headers/",
); );
IPHONEOS_DEPLOYMENT_TARGET = 10.2; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforios; PRODUCT_BUNDLE_IDENTIFIER = me.mssun.passforios;
PRODUCT_NAME = Pass; PRODUCT_NAME = Pass;

View 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 = "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>

View 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>

View 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>

View file

@ -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>

View file

@ -1,31 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/> <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"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--Password Store--> <!--Password Store-->
<scene sceneID="Uma-9u-xWV"> <scene sceneID="Uma-9u-xWV">
<objects> <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"> <view key="view" contentMode="scaleToFill" id="BuU-Ak-iZz">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <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"/> <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"/> <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> <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"> <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="84" width="375" height="43.5"/> <rect key="frame" x="0.0" y="28" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="fXA-SG-IOe" id="KPa-Az-i6V"> <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"/> <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
@ -52,34 +50,52 @@
</tableView> </tableView>
</subviews> </subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <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> </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"> <barButtonItem key="leftBarButtonItem" style="plain" systemItem="cancel" id="zCC-lm-1xr">
<connections> <connections>
<action selector="cancel:" destination="Xki-Si-B7m" id="3tK-C9-LKo"/> <action selector="cancel:" destination="Xki-Si-B7m" id="CgJ-49-QwT"/>
</connections> </connections>
</barButtonItem> </barButtonItem>
</navigationItem> </navigationItem>
<connections> <connections>
<outlet property="searchBar" destination="MmN-WX-sur" id="KZk-g3-9Ox"/> <outlet property="tableView" destination="XmI-l4-SgT" id="9k1-t7-Xnc"/>
<outlet property="tableView" destination="XmI-l4-SgT" id="2yd-Uj-96H"/>
</connections> </connections>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="RwB-HB-TSk" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="RwB-HB-TSk" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="992.79999999999995" y="26.53673163418291"/> <point key="canvasLocation" x="992.79999999999995" y="26.53673163418291"/>
</scene> </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--> <!--Navigation Controller-->
<scene sceneID="mg9-JA-x9T"> <scene sceneID="mg9-JA-x9T">
<objects> <objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="Aeb-0C-dDR" sceneMemberID="viewController"> <navigationController automaticallyAdjustsScrollViewInsets="NO" id="Aeb-0C-dDR" sceneMemberID="viewController">
<toolbarItems/> <toolbarItems/>
<navigationItem key="navigationItem" id="d0b-i7-rXf"/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="RMW-As-go6"> <navigationBar key="navigationBar" contentMode="scaleToFill" id="RMW-As-go6">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/> <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
@ -94,4 +110,9 @@
<point key="canvasLocation" x="53.600000000000001" y="26.53673163418291"/> <point key="canvasLocation" x="53.600000000000001" y="26.53673163418291"/>
</scene> </scene>
</scenes> </scenes>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document> </document>

View file

@ -9,208 +9,44 @@
import AuthenticationServices import AuthenticationServices
import passKit import passKit
class CredentialProviderViewController: ASCredentialProviderViewController, UITableViewDataSource, UITableViewDelegate, UISearchBarDelegate { class CredentialProviderViewController: ASCredentialProviderViewController {
@IBOutlet var searchBar: UISearchBar! var passcodelock: PasscodeExtensionDisplay {
@IBOutlet var tableView: UITableView! PasscodeExtensionDisplay(extensionContext: self.extensionContext)
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
} }
// get the domain var embeddedNavigationController: UINavigationController {
var identifier = serviceIdentifiers[0].identifier children.first as! UINavigationController
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 passwordsViewController: PasswordsViewController {
Implement this method if your extension support embeddedNavigationController.viewControllers.first as! PasswordsViewController
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)
} }
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
passcodelock.presentPasscodeLockIfNeeded(self)
// prepare let passwordsTableEntries = PasswordStore.shared.fetchPasswordEntityCoreData(withDir: false).compactMap { PasswordTableEntry($0) }
searchBar.delegate = self let dataSource = PasswordsTableDataSource(entries: passwordsTableEntries)
tableView.delegate = self passwordsViewController.dataSource = dataSource
tableView.dataSource = self passwordsViewController.selectionDelegate = self
tableView.register(UINib(nibName: "PasswordWithFolderTableViewCell", bundle: nil), forCellReuseIdentifier: "passwordWithFolderTableViewCell")
// initialize table entries
initPasswordsTableEntries()
} }
private func initPasswordsTableEntries() { override func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier]) {
filteredPasswordsTableEntries.removeAll() let url = serviceIdentifiers.first.flatMap { URL(string: $0.identifier) }
passwordsViewController.navigationItem.prompt = url?.host
let passwordEntities = passwordStore.fetchPasswordEntityCoreData(withDir: false)
passwordsTableEntries = passwordEntities.compactMap {
PasswordTableEntry($0)
} }
} }
// define cell contents extension CredentialProviderViewController: PasswordSelectionDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func selected(password: PasswordTableEntry) {
let cell = tableView.dequeueReusableCell(withIdentifier: "passwordTableViewCell", for: indexPath) let passwordEntity = password.passwordEntity
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
}
// select row -> extension returns (with username and password) decryptPassword(in: self, with: passwordEntity) { password in
func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) { let username = password.getUsernameForCompletion()
let entry = getPasswordEntry(by: indexPath) let password = password.password
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 {
let passwordCredential = ASPasswordCredential(user: username, password: password) let passwordCredential = ASPasswordCredential(user: username, password: password)
self.extensionContext.completeRequest(withSelectedCredential: passwordCredential) 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]
}
} }
} }

View file

@ -12,11 +12,11 @@ import passKit
// cancel means cancel the extension // cancel means cancel the extension
class PasscodeLockViewControllerForExtension: PasscodeLockViewController { class PasscodeLockViewControllerForExtension: PasscodeLockViewController {
var originalExtensionContest: ASCredentialProviderExtensionContext? var originalExtensionContext: ASCredentialProviderExtensionContext!
convenience init(extensionContext: ASCredentialProviderExtensionContext?) { convenience init(extensionContext: ASCredentialProviderExtensionContext) {
self.init() self.init()
self.originalExtensionContest = extensionContext self.originalExtensionContext = extensionContext
} }
override func viewDidLoad() { override func viewDidLoad() {
@ -27,7 +27,7 @@ class PasscodeLockViewControllerForExtension: PasscodeLockViewController {
@objc @objc
func cancelExtension() { 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 passcodeLockVC: PasscodeLockViewControllerForExtension
private let extensionContext: ASCredentialProviderExtensionContext? private let extensionContext: ASCredentialProviderExtensionContext?
init(extensionContext: ASCredentialProviderExtensionContext?) { init(extensionContext: ASCredentialProviderExtensionContext) {
self.extensionContext = extensionContext self.extensionContext = extensionContext
self.passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext) self.passcodeLockVC = PasscodeLockViewControllerForExtension(extensionContext: extensionContext)
passcodeLockVC.dismissCompletionCallback = { [weak self] in passcodeLockVC.dismissCompletionCallback = { [weak self] in

View file

@ -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)
}
}

View file

@ -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)
}

View 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)
}
}
}
}

View file

@ -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) }
}
}

View 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
}
}