1
0
mirror of https://github.com/bitwarden/mobile synced 2026-01-08 11:33:31 +00:00

auth activity for locked vaults when autofilling

This commit is contained in:
Kyle Spearrin
2017-11-17 00:16:45 -05:00
parent 0a6767209d
commit 322b251def
7 changed files with 1256 additions and 405 deletions

View File

@@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Support.V7.App;
using Android.Views.Autofill;
using Android.App.Assist;
using XLabs.Ioc;
using Bit.App.Abstractions;
using Bit.App.Resources;
namespace Bit.Android.Autofill
{
[Activity(Label = "AuthActivity")]
public class AuthActivity : AppCompatActivity
{
private EditText _masterPassword;
private Intent _replyIntent = null;
protected override void OnCreate(Bundle state)
{
base.OnCreate(state);
SetContentView(Resource.Layout.autofill_authactivity);
var masterLoginLabel = FindViewById(Resource.Id.master_login_header) as TextView;
masterLoginLabel.Text = AppResources.VerifyMasterPassword;
var masterPasswordLabel = FindViewById(Resource.Id.password_label) as TextView;
masterPasswordLabel.Text = AppResources.MasterPassword;
_masterPassword = FindViewById(Resource.Id.master_password) as EditText;
var loginButton = FindViewById(Resource.Id.login) as TextView;
loginButton.Text = AppResources.LogIn;
loginButton.Click += (sender, e) => {
Login();
};
var cancelButton = FindViewById(Resource.Id.cancel) as TextView;
cancelButton.Text = AppResources.Cancel;
cancelButton.Click += (sender, e) => {
_replyIntent = null;
Finish();
};
}
public override void Finish()
{
if(_replyIntent != null)
{
SetResult(Result.Ok, _replyIntent);
}
else
{
SetResult(Result.Canceled);
}
base.Finish();
}
private void Login()
{
var password = _masterPassword.Text;
if(true) // Check password
{
Success();
}
else
{
Toast.MakeText(this, "Password incorrect", ToastLength.Short).Show();
_replyIntent = null;
}
Finish();
}
private async void Success()
{
var structure = Intent.GetParcelableExtra(AutofillManager.ExtraAssistStructure) as AssistStructure;
if(structure == null)
{
_replyIntent = null;
return;
}
var parser = new Parser(structure);
parser.ParseForFill();
if(!parser.FieldCollection.Fields.Any() || string.IsNullOrWhiteSpace(parser.Uri))
{
_replyIntent = null;
return;
}
var items = await AutofillHelpers.GetFillItemsAsync(Resolver.Resolve<ICipherService>(), parser.Uri);
if(!items.Any())
{
_replyIntent = null;
return;
}
var response = AutofillHelpers.BuildFillResponse(this, parser.FieldCollection, items);
_replyIntent = new Intent();
_replyIntent.PutExtra(AutofillManager.ExtraAuthenticationResult, response);
}
}
}

View File

@@ -5,56 +5,70 @@ using Android.Service.Autofill;
using Android.Views;
using Android.Widget;
using System.Linq;
using Android.App;
using Bit.App.Abstractions;
using System.Threading.Tasks;
namespace Bit.Android.Autofill
{
public static class AutofillHelpers
{
public static FillResponse BuildFillResponse(Context context, bool auth, FieldCollection fields,
IDictionary<string, IFilledItem> items)
public static async Task<List<IFilledItem>> GetFillItemsAsync(ICipherService service, string uri)
{
var items = new List<IFilledItem>();
var ciphers = await service.GetAllAsync(uri);
if(ciphers.Item1.Any() || ciphers.Item2.Any())
{
var allCiphers = ciphers.Item1.ToList();
allCiphers.AddRange(ciphers.Item2.ToList());
foreach(var cipher in allCiphers)
{
items.Add(new CipherFilledItem(cipher));
}
}
return items;
}
public static FillResponse BuildFillResponse(Context context, FieldCollection fields, List<IFilledItem> items)
{
var responseBuilder = new FillResponse.Builder();
if(items != null)
{
foreach(var datasetName in items.Keys)
foreach(var item in items)
{
var dataset = BuildDataset(context, fields, items[datasetName], auth);
var dataset = BuildDataset(context, fields, item);
if(dataset != null)
{
responseBuilder.AddDataset(dataset);
}
}
}
var info = new SaveInfo.Builder(fields.SaveType, fields.AutofillIds.ToArray()).Build();
responseBuilder.SetSaveInfo(info);
return responseBuilder.Build();
}
public static Dataset BuildDataset(Context context, FieldCollection fields, IFilledItem filledItem, bool auth)
public static Dataset BuildDataset(Context context, FieldCollection fields, IFilledItem filledItem)
{
Dataset.Builder datasetBuilder;
if(auth)
{
datasetBuilder = new Dataset.Builder(
BuildListView(context.PackageName, filledItem.Name, filledItem.Subtitle, Resource.Drawable.fa_lock));
//IntentSender sender = AuthActivity.getAuthIntentSenderForDataset(context, datasetName);
//datasetBuilder.SetAuthentication(sender);
}
else
{
datasetBuilder = new Dataset.Builder(
BuildListView(context.PackageName, filledItem.Name, filledItem.Subtitle, filledItem.Icon));
}
var datasetBuilder = new Dataset.Builder(
BuildListView(context.PackageName, filledItem.Name, filledItem.Subtitle, filledItem.Icon));
if(filledItem.ApplyToFields(fields, datasetBuilder))
{
return datasetBuilder.Build();
}
return null;
}
public static FillResponse BuildAuthResponse(Context context, FieldCollection fields)
{
var responseBuilder = new FillResponse.Builder();
var view = BuildListView(context.PackageName, "Autofill with bitwarden",
"Vault locked", Resource.Drawable.icon);
var intent = new Intent(context, typeof(AuthActivity));
var pendingIntent = PendingIntent.GetActivity(context, 0, intent, PendingIntentFlags.CancelCurrent);
responseBuilder.SetAuthentication(fields.AutofillIds.ToArray(), pendingIntent.IntentSender, view);
return responseBuilder.Build();
}
public static RemoteViews BuildListView(string packageName, string text, string subtext, int iconId)
{
var view = new RemoteViews(packageName, Resource.Layout.autofill_listitem);

View File

@@ -1,5 +1,6 @@
using Android;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Service.Autofill;
@@ -18,6 +19,7 @@ namespace Bit.Android.Autofill
public class AutofillService : global::Android.Service.Autofill.AutofillService
{
private ICipherService _cipherService;
private ILockService _lockService;
public async override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback)
{
@@ -27,8 +29,6 @@ namespace Bit.Android.Autofill
return;
}
var clientState = request.ClientState;
var parser = new Parser(structure);
parser.ParseForFill();
@@ -37,30 +37,31 @@ namespace Bit.Android.Autofill
return;
}
if(_lockService == null)
{
_lockService = Resolver.Resolve<ILockService>();
}
if(true) // if locked
{
var authResponse = AutofillHelpers.BuildAuthResponse(this, parser.FieldCollection);
callback.OnSuccess(authResponse);
return;
}
if(_cipherService == null)
{
_cipherService = Resolver.Resolve<ICipherService>();
}
// build response
var items = new Dictionary<string, IFilledItem>();
var ciphers = await _cipherService.GetAllAsync(parser.Uri);
if(ciphers.Item1.Any() || ciphers.Item2.Any())
{
var allCiphers = ciphers.Item1.ToList();
allCiphers.AddRange(ciphers.Item2.ToList());
foreach(var cipher in allCiphers)
{
items.Add(cipher.Id, new CipherFilledItem(cipher));
}
}
var items = await AutofillHelpers.GetFillItemsAsync(_cipherService, parser.Uri);
if(!items.Any())
{
return;
}
var response = AutofillHelpers.BuildFillResponse(this, false, parser.FieldCollection, items);
var response = AutofillHelpers.BuildFillResponse(this, parser.FieldCollection, items);
callback.OnSuccess(response);
}