1
0
mirror of https://github.com/bitwarden/mobile synced 2026-01-05 01:53:17 +00:00

PM-5154 [Passkeys iOS] Refactored and added cipher selection for passkey creation on autofill search.

This commit is contained in:
Federico Maccaroni
2024-02-20 18:45:32 -03:00
parent c11df272be
commit 25f63ec4e0
7 changed files with 116 additions and 89 deletions

View File

@@ -0,0 +1,10 @@
using Bit.iOS.Autofill.Models;
namespace Bit.iOS.Autofill
{
public interface ILoginListViewController
{
Context Context { get; }
CredentialProviderViewController CPViewController { get; }
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.Core.Abstractions;
using Bit.Core.Resources.Localization;
@@ -19,7 +18,7 @@ using UIKit;
namespace Bit.iOS.Autofill
{
public partial class LoginListViewController : ExtendedUIViewController
public partial class LoginListViewController : ExtendedUIViewController, ILoginListViewController
{
internal const string HEADER_SECTION_IDENTIFIER = "headerSectionId";
@@ -30,12 +29,10 @@ namespace Bit.iOS.Autofill
: base(handle)
{
DismissModalAction = Cancel;
PasswordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
}
public Context Context { get; set; }
public CredentialProviderViewController CPViewController { get; set; }
public IPasswordRepromptService PasswordRepromptService { get; private set; }
AccountSwitchingOverlayView _accountSwitchingOverlayView;
AccountSwitchingOverlayHelper _accountSwitchingOverlayHelper;
@@ -254,20 +251,14 @@ namespace Bit.iOS.Autofill
base.Dispose(disposing);
}
public class TableSource : ExtensionTableSource
public class TableSource : BaseLoginListTableSource<LoginListViewController>
{
private readonly LoginListViewController _controller;
private readonly LazyResolve<IPlatformUtilsService> _platformUtilsService = new LazyResolve<IPlatformUtilsService>();
private readonly LazyResolve<IPasswordRepromptService> _passwordRepromptService = new LazyResolve<IPasswordRepromptService>();
public TableSource(LoginListViewController controller)
: base(controller.Context, controller)
: base(controller)
{
_controller = controller;
}
private Context Context => (Context)_context;
protected override string LoginAddSegue => SegueConstants.ADD_LOGIN;
public override async Task LoadAsync(bool urlFilter = true, string searchFilter = null)
{
@@ -277,7 +268,7 @@ namespace Bit.iOS.Autofill
if (Context.IsCreatingPasskey && !Items.Any())
{
_controller?.OnEmptyList();
Controller?.OnEmptyList();
}
}
catch (Exception ex)
@@ -316,62 +307,6 @@ namespace Bit.iOS.Autofill
return base.RowsInSection(tableview, section);
}
public async override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
try
{
if (Context.IsCreatingPasskey)
{
await SelectRowForPasskeyCreationAsync(tableView, indexPath);
return;
}
await AutofillHelpers.TableRowSelectedAsync(tableView, indexPath, this,
_controller.CPViewController, _controller, _controller.PasswordRepromptService, "loginAddSegue");
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
}
private async Task SelectRowForPasskeyCreationAsync(UITableView tableView, NSIndexPath indexPath)
{
tableView.DeselectRow(indexPath, true);
tableView.EndEditing(true);
var item = Items.ElementAt(indexPath.Row);
if (item is null)
{
await _platformUtilsService.Value.ShowDialogAsync(AppResources.GenericErrorMessage, AppResources.AnErrorHasOccurred);
return;
}
if (item.CipherView.Login.HasFido2Credentials
&&
!await _platformUtilsService.Value.ShowDialogAsync(
AppResources.ThisItemAlreadyContainsAPasskeyAreYouSureYouWantToOverwriteTheCurrentPasskey,
AppResources.OverwritePasskey,
AppResources.Yes,
AppResources.No))
{
return;
}
if (!await _passwordRepromptService.Value.PromptAndCheckPasswordIfNeededAsync(item.Reprompt))
{
return;
}
// TODO: Check user verification
Context.ConfirmNewCredentialTcs.SetResult(new Fido2ConfirmNewCredentialResult
{
CipherId = item.Id,
UserVerified = true
});
}
}
}
}

View File

@@ -12,19 +12,17 @@ using Bit.Core.Utilities;
namespace Bit.iOS.Autofill
{
public partial class LoginSearchViewController : ExtendedUITableViewController
public partial class LoginSearchViewController : ExtendedUITableViewController, ILoginListViewController
{
public LoginSearchViewController(IntPtr handle)
: base(handle)
{
DismissModalAction = Cancel;
PasswordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
}
public Context Context { get; set; }
public CredentialProviderViewController CPViewController { get; set; }
public bool FromList { get; set; }
public IPasswordRepromptService PasswordRepromptService { get; private set; }
public async override void ViewDidLoad()
{
@@ -61,13 +59,13 @@ namespace Bit.iOS.Autofill
}
else
{
CPViewController.CompleteRequest();
CPViewController.CancelRequest(AuthenticationServices.ASExtensionErrorCode.UserCanceled);
}
}
partial void AddBarButton_Activated(UIBarButtonItem sender)
{
PerformSegue("loginAddFromSearchSegue", this);
PerformSegue(SegueConstants.ADD_LOGIN_FROM_SEARCH, this);
}
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
@@ -93,24 +91,14 @@ namespace Bit.iOS.Autofill
});
}
public class TableSource : ExtensionTableSource
public class TableSource : BaseLoginListTableSource<LoginSearchViewController>
{
private Context _context;
private LoginSearchViewController _controller;
public TableSource(LoginSearchViewController controller)
: base(controller.Context, controller)
: base(controller)
{
_context = controller.Context;
_controller = controller;
}
public async override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
await AutofillHelpers.TableRowSelectedAsync(tableView, indexPath, this,
_controller.CPViewController, _controller, _controller.PasswordRepromptService,
"loginAddFromSearchSegue");
}
protected override string LoginAddSegue => SegueConstants.ADD_LOGIN_FROM_SEARCH;
}
}
}

View File

@@ -8,5 +8,6 @@
public const string SETUP = "setupSegue";
public const string ADD_LOGIN = "loginAddSegue";
public const string LOGIN_SEARCH_FROM_LIST = "loginSearchFromListSegue";
public const string ADD_LOGIN_FROM_SEARCH = "loginAddFromSearchSegue";
}
}

View File

@@ -0,0 +1,91 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.Core.Abstractions;
using Bit.Core.Resources.Localization;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Bit.iOS.Autofill.Models;
using Bit.iOS.Core.Views;
using Foundation;
using UIKit;
namespace Bit.iOS.Autofill.Utilities
{
public abstract class BaseLoginListTableSource<T> : ExtensionTableSource
where T : UIViewController, ILoginListViewController
{
private IPasswordRepromptService _passwordRepromptService;
private readonly LazyResolve<IPlatformUtilsService> _platformUtilsService = new LazyResolve<IPlatformUtilsService>();
public BaseLoginListTableSource(T controller)
: base(controller.Context, controller)
{
_controller = controller;
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>();
}
protected Context Context => (Context)_context;
protected T Controller => (T)_controller;
protected abstract string LoginAddSegue { get; }
public async override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
try
{
if (Context.IsCreatingPasskey)
{
await SelectRowForPasskeyCreationAsync(tableView, indexPath);
return;
}
await AutofillHelpers.TableRowSelectedAsync(tableView, indexPath, this,
Controller.CPViewController, Controller, _passwordRepromptService, LoginAddSegue);
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
}
private async Task SelectRowForPasskeyCreationAsync(UITableView tableView, NSIndexPath indexPath)
{
tableView.DeselectRow(indexPath, true);
tableView.EndEditing(true);
var item = Items.ElementAt(indexPath.Row);
if (item is null)
{
await _platformUtilsService.Value.ShowDialogAsync(AppResources.GenericErrorMessage, AppResources.AnErrorHasOccurred);
return;
}
if (item.CipherView.Login.HasFido2Credentials
&&
!await _platformUtilsService.Value.ShowDialogAsync(
AppResources.ThisItemAlreadyContainsAPasskeyAreYouSureYouWantToOverwriteTheCurrentPasskey,
AppResources.OverwritePasskey,
AppResources.Yes,
AppResources.No))
{
return;
}
if (!await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(item.Reprompt))
{
return;
}
// TODO: Check user verification
Context.ConfirmNewCredentialTcs.SetResult(new Fido2ConfirmNewCredentialResult
{
CipherId = item.Id,
UserVerified = true
});
}
}
}

View File

@@ -88,6 +88,8 @@
<Compile Include="SegueConstants.cs" />
<Compile Include="ColorConstants.cs" />
<Compile Include="ListItems\HeaderItemView.cs" />
<Compile Include="Utilities\BaseLoginListTableSource.cs" />
<Compile Include="ILoginListViewController.cs" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\check.png" />

View File

@@ -21,7 +21,7 @@ namespace Bit.iOS.Core.Views
protected IStateService _stateService;
protected ISearchService _searchService;
protected AppExtensionContext _context;
private UIViewController _controller;
protected UIViewController _controller;
public ExtensionTableSource(AppExtensionContext context, UIViewController controller)
{