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:
114
src/Android/Autofill/AuthActivity.cs
Normal file
114
src/Android/Autofill/AuthActivity.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user