bitwarden-android/src/iOS.Extension/LoadingViewController.cs

520 lines
22 KiB
C#
Raw Normal View History

using System;
using System.Drawing;
using System.Diagnostics;
using Bit.App.Abstractions;
using Bit.App.Repositories;
using Bit.App.Services;
using Bit.iOS.Core.Services;
using Foundation;
using UIKit;
using XLabs.Ioc;
using Bit.iOS.Core;
using Newtonsoft.Json;
using Bit.iOS.Extension.Models;
using MobileCoreServices;
using Plugin.Settings.Abstractions;
using Plugin.Connectivity;
2016-07-09 20:11:18 +03:00
using Plugin.Fingerprint;
using Bit.iOS.Core.Utilities;
using Bit.App.Resources;
using Bit.iOS.Core.Controllers;
2017-06-01 21:49:48 +03:00
using SimpleInjector;
using XLabs.Ioc.SimpleInjectorContainer;
namespace Bit.iOS.Extension
{
public partial class LoadingViewController : ExtendedUIViewController
{
private Context _context = new Context();
2016-07-23 00:14:57 +03:00
private bool _setupHockeyApp = false;
private readonly JsonSerializerSettings _jsonSettings =
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
2016-08-04 07:25:10 +03:00
private IGoogleAnalyticsService _googleAnalyticsService;
private ILockService _lockService;
public LoadingViewController(IntPtr handle) : base(handle)
{ }
public override void ViewDidLoad()
{
SetIoc();
SetCulture();
2016-08-04 15:31:42 +03:00
base.ViewDidLoad();
View.BackgroundColor = new UIColor(red: 0.94f, green: 0.94f, blue: 0.96f, alpha: 1.0f);
_context.ExtContext = ExtensionContext;
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
_lockService = Resolver.Resolve<ILockService>();
2016-08-04 15:31:42 +03:00
2016-07-23 00:14:57 +03:00
if(!_setupHockeyApp)
{
var appIdService = Resolver.Resolve<IAppIdService>();
var crashManagerDelegate = new HockeyAppCrashManagerDelegate(appIdService, Resolver.Resolve<IAuthService>());
2016-07-23 00:14:57 +03:00
var manager = HockeyApp.iOS.BITHockeyManager.SharedHockeyManager;
manager.Configure("51f96ae568ba45f699a18ad9f63046c3", crashManagerDelegate);
2016-07-23 05:56:45 +03:00
manager.CrashManager.CrashManagerStatus = HockeyApp.iOS.BITCrashManagerStatus.AutoSend;
manager.UserId = appIdService.AppId;
2016-07-23 00:14:57 +03:00
manager.StartManager();
manager.Authenticator.AuthenticateInstallation();
_setupHockeyApp = true;
}
foreach(var item in ExtensionContext.InputItems)
{
var processed = false;
foreach(var itemProvider in item.Attachments)
{
if(ProcessWebUrlProvider(itemProvider)
|| ProcessFindLoginProvider(itemProvider)
|| ProcessFindLoginBrowserProvider(itemProvider, Constants.UTTypeAppExtensionFillBrowserAction)
|| ProcessFindLoginBrowserProvider(itemProvider, Constants.UTTypeAppExtensionFillWebViewAction)
|| ProcessSaveLoginProvider(itemProvider)
|| ProcessChangePasswordProvider(itemProvider)
|| ProcessExtensionSetupProvider(itemProvider))
{
processed = true;
break;
}
}
if(processed)
{
break;
}
}
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
var authService = Resolver.Resolve<IAuthService>();
if(!authService.IsAuthenticated)
{
var alert = Dialogs.CreateAlert(null, AppResources.MustLogInMainApp, AppResources.Ok, (a) =>
{
CompleteRequest(null);
});
PresentViewController(alert, true, null);
return;
}
var lockService = Resolver.Resolve<ILockService>();
2017-06-02 21:46:10 +03:00
var lockType = lockService.GetLockTypeAsync(false).GetAwaiter().GetResult();
switch(lockType)
{
case App.Enums.LockType.Fingerprint:
PerformSegue("lockFingerprintSegue", this);
break;
case App.Enums.LockType.PIN:
PerformSegue("lockPinSegue", this);
break;
case App.Enums.LockType.Password:
PerformSegue("lockPasswordSegue", this);
break;
default:
2016-08-05 07:23:48 +03:00
ContinueOn();
break;
}
}
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
{
var navController = segue.DestinationViewController as UINavigationController;
if(navController != null)
{
2017-01-03 08:17:15 +03:00
var listLoginController = navController.TopViewController as LoginListViewController;
var addLoginController = navController.TopViewController as LoginAddViewController;
var fingerprintViewController = navController.TopViewController as LockFingerprintViewController;
var pinViewController = navController.TopViewController as LockPinViewController;
var passwordViewController = navController.TopViewController as LockPasswordViewController;
var setupViewController = navController.TopViewController as SetupViewController;
2017-01-03 08:17:15 +03:00
if(listLoginController != null)
{
2017-01-03 08:17:15 +03:00
listLoginController.Context = _context;
listLoginController.LoadingController = this;
}
2017-01-03 08:17:15 +03:00
else if(addLoginController != null)
{
2017-01-03 08:17:15 +03:00
addLoginController.Context = _context;
addLoginController.LoadingController = this;
}
else if(fingerprintViewController != null)
{
fingerprintViewController.Context = _context;
fingerprintViewController.LoadingController = this;
}
else if(pinViewController != null)
{
pinViewController.Context = _context;
pinViewController.LoadingController = this;
}
else if(passwordViewController != null)
{
passwordViewController.Context = _context;
passwordViewController.LoadingController = this;
}
else if(setupViewController != null)
{
setupViewController.Context = _context;
setupViewController.LoadingController = this;
}
}
}
public void DismissLockAndContinue()
{
2016-07-22 03:59:55 +03:00
Debug.WriteLine("BW Log, Dismissing lock controller.");
DismissViewController(false, () =>
{
2016-08-05 07:23:48 +03:00
ContinueOn();
});
}
2016-08-05 07:23:48 +03:00
private void ContinueOn()
{
2017-01-03 08:17:15 +03:00
Debug.WriteLine("BW Log, Segue to setup, login add or list.");
_lockService.UpdateLastActivity();
2016-08-05 07:23:48 +03:00
if(_context.ProviderType == Constants.UTTypeAppExtensionSaveLoginAction)
{
2017-01-03 08:17:15 +03:00
PerformSegue("newLoginSegue", this);
2016-08-05 07:23:48 +03:00
}
else if(_context.ProviderType == Constants.UTTypeAppExtensionSetup)
{
PerformSegue("setupSegue", this);
}
else
{
2017-01-03 08:17:15 +03:00
PerformSegue("loginListSegue", this);
2016-08-05 07:23:48 +03:00
}
}
2017-07-21 18:39:22 +03:00
public void CompleteUsernamePasswordRequest(string username, string password, string totp)
{
NSDictionary itemData = null;
if(_context.ProviderType == UTType.PropertyList)
{
var fillScript = new FillScript(_context.Details, username, password);
var scriptJson = JsonConvert.SerializeObject(fillScript, _jsonSettings);
var scriptDict = new NSDictionary(Constants.AppExtensionWebViewPageFillScript, scriptJson);
itemData = new NSDictionary(NSJavaScriptExtension.FinalizeArgumentKey, scriptDict);
}
else if(_context.ProviderType == Constants.UTTypeAppExtensionFindLoginAction)
{
itemData = new NSDictionary(
Constants.AppExtensionUsernameKey, username,
Constants.AppExtensionPasswordKey, password);
}
else if(_context.ProviderType == Constants.UTTypeAppExtensionFillBrowserAction
|| _context.ProviderType == Constants.UTTypeAppExtensionFillWebViewAction)
{
var fillScript = new FillScript(_context.Details, username, password);
var scriptJson = JsonConvert.SerializeObject(fillScript, _jsonSettings);
itemData = new NSDictionary(Constants.AppExtensionWebViewPageFillScript, scriptJson);
}
else if(_context.ProviderType == Constants.UTTypeAppExtensionSaveLoginAction)
{
itemData = new NSDictionary(
Constants.AppExtensionUsernameKey, username,
Constants.AppExtensionPasswordKey, password);
}
else if(_context.ProviderType == Constants.UTTypeAppExtensionChangePasswordAction)
{
itemData = new NSDictionary(
Constants.AppExtensionPasswordKey, string.Empty,
Constants.AppExtensionOldPasswordKey, password);
}
2017-07-21 18:39:22 +03:00
if(!string.IsNullOrWhiteSpace(totp))
{
UIPasteboard.General.String = totp;
}
CompleteRequest(itemData);
}
public void CompleteRequest(NSDictionary itemData)
{
Debug.WriteLine("BW LOG, itemData: " + itemData);
var resultsProvider = new NSItemProvider(itemData, UTType.PropertyList);
var resultsItem = new NSExtensionItem { Attachments = new NSItemProvider[] { resultsProvider } };
var returningItems = new NSExtensionItem[] { resultsItem };
if(itemData != null)
{
_lockService.UpdateLastActivity();
_googleAnalyticsService.TrackExtensionEvent("AutoFilled", _context.ProviderType);
}
else
{
_googleAnalyticsService.TrackExtensionEvent("Closed", _context.ProviderType);
}
_googleAnalyticsService.Dispatch(() =>
{
NSRunLoop.Main.BeginInvokeOnMainThread(() =>
{
Resolver.ResetResolver();
ExtensionContext.CompleteRequest(returningItems, null);
});
});
}
private void SetIoc()
{
2017-06-01 21:49:48 +03:00
var container = new Container();
2017-06-01 21:49:48 +03:00
// Services
container.RegisterSingleton<IDatabaseService, DatabaseService>();
container.RegisterSingleton<ISqlService, SqlService>();
container.RegisterSingleton<ISecureStorageService, KeyChainStorageService>();
container.RegisterSingleton<ICryptoService, CryptoService>();
container.RegisterSingleton<IKeyDerivationService, CommonCryptoKeyDerivationService>();
container.RegisterSingleton<IAuthService, AuthService>();
container.RegisterSingleton<IFolderService, FolderService>();
container.RegisterSingleton<ILoginService, LoginService>();
container.RegisterSingleton<ISyncService, SyncService>();
container.RegisterSingleton<IPasswordGenerationService, PasswordGenerationService>();
container.RegisterSingleton<IAppIdService, AppIdService>();
container.RegisterSingleton<ILockService, LockService>();
container.RegisterSingleton<IGoogleAnalyticsService, GoogleAnalyticsService>();
container.RegisterSingleton<ILocalizeService, LocalizeService>();
container.RegisterSingleton<ILogService, LogService>();
container.RegisterSingleton<IHttpService, HttpService>();
container.RegisterSingleton<ITokenService, TokenService>();
container.RegisterSingleton<ISettingsService, SettingsService>();
container.RegisterSingleton<IDeviceInfoService, DeviceInfoService>();
container.RegisterSingleton<IAppSettingsService, AppSettingsService>();
2017-06-01 21:49:48 +03:00
// Repositories
container.RegisterSingleton<IFolderRepository, FolderRepository>();
container.RegisterSingleton<IFolderApiRepository, FolderApiRepository>();
container.RegisterSingleton<ILoginRepository, LoginRepository>();
container.RegisterSingleton<IAttachmentRepository, AttachmentRepository>();
2017-06-01 21:49:48 +03:00
container.RegisterSingleton<ILoginApiRepository, LoginApiRepository>();
container.RegisterSingleton<IConnectApiRepository, ConnectApiRepository>();
container.RegisterSingleton<ISettingsRepository, SettingsRepository>();
container.RegisterSingleton<IAccountsApiRepository, AccountsApiRepository>();
2017-07-25 16:52:07 +03:00
container.RegisterSingleton<ICipherApiRepository, CipherApiRepository>();
2017-06-01 21:49:48 +03:00
// Other
container.RegisterSingleton(CrossConnectivity.Current);
container.RegisterSingleton(CrossFingerprint.Current);
var settings = new Settings("group.com.8bit.bitwarden");
container.RegisterSingleton<ISettings>(settings);
Resolver.ResetResolver(new SimpleInjectorResolver(container));
}
private void SetCulture()
{
var localizeService = Resolver.Resolve<ILocalizeService>();
var ci = localizeService.GetCurrentCultureInfo();
AppResources.Culture = ci;
localizeService.SetLocale(ci);
}
2017-04-25 00:40:17 +03:00
private bool ProcessItemProvider(NSItemProvider itemProvider, string type, Action<NSDictionary> dictAction,
2017-04-25 01:05:23 +03:00
Action<NSUrl> urlAction = null)
{
if(!itemProvider.HasItemConformingTo(type))
{
return false;
}
itemProvider.LoadItem(type, null, (NSObject list, NSError error) =>
{
if(list == null)
{
return;
}
_context.ProviderType = type;
2017-04-25 00:40:17 +03:00
var dict = list as NSDictionary;
2017-04-25 00:40:17 +03:00
if(dict != null && dictAction != null)
{
dictAction(dict);
}
else if(list is NSUrl && urlAction != null)
2017-04-25 00:40:17 +03:00
{
var url = list as NSUrl;
2017-04-25 01:05:23 +03:00
urlAction(url);
2017-04-25 00:40:17 +03:00
}
else
{
throw new Exception("Cannot parse list for action.");
}
_googleAnalyticsService.TrackExtensionEvent("ProcessItemProvider", type);
2016-08-04 07:25:10 +03:00
Debug.WriteLine("BW LOG, ProviderType: " + _context.ProviderType);
Debug.WriteLine("BW LOG, Url: " + _context.UrlString);
2017-01-03 08:17:15 +03:00
Debug.WriteLine("BW LOG, Title: " + _context.LoginTitle);
Debug.WriteLine("BW LOG, Username: " + _context.Username);
Debug.WriteLine("BW LOG, Password: " + _context.Password);
Debug.WriteLine("BW LOG, Old Password: " + _context.OldPassword);
Debug.WriteLine("BW LOG, Notes: " + _context.Notes);
Debug.WriteLine("BW LOG, Details: " + _context.Details);
if(_context.PasswordOptions != null)
{
Debug.WriteLine("BW LOG, PasswordOptions Min Length: " + _context.PasswordOptions.MinLength);
Debug.WriteLine("BW LOG, PasswordOptions Max Length: " + _context.PasswordOptions.MaxLength);
Debug.WriteLine("BW LOG, PasswordOptions Require Digits: " + _context.PasswordOptions.RequireDigits);
Debug.WriteLine("BW LOG, PasswordOptions Require Symbols: " + _context.PasswordOptions.RequireSymbols);
Debug.WriteLine("BW LOG, PasswordOptions Forbidden Chars: " + _context.PasswordOptions.ForbiddenCharacters);
}
});
return true;
}
private bool ProcessWebUrlProvider(NSItemProvider itemProvider)
{
return ProcessItemProvider(itemProvider, UTType.PropertyList, (dict) =>
{
var result = dict[NSJavaScriptExtension.PreprocessingResultsKey];
if(result == null)
{
return;
}
_context.UrlString = result.ValueForKey(new NSString(Constants.AppExtensionUrlStringKey)) as NSString;
var jsonStr = result.ValueForKey(new NSString(Constants.AppExtensionWebViewPageDetails)) as NSString;
_context.Details = DeserializeString<PageDetails>(jsonStr);
});
}
private bool ProcessFindLoginProvider(NSItemProvider itemProvider)
{
return ProcessItemProvider(itemProvider, Constants.UTTypeAppExtensionFindLoginAction, (dict) =>
{
var version = dict[Constants.AppExtensionVersionNumberKey] as NSNumber;
var url = dict[Constants.AppExtensionUrlStringKey] as NSString;
if(url != null)
{
_context.UrlString = url;
}
});
}
private bool ProcessFindLoginBrowserProvider(NSItemProvider itemProvider, string action)
{
return ProcessItemProvider(itemProvider, action, (dict) =>
{
var version = dict[Constants.AppExtensionVersionNumberKey] as NSNumber;
var url = dict[Constants.AppExtensionUrlStringKey] as NSString;
if(url != null)
{
_context.UrlString = url;
}
_context.Details = DeserializeDictionary<PageDetails>(dict[Constants.AppExtensionWebViewPageDetails] as NSDictionary);
2017-04-25 01:05:23 +03:00
}, (url) =>
2017-04-25 00:40:17 +03:00
{
2017-04-25 01:05:23 +03:00
if(url != null)
2017-04-25 00:40:17 +03:00
{
2017-04-25 01:05:23 +03:00
_context.UrlString = url.AbsoluteString;
2017-04-25 00:40:17 +03:00
}
});
}
private bool ProcessSaveLoginProvider(NSItemProvider itemProvider)
{
return ProcessItemProvider(itemProvider, Constants.UTTypeAppExtensionSaveLoginAction, (dict) =>
{
var version = dict[Constants.AppExtensionVersionNumberKey] as NSNumber;
var url = dict[Constants.AppExtensionUrlStringKey] as NSString;
var title = dict[Constants.AppExtensionTitleKey] as NSString;
var sectionTitle = dict[Constants.AppExtensionSectionTitleKey] as NSString;
var username = dict[Constants.AppExtensionUsernameKey] as NSString;
var password = dict[Constants.AppExtensionPasswordKey] as NSString;
var notes = dict[Constants.AppExtensionNotesKey] as NSString;
var fields = dict[Constants.AppExtensionFieldsKey] as NSDictionary;
if(url != null)
{
_context.UrlString = url;
}
2017-01-03 08:17:15 +03:00
_context.LoginTitle = title;
_context.Username = username;
_context.Password = password;
_context.Notes = notes;
_context.PasswordOptions = DeserializeDictionary<PasswordGenerationOptions>(dict[Constants.AppExtensionPasswordGeneratorOptionsKey] as NSDictionary);
});
}
private bool ProcessChangePasswordProvider(NSItemProvider itemProvider)
{
return ProcessItemProvider(itemProvider, Constants.UTTypeAppExtensionChangePasswordAction, (dict) =>
{
var version = dict[Constants.AppExtensionVersionNumberKey] as NSNumber;
var url = dict[Constants.AppExtensionUrlStringKey] as NSString;
var title = dict[Constants.AppExtensionTitleKey] as NSString;
var sectionTitle = dict[Constants.AppExtensionSectionTitleKey] as NSString;
var username = dict[Constants.AppExtensionUsernameKey] as NSString;
var password = dict[Constants.AppExtensionPasswordKey] as NSString;
var oldPassword = dict[Constants.AppExtensionOldPasswordKey] as NSString;
var notes = dict[Constants.AppExtensionNotesKey] as NSString;
var fields = dict[Constants.AppExtensionFieldsKey] as NSDictionary;
if(url != null)
{
_context.UrlString = url;
}
2017-01-03 08:17:15 +03:00
_context.LoginTitle = title;
_context.Username = username;
_context.Password = password;
_context.OldPassword = oldPassword;
_context.Notes = notes;
_context.PasswordOptions = DeserializeDictionary<PasswordGenerationOptions>(dict[Constants.AppExtensionPasswordGeneratorOptionsKey] as NSDictionary);
});
}
private bool ProcessExtensionSetupProvider(NSItemProvider itemProvider)
{
if(itemProvider.HasItemConformingTo(Constants.UTTypeAppExtensionSetup))
{
_context.ProviderType = Constants.UTTypeAppExtensionSetup;
return true;
}
return false;
}
private T DeserializeDictionary<T>(NSDictionary dict)
{
if(dict != null)
{
NSError jsonError;
var jsonData = NSJsonSerialization.Serialize(dict, NSJsonWritingOptions.PrettyPrinted, out jsonError);
if(jsonData != null)
{
var jsonString = new NSString(jsonData, NSStringEncoding.UTF8);
return DeserializeString<T>(jsonString);
}
}
return default(T);
}
private T DeserializeString<T>(NSString jsonString)
{
if(jsonString != null)
{
var convertedObject = JsonConvert.DeserializeObject<T>(jsonString.ToString());
return convertedObject;
}
return default(T);
}
}
}