Support Chrome and app logins
This commit is contained in:
parent
daacfbea83
commit
fb55f32797
5 changed files with 144 additions and 31 deletions
|
|
@ -12,6 +12,7 @@
|
|||
2C58F31EECC494C7A7F00A98 /* libPods-passKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB6C63FA1652F925B5C9F0B5 /* libPods-passKitTests.a */; };
|
||||
398A8F69C2230A8117820BB7 /* libPods-passKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45BAA15189E80AA544EAF7AD /* libPods-passKit.a */; };
|
||||
6930A9D26085DE7CA1A7AACC /* libPods-passExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B240CA444AC9172F3053651 /* libPods-passExtension.a */; };
|
||||
A2168A7F1EFD40D5005EA873 /* OnePasswordExtensionConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2168A7E1EFD40D5005EA873 /* OnePasswordExtensionConstants.swift */; };
|
||||
A217ACE21E9AB17C00A1A6CF /* OTPScannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A217ACE11E9AB17C00A1A6CF /* OTPScannerController.swift */; };
|
||||
A217ACE41E9BBBBD00A1A6CF /* GitConfigSettingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A217ACE31E9BBBBD00A1A6CF /* GitConfigSettingTableViewController.swift */; };
|
||||
A2367B9B1EEFE1B300C8FE8B /* UtilsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2367B9A1EEFE1B300C8FE8B /* UtilsExtension.swift */; };
|
||||
|
|
@ -166,6 +167,7 @@
|
|||
7592A214C22CEBBEF4596CC1 /* libPods-pass.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-pass.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7E088A9255B6CB576EF757C1 /* Pods-passKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-passKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-passKit/Pods-passKit.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
A02ACA4077630047EA669D05 /* Pods-pass.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-pass.debug.xcconfig"; path = "Pods/Target Support Files/Pods-pass/Pods-pass.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
A2168A7E1EFD40D5005EA873 /* OnePasswordExtensionConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnePasswordExtensionConstants.swift; sourceTree = "<group>"; };
|
||||
A217ACE11E9AB17C00A1A6CF /* OTPScannerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OTPScannerController.swift; sourceTree = "<group>"; };
|
||||
A217ACE31E9BBBBD00A1A6CF /* GitConfigSettingTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = GitConfigSettingTableViewController.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
A2227D4C1EEE5E25002A69A9 /* libObjectivePGP.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libObjectivePGP.a; path = "Pods/../build/Debug-iphoneos/ObjectivePGP/libObjectivePGP.a"; sourceTree = "<group>"; };
|
||||
|
|
@ -326,6 +328,24 @@
|
|||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
A2168A801EFD431A005EA873 /* Controllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A2A61C2B1EEFDF3300CFE063 /* ExtensionViewController.swift */,
|
||||
A28C66671EF10EC900A398A1 /* PasscodeExtensionDisplay.swift */,
|
||||
);
|
||||
name = Controllers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A2168A811EFD4322005EA873 /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A2367B9A1EEFE1B300C8FE8B /* UtilsExtension.swift */,
|
||||
A2168A7E1EFD40D5005EA873 /* OnePasswordExtensionConstants.swift */,
|
||||
);
|
||||
name = Helpers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A26075791EEC6F34005DB03E /* passKit */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -350,14 +370,13 @@
|
|||
A26700251EEC466A00176B8A /* passExtension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A26700331EEC46C900176B8A /* passExtension.entitlements */,
|
||||
A267002B1EEC466A00176B8A /* Info.plist */,
|
||||
A2168A801EFD431A005EA873 /* Controllers */,
|
||||
A2168A811EFD4322005EA873 /* Helpers */,
|
||||
A2367B9F1EF0387000C8FE8B /* Assets.xcassets */,
|
||||
A26700351EEC475600176B8A /* passProcessor.js */,
|
||||
A26700331EEC46C900176B8A /* passExtension.entitlements */,
|
||||
A26700281EEC466A00176B8A /* MainInterface.storyboard */,
|
||||
A267002B1EEC466A00176B8A /* Info.plist */,
|
||||
A2A61C2B1EEFDF3300CFE063 /* ExtensionViewController.swift */,
|
||||
A2367B9A1EEFE1B300C8FE8B /* UtilsExtension.swift */,
|
||||
A28C66671EF10EC900A398A1 /* PasscodeExtensionDisplay.swift */,
|
||||
);
|
||||
path = passExtension;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1010,6 +1029,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A2A61C2C1EEFDF3300CFE063 /* ExtensionViewController.swift in Sources */,
|
||||
A2168A7F1EFD40D5005EA873 /* OnePasswordExtensionConstants.swift in Sources */,
|
||||
A28C66681EF10EC900A398A1 /* PasscodeExtensionDisplay.swift in Sources */,
|
||||
A2367B9B1EEFE1B300C8FE8B /* UtilsExtension.swift in Sources */,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,15 @@
|
|||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.2.8</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>org-appextension-feature-password-management</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
|
|
|
|||
|
|
@ -26,13 +26,15 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
private let passwordStore = PasswordStore.shared
|
||||
|
||||
private var searchActive = false
|
||||
|
||||
// the URL passed to the extension
|
||||
private var extensionURL: String?
|
||||
|
||||
private var passwordsTableEntries: [PasswordsTableEntry] = []
|
||||
private var filteredPasswordsTableEntries: [PasswordsTableEntry] = []
|
||||
|
||||
enum Action {
|
||||
case findLogin, fillBrowser, unknown
|
||||
}
|
||||
|
||||
private var extensionAction = Action.unknown
|
||||
|
||||
private lazy var passcodelock: PasscodeExtensionDisplay = {
|
||||
let passcodelock = PasscodeExtensionDisplay(extensionContext: self.extensionContext)
|
||||
return passcodelock
|
||||
|
|
@ -64,24 +66,47 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
// initialize table entries
|
||||
initPasswordsTableEntries()
|
||||
|
||||
// search using the extensionContext inputs
|
||||
// get the provider
|
||||
let item = extensionContext?.inputItems.first as! NSExtensionItem
|
||||
let provider = item.attachments?.first as! NSItemProvider
|
||||
let propertyList = String(kUTTypePropertyList)
|
||||
if provider.hasItemConformingToTypeIdentifier(propertyList) {
|
||||
provider.loadItem(forTypeIdentifier: propertyList, options: nil, completionHandler: { (item, error) -> Void in
|
||||
let provider: NSItemProvider
|
||||
if item.attachments?.count == 1 {
|
||||
// Safari
|
||||
provider = item.attachments?.first as! NSItemProvider
|
||||
} else {
|
||||
// e.g., Chrome, apps
|
||||
provider = item.attachments?[1] as! NSItemProvider
|
||||
}
|
||||
|
||||
// search using the extensionContext inputs
|
||||
if provider.hasItemConformingToTypeIdentifier(OnePasswordExtensionActions.findLogin) {
|
||||
provider.loadItem(forTypeIdentifier: OnePasswordExtensionActions.findLogin, options: nil, completionHandler: { (item, error) -> Void in
|
||||
let dictionary = item as! NSDictionary
|
||||
let results = dictionary[NSExtensionJavaScriptPreprocessingResultsKey] as! NSDictionary
|
||||
let url = URL(string: (results["url"] as? String)!)?.host
|
||||
var url: String?
|
||||
if var urlString = dictionary[OnePasswordExtensionKey.URLStringKey] as? String {
|
||||
if !urlString.hasPrefix("http://") && !urlString.hasPrefix("https://") {
|
||||
urlString = "http://" + urlString
|
||||
}
|
||||
url = URL(string: urlString)?.host
|
||||
}
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.extensionAction = .findLogin
|
||||
// force search (set text, set active, force search)
|
||||
self?.searchBar.text = url
|
||||
self?.searchBar.becomeFirstResponder()
|
||||
self?.searchBarSearchButtonClicked((self?.searchBar)!)
|
||||
}
|
||||
})
|
||||
} else if provider.hasItemConformingToTypeIdentifier(kUTTypeURL as String) {
|
||||
provider.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil, completionHandler: { (item, error) -> Void in
|
||||
let url = (item as? NSURL)!.host
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.extensionAction = .fillBrowser
|
||||
// force search (set text, set active, force search)
|
||||
self?.searchBar.text = url
|
||||
self?.searchBar.becomeFirstResponder()
|
||||
self?.searchBarSearchButtonClicked((self?.searchBar)!)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
print("error")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -116,19 +141,29 @@ class ExtensionViewController: UIViewController, UITableViewDataSource, UITableV
|
|||
do {
|
||||
decryptedPassword = try self.passwordStore.decrypt(passwordEntity: passwordEntity, requestPGPKeyPassphrase: self.requestPGPKeyPassphrase)
|
||||
DispatchQueue.main.async {
|
||||
switch self.extensionAction {
|
||||
case .findLogin:
|
||||
// prepare a dictionary to return
|
||||
let extensionItem = NSExtensionItem()
|
||||
var returnDictionary = [OnePasswordExtensionKey.usernameKey: decryptedPassword?.getUsername() ?? "",
|
||||
OnePasswordExtensionKey.passwordKey: decryptedPassword?.password ?? ""]
|
||||
if let totpPassword = decryptedPassword?.getOtp() {
|
||||
returnDictionary[OnePasswordExtensionKey.totpKey] = totpPassword
|
||||
}
|
||||
extensionItem.attachments = [NSItemProvider(item: returnDictionary as NSSecureCoding, typeIdentifier: String(kUTTypePropertyList))]
|
||||
self.extensionContext!.completeRequest(returningItems: [extensionItem], completionHandler: nil)
|
||||
default:
|
||||
// copy the password to the clipboard
|
||||
Utils.copyToPasteboard(textToCopy: decryptedPassword?.password)
|
||||
let title = "Password Copied"
|
||||
let message = "Usename: " + (decryptedPassword?.getUsername() ?? "Unknown") + "\r\n(Remember to clear the clipboard.)"
|
||||
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
|
||||
// return a dictionary for JavaScript for best-effor fill in
|
||||
let extensionItem = NSExtensionItem()
|
||||
let returnDictionary = [ NSExtensionJavaScriptFinalizeArgumentKey : ["username": decryptedPassword?.getUsername() ?? "", "password": decryptedPassword?.password ?? ""]]
|
||||
extensionItem.attachments = [NSItemProvider(item: returnDictionary as NSSecureCoding, typeIdentifier: String(kUTTypePropertyList))]
|
||||
self.extensionContext!.completeRequest(returningItems: [extensionItem], completionHandler: nil)
|
||||
self.extensionContext!.completeRequest(returningItems: nil, completionHandler: nil)
|
||||
}))
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print(error)
|
||||
DispatchQueue.main.async {
|
||||
|
|
|
|||
|
|
@ -28,11 +28,9 @@
|
|||
<dict>
|
||||
<key>NSExtensionActivationSupportsText</key>
|
||||
<true/>
|
||||
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
|
||||
<integer>100</integer>
|
||||
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>NSExtensionJavaScriptPreprocessingFile</key>
|
||||
<string>passProcessor</string>
|
||||
</dict>
|
||||
<key>NSExtensionMainStoryboard</key>
|
||||
<string>MainInterface</string>
|
||||
|
|
|
|||
51
passExtension/OnePasswordExtensionConstants.swift
Normal file
51
passExtension/OnePasswordExtensionConstants.swift
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// OnePasswordExtensionConstants.swift
|
||||
// pass
|
||||
//
|
||||
// Created by Yishi Lin on 2017/6/23.
|
||||
// Copyright © 2017 Bob Sun. All rights reserved.
|
||||
//
|
||||
// This file contains constants from https://github.com/agilebits/onepassword-app-extension/
|
||||
|
||||
class OnePasswordExtensionActions {
|
||||
static let findLogin = "org.appextension.find-login-action"
|
||||
static let saveLogin = "org.appextension.save-login-action"
|
||||
static let changePassword = "org.appextension.change-password-action"
|
||||
static let fillWebView = "org.appextension.fill-webview-action"
|
||||
static let fillBrowser = "org.appextension.fill-browser-action"
|
||||
}
|
||||
|
||||
class OnePasswordExtensionKey {
|
||||
// Login Dictionary keys - Used to get or set the properties of a 1Password Login
|
||||
static let URLStringKey = "url_string"
|
||||
static let usernameKey = "username"
|
||||
static let passwordKey = "password"
|
||||
static let totpKey = "totp"
|
||||
static let titleKey = "login_title"
|
||||
static let notesKey = "notes"
|
||||
static let sectionTitleKey = "section_title"
|
||||
static let fieldsKey = "fields"
|
||||
static let returnedFieldsKey = "returned_fields"
|
||||
static let oldPasswordKey = "old_password"
|
||||
static let passwordGeneratorOptionsKey = "password_generator_options"
|
||||
|
||||
// Password Generator options - Used to set the 1Password Password Generator options when saving a new Login or when changing the password for for an existing Login
|
||||
static let generatedPasswordMinLengthKey = "password_min_length"
|
||||
static let generatedPasswordMaxLengthKey = "password_max_length"
|
||||
static let generatedPasswordRequireDigitsKey = "password_require_digits"
|
||||
static let generatedPasswordRequireSymbolsKey = "password_require_symbols"
|
||||
static let generatedPasswordForbiddenCharactersKey = "password_forbidden_characters"
|
||||
}
|
||||
|
||||
// Errors codes
|
||||
class OnePasswordExtensionError {
|
||||
static let errorDomain = "OnePasswordExtension"
|
||||
static let errorCodeCancelledByUser = 0
|
||||
static let errorCodeAPINotAvailable = 1
|
||||
static let errorCodeFailedToContactExtension = 2
|
||||
static let errorCodeFailedToLoadItemProviderData = 3
|
||||
static let errorCodeCollectFieldsScriptFailed = 4
|
||||
static let errorCodeFillFieldsScriptFailed = 5
|
||||
static let errorCodeUnexpectedData = 6
|
||||
static let errorCodeFailedToObtainURLStringFromWebView = 7
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue