From fac4401e974c883b842ffa52259a285f8362b3ba Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Mon, 30 May 2016 22:51:53 -0400 Subject: [PATCH] Added support for find login, save login, and change password app extension provider types. --- src/iOS.Extension/ActionViewController.cs | 249 +++++++++++++++++----- src/iOS.Extension/extension.js | 4 +- 2 files changed, 198 insertions(+), 55 deletions(-) diff --git a/src/iOS.Extension/ActionViewController.cs b/src/iOS.Extension/ActionViewController.cs index 9f00c4d04..e0f4ff7c9 100644 --- a/src/iOS.Extension/ActionViewController.cs +++ b/src/iOS.Extension/ActionViewController.cs @@ -18,7 +18,25 @@ namespace Bit.iOS.Extension public partial class ActionViewController : UIViewController { private const string AppExtensionVersionNumberKey = "version_number"; + private const string AppExtensionUrlStringKey = "url_string"; + private const string AppExtensionUsernameKey = "username"; + private const string AppExtensionPasswordKey = "password"; + private const string AppExtensionTotpKey = "totp"; + private const string AppExtensionTitleKey = "login_title"; + private const string AppExtensionNotesKey = "notes"; + private const string AppExtensionSectionTitleKey = "section_title"; + private const string AppExtensionFieldsKey = "fields"; + private const string AppExtensionReturnedFieldsKey = "returned_fields"; + private const string AppExtensionOldPasswordKey = "old_password"; + private const string AppExtensionPasswordGeneratorOptionsKey = "password_generator_options"; + + private const string AppExtensionGeneratedPasswordMinLengthKey = "password_min_length"; + private const string AppExtensionGeneratedPasswordMaxLengthKey = "password_max_length"; + private const string AppExtensionGeneratedPasswordRequireDigitsKey = "password_require_digits"; + private const string AppExtensionGeneratedPasswordRequireSymbolsKey = "password_require_symbols"; + private const string AppExtensionGeneratedPasswordForbiddenCharactersKey = "password_forbidden_characters"; + private const string UTTypeAppExtensionFindLoginAction = "org.appextension.find-login-action"; private const string UTTypeAppExtensionSaveLoginAction = "org.appextension.save-login-action"; private const string UTTypeAppExtensionChangePasswordAction = "org.appextension.change-password-action"; @@ -33,9 +51,14 @@ namespace Bit.iOS.Extension } } - public string HtmlContent { get; set; } - public Uri BaseUri { get; set; } + public string ProviderType { get; set; } public Uri Url { get; set; } + public string SiteTitle { get; set; } + public string Username { get; set; } + public string Password { get; set; } + public string OldPassword { get; set; } + public string Notes { get; set; } + public PasswordGenerationOptions PasswordOptions { get; set; } private void SetIoc() { @@ -72,35 +95,8 @@ namespace Bit.iOS.Extension base.DidReceiveMemoryWarning(); } - public async override void LoadView() + public override void LoadView() { - View = new UIView(new CGRect(x: 0.0, y: 0, width: 320.0, height: 200.0)); - var button = new UIButton(new CGRect(x: 10.0, y: 50.0, width: 200.0, height: 30.0)); - button.SetTitle("Done", UIControlState.Normal); - button.TouchUpInside += Button_TouchUpInside; - View.AddSubview(button); - } - - private void Button_TouchUpInside(object sender, EventArgs e) - { - var itemData = new NSDictionary( - "username", "me@example.com", - "password", "mypassword", - "autoSubmit", true); - - var resultsProvider = new NSItemProvider( - new NSDictionary(NSJavaScriptExtension.FinalizeArgumentKey, itemData), UTType.PropertyList); - - var resultsItem = new NSExtensionItem { Attachments = new NSItemProvider[] { resultsProvider } }; - var returningItems = new NSExtensionItem[] { resultsItem }; - - ExtensionContext.CompleteRequest(returningItems, null); - } - - public override void ViewDidLoad() - { - base.ViewDidLoad(); - foreach(var item in ExtensionContext.InputItems) { foreach(var itemProvider in item.Attachments) @@ -113,65 +109,214 @@ namespace Bit.iOS.Extension { break; } + else if(ProcessSaveLoginProvider(itemProvider)) + { + break; + } + else if(ProcessChangePasswordProvider(itemProvider)) + { + break; + } } } + + View = new UIView(new CGRect(x: 0.0, y: 0, width: 320.0, height: 200.0)); + var button = new UIButton(new CGRect(x: 10.0, y: 50.0, width: 200.0, height: 30.0)); + button.SetTitle("Done", UIControlState.Normal); + button.TouchUpInside += Button_TouchUpInside; + View.AddSubview(button); } - private bool ProcessWebUrlProvider(NSItemProvider itemProvider) + private void Button_TouchUpInside(object sender, EventArgs e) { - if(!itemProvider.HasItemConformingTo(UTType.PropertyList)) + NSDictionary itemData = null; + if(ProviderType == UTType.PropertyList) + { + itemData = new NSDictionary( + "username", "me@example.com", + "password", "mypassword", + "autoSubmit", true); + } + else if(ProviderType == UTTypeAppExtensionFindLoginAction) + { + itemData = new NSDictionary( + AppExtensionUsernameKey, "me@example.com", + AppExtensionPasswordKey, "mypassword"); + } + else if(ProviderType == UTTypeAppExtensionSaveLoginAction) + { + itemData = new NSDictionary( + AppExtensionUsernameKey, "me@example.com", + AppExtensionPasswordKey, "mypassword"); + } + else if(ProviderType == UTTypeAppExtensionChangePasswordAction) + { + itemData = new NSDictionary( + AppExtensionPasswordKey, "mynewpassword", + AppExtensionOldPasswordKey, "myoldpassword"); + } + else + { + return; + } + + var resultsProvider = new NSItemProvider(itemData, UTType.PropertyList); + var resultsItem = new NSExtensionItem { Attachments = new NSItemProvider[] { resultsProvider } }; + var returningItems = new NSExtensionItem[] { resultsItem }; + + ExtensionContext.CompleteRequest(returningItems, null); + } + + public override void ViewDidLoad() + { + base.ViewDidLoad(); + } + + private bool ProcessItemProvider(NSItemProvider itemProvider, string type, Action action) + { + if(!itemProvider.HasItemConformingTo(type)) { return false; } - itemProvider.LoadItem(UTType.PropertyList, null, (NSObject list, NSError error) => + itemProvider.LoadItem(type, null, (NSObject list, NSError error) => { if(list == null) { return; } + ProviderType = type; var dict = list as NSDictionary; + action(dict); + + Debug.WriteLine("BW LOG, ProviderType: " + ProviderType); + Debug.WriteLine("BW LOG, Url: " + Url); + Debug.WriteLine("BW LOG, Title: " + SiteTitle); + Debug.WriteLine("BW LOG, Username: " + Username); + Debug.WriteLine("BW LOG, Password: " + Password); + Debug.WriteLine("BW LOG, Old Password: " + OldPassword); + Debug.WriteLine("BW LOG, Notes: " + Notes); + + if(PasswordOptions != null) + { + Debug.WriteLine("BW LOG, PasswordOptions Min Length: " + PasswordOptions.MinLength); + Debug.WriteLine("BW LOG, PasswordOptions Max Length: " + PasswordOptions.MaxLength); + Debug.WriteLine("BW LOG, PasswordOptions Require Digits: " + PasswordOptions.RequireDigits); + Debug.WriteLine("BW LOG, PasswordOptions Require Symbols: " + PasswordOptions.RequireSymbols); + Debug.WriteLine("BW LOG, PasswordOptions Forbidden Chars: " + PasswordOptions.ForbiddenCharacters); + } + }); + + return true; + } + + private bool ProcessWebUrlProvider(NSItemProvider itemProvider) + { + return ProcessItemProvider(itemProvider, UTType.PropertyList, (dict) => + { var result = dict[NSJavaScriptExtension.PreprocessingResultsKey]; if(result == null) { return; } - HtmlContent = result.ValueForKey(new NSString("htmlContent")) as NSString; - BaseUri = new Uri(result.ValueForKey(new NSString("baseUri")) as NSString); Url = new Uri(result.ValueForKey(new NSString("url")) as NSString); }); - - return true; } private bool ProcessFindLoginProvider(NSItemProvider itemProvider) { - if(!itemProvider.HasItemConformingTo(UTTypeAppExtensionFindLoginAction)) + return ProcessItemProvider(itemProvider, UTTypeAppExtensionFindLoginAction, (dict) => { - return false; - } - - itemProvider.LoadItem(UTTypeAppExtensionFindLoginAction, null, (NSObject list, NSError error) => - { - if(list == null) - { - return; - } - - var dict = list as NSDictionary; var version = dict[AppExtensionVersionNumberKey] as NSNumber; var url = dict[AppExtensionUrlStringKey] as NSString; - if(url == null) + + if(url != null) { - return; + Url = new Uri(url); + } + }); + } + + private bool ProcessSaveLoginProvider(NSItemProvider itemProvider) + { + return ProcessItemProvider(itemProvider, UTTypeAppExtensionSaveLoginAction, (dict) => + { + var version = dict[AppExtensionVersionNumberKey] as NSNumber; + var url = dict[AppExtensionUrlStringKey] as NSString; + var title = dict[AppExtensionTitleKey] as NSString; + var sectionTitle = dict[AppExtensionSectionTitleKey] as NSString; + var username = dict[AppExtensionUsernameKey] as NSString; + var password = dict[AppExtensionPasswordKey] as NSString; + var notes = dict[AppExtensionNotesKey] as NSString; + var fields = dict[AppExtensionFieldsKey] as NSDictionary; + var passwordGenerationOptions = dict[AppExtensionPasswordGeneratorOptionsKey] as NSDictionary; + + if(url != null) + { + Url = new Uri(url); } Url = new Uri(url); + SiteTitle = title; + Username = username; + Password = password; + Notes = notes; + PasswordOptions = new PasswordGenerationOptions(passwordGenerationOptions); }); + } - return true; + private bool ProcessChangePasswordProvider(NSItemProvider itemProvider) + { + return ProcessItemProvider(itemProvider, UTTypeAppExtensionChangePasswordAction, (dict) => + { + var version = dict[AppExtensionVersionNumberKey] as NSNumber; + var url = dict[AppExtensionUrlStringKey] as NSString; + var title = dict[AppExtensionTitleKey] as NSString; + var sectionTitle = dict[AppExtensionSectionTitleKey] as NSString; + var username = dict[AppExtensionUsernameKey] as NSString; + var password = dict[AppExtensionPasswordKey] as NSString; + var oldPassword = dict[AppExtensionOldPasswordKey] as NSString; + var notes = dict[AppExtensionNotesKey] as NSString; + var fields = dict[AppExtensionFieldsKey] as NSDictionary; + var passwordGenerationOptions = dict[AppExtensionPasswordGeneratorOptionsKey] as NSDictionary; + + if(url != null) + { + Url = new Uri(url); + } + + SiteTitle = title; + Username = username; + Password = password; + OldPassword = oldPassword; + Notes = notes; + PasswordOptions = new PasswordGenerationOptions(passwordGenerationOptions); + }); + } + + public class PasswordGenerationOptions + { + public PasswordGenerationOptions(NSDictionary dict) + { + if(dict == null) + { + throw new ArgumentNullException(nameof(dict)); + } + + MinLength = (dict[AppExtensionGeneratedPasswordMinLengthKey] as NSNumber)?.Int32Value ?? 0; + MaxLength = (dict[AppExtensionGeneratedPasswordMaxLengthKey] as NSNumber)?.Int32Value ?? 0; + RequireDigits = (dict[AppExtensionGeneratedPasswordRequireDigitsKey] as NSNumber)?.BoolValue ?? false; + RequireSymbols = (dict[AppExtensionGeneratedPasswordRequireSymbolsKey] as NSNumber)?.BoolValue ?? false; + ForbiddenCharacters = (dict[AppExtensionGeneratedPasswordForbiddenCharactersKey] as NSString)?.ToString(); + } + + public int MinLength { get; set; } + public int MaxLength { get; set; } + public bool RequireDigits { get; set; } + public bool RequireSymbols { get; set; } + public string ForbiddenCharacters { get; set; } } } } \ No newline at end of file diff --git a/src/iOS.Extension/extension.js b/src/iOS.Extension/extension.js index fd281a46b..490872cc9 100644 --- a/src/iOS.Extension/extension.js +++ b/src/iOS.Extension/extension.js @@ -6,9 +6,7 @@ BitwardenExtension.prototype = { console.log(arguments); var args = { - baseUri: document.baseURI, - url: document.URL, - htmlContent: document.body.innerHTML + url: document.URL }; arguments.completionFunction(args); },