mirror of
https://github.com/bitwarden/android.git
synced 2025-01-11 18:57:39 +03:00
copy totp code on autofill
This commit is contained in:
parent
98e429505c
commit
1124c48c8d
10 changed files with 171 additions and 22 deletions
|
@ -29,6 +29,8 @@ namespace Bit.Android
|
||||||
private const string HockeyAppId = "d3834185b4a643479047b86c65293d42";
|
private const string HockeyAppId = "d3834185b4a643479047b86c65293d42";
|
||||||
private DateTime? _lastAction;
|
private DateTime? _lastAction;
|
||||||
private Java.Util.Regex.Pattern _otpPattern = Java.Util.Regex.Pattern.Compile("^.*?([cbdefghijklnrtuv]{32,64})$");
|
private Java.Util.Regex.Pattern _otpPattern = Java.Util.Regex.Pattern.Compile("^.*?([cbdefghijklnrtuv]{32,64})$");
|
||||||
|
private IDeviceActionService _deviceActionService;
|
||||||
|
private ISettings _settings;
|
||||||
|
|
||||||
protected override void OnCreate(Bundle bundle)
|
protected override void OnCreate(Bundle bundle)
|
||||||
{
|
{
|
||||||
|
@ -65,6 +67,8 @@ namespace Bit.Android
|
||||||
typeof(Color).GetProperty("Accent", BindingFlags.Public | BindingFlags.Static)
|
typeof(Color).GetProperty("Accent", BindingFlags.Public | BindingFlags.Static)
|
||||||
.SetValue(null, Color.FromHex("d2d6de"));
|
.SetValue(null, Color.FromHex("d2d6de"));
|
||||||
|
|
||||||
|
_deviceActionService = Resolver.Resolve<IDeviceActionService>();
|
||||||
|
_settings = Resolver.Resolve<ISettings>();
|
||||||
LoadApplication(new App.App(
|
LoadApplication(new App.App(
|
||||||
uri,
|
uri,
|
||||||
Resolver.Resolve<IAuthService>(),
|
Resolver.Resolve<IAuthService>(),
|
||||||
|
@ -72,13 +76,13 @@ namespace Bit.Android
|
||||||
Resolver.Resolve<IUserDialogs>(),
|
Resolver.Resolve<IUserDialogs>(),
|
||||||
Resolver.Resolve<IDatabaseService>(),
|
Resolver.Resolve<IDatabaseService>(),
|
||||||
Resolver.Resolve<ISyncService>(),
|
Resolver.Resolve<ISyncService>(),
|
||||||
Resolver.Resolve<ISettings>(),
|
_settings,
|
||||||
Resolver.Resolve<ILockService>(),
|
Resolver.Resolve<ILockService>(),
|
||||||
Resolver.Resolve<IGoogleAnalyticsService>(),
|
Resolver.Resolve<IGoogleAnalyticsService>(),
|
||||||
Resolver.Resolve<ILocalizeService>(),
|
Resolver.Resolve<ILocalizeService>(),
|
||||||
Resolver.Resolve<IAppInfoService>(),
|
Resolver.Resolve<IAppInfoService>(),
|
||||||
Resolver.Resolve<IAppSettingsService>(),
|
Resolver.Resolve<IAppSettingsService>(),
|
||||||
Resolver.Resolve<IDeviceActionService>()));
|
_deviceActionService));
|
||||||
|
|
||||||
MessagingCenter.Subscribe<Xamarin.Forms.Application>(
|
MessagingCenter.Subscribe<Xamarin.Forms.Application>(
|
||||||
Xamarin.Forms.Application.Current, "DismissKeyboard", (sender) =>
|
Xamarin.Forms.Application.Current, "DismissKeyboard", (sender) =>
|
||||||
|
@ -129,6 +133,13 @@ namespace Bit.Android
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var isPremium = Resolver.Resolve<ITokenService>()?.TokenPremium ?? false;
|
||||||
|
var autoCopyEnabled = !_settings.GetValueOrDefault(Constants.SettingDisableTotpCopy, false);
|
||||||
|
if(isPremium && autoCopyEnabled && _deviceActionService != null && login.Totp.Value != null)
|
||||||
|
{
|
||||||
|
_deviceActionService.CopyToClipboard(App.Utilities.Crypto.Totp(login.Totp.Value));
|
||||||
|
}
|
||||||
|
|
||||||
data.PutExtra("uri", login.Uri.Value);
|
data.PutExtra("uri", login.Uri.Value);
|
||||||
data.PutExtra("username", login.Username);
|
data.PutExtra("username", login.Username);
|
||||||
data.PutExtra("password", login.Password.Value);
|
data.PutExtra("password", login.Password.Value);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
public const string SettingPinUnlockOn = "setting:pinUnlockOn";
|
public const string SettingPinUnlockOn = "setting:pinUnlockOn";
|
||||||
public const string SettingLockSeconds = "setting:lockSeconds";
|
public const string SettingLockSeconds = "setting:lockSeconds";
|
||||||
public const string SettingGaOptOut = "setting:googleAnalyticsOptOut";
|
public const string SettingGaOptOut = "setting:googleAnalyticsOptOut";
|
||||||
|
public const string SettingDisableTotpCopy = "setting:disableAutoCopyTotp";
|
||||||
public const string AutofillPersistNotification = "setting:persistNotification";
|
public const string AutofillPersistNotification = "setting:persistNotification";
|
||||||
public const string AutofillPasswordField = "setting:autofillPasswordField";
|
public const string AutofillPasswordField = "setting:autofillPasswordField";
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ namespace Bit.App.Models.Page
|
||||||
Username = login.Username?.Decrypt(login.OrganizationId) ?? " ";
|
Username = login.Username?.Decrypt(login.OrganizationId) ?? " ";
|
||||||
Password = new Lazy<string>(() => login.Password?.Decrypt(login.OrganizationId));
|
Password = new Lazy<string>(() => login.Password?.Decrypt(login.OrganizationId));
|
||||||
Uri = new Lazy<string>(() => login.Uri?.Decrypt(login.OrganizationId));
|
Uri = new Lazy<string>(() => login.Uri?.Decrypt(login.OrganizationId));
|
||||||
|
Totp = new Lazy<string>(() => login.Totp?.Decrypt(login.OrganizationId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
@ -26,6 +27,7 @@ namespace Bit.App.Models.Page
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
public Lazy<string> Password { get; set; }
|
public Lazy<string> Password { get; set; }
|
||||||
public Lazy<string> Uri { get; set; }
|
public Lazy<string> Uri { get; set; }
|
||||||
|
public Lazy<string> Totp { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AutofillLogin : Login
|
public class AutofillLogin : Login
|
||||||
|
|
|
@ -27,6 +27,8 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
|
|
||||||
private StackLayout StackLayout { get; set; }
|
private StackLayout StackLayout { get; set; }
|
||||||
|
private ExtendedSwitchCell CopyTotpCell { get; set; }
|
||||||
|
private Label CopyTotpLabel { get; set; }
|
||||||
private ExtendedSwitchCell AnalyticsCell { get; set; }
|
private ExtendedSwitchCell AnalyticsCell { get; set; }
|
||||||
private Label AnalyticsLabel { get; set; }
|
private Label AnalyticsLabel { get; set; }
|
||||||
private ExtendedSwitchCell AutofillPersistNotificationCell { get; set; }
|
private ExtendedSwitchCell AutofillPersistNotificationCell { get; set; }
|
||||||
|
@ -38,6 +40,23 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
private void Init()
|
private void Init()
|
||||||
{
|
{
|
||||||
|
CopyTotpCell = new ExtendedSwitchCell
|
||||||
|
{
|
||||||
|
Text = AppResources.DisableAutoTotpCopy,
|
||||||
|
On = _settings.GetValueOrDefault(Constants.SettingDisableTotpCopy, false)
|
||||||
|
};
|
||||||
|
|
||||||
|
var totpTable = new FormTableView(true)
|
||||||
|
{
|
||||||
|
Root = new TableRoot
|
||||||
|
{
|
||||||
|
new TableSection(" ")
|
||||||
|
{
|
||||||
|
CopyTotpCell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
AnalyticsCell = new ExtendedSwitchCell
|
AnalyticsCell = new ExtendedSwitchCell
|
||||||
{
|
{
|
||||||
Text = AppResources.DisableGA,
|
Text = AppResources.DisableGA,
|
||||||
|
@ -55,18 +74,19 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
AnalyticsLabel = new Label
|
CopyTotpLabel = new FormTableLabel(this)
|
||||||
{
|
{
|
||||||
Text = AppResources.DisbaleGADescription,
|
Text = AppResources.DisableAutoTotpCopyDescription
|
||||||
LineBreakMode = LineBreakMode.WordWrap,
|
};
|
||||||
FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)),
|
|
||||||
Style = (Style)Application.Current.Resources["text-muted"],
|
AnalyticsLabel = new FormTableLabel(this)
|
||||||
Margin = new Thickness(15, (this.IsLandscape() ? 5 : 0), 15, 25)
|
{
|
||||||
|
Text = AppResources.DisableGADescription
|
||||||
};
|
};
|
||||||
|
|
||||||
StackLayout = new StackLayout
|
StackLayout = new StackLayout
|
||||||
{
|
{
|
||||||
Children = { analyticsTable, AnalyticsLabel },
|
Children = { totpTable, CopyTotpLabel, analyticsTable, AnalyticsLabel },
|
||||||
Spacing = 0
|
Spacing = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,7 +98,7 @@ namespace Bit.App.Pages
|
||||||
On = !_appSettings.AutofillPersistNotification && !_appSettings.AutofillPasswordField
|
On = !_appSettings.AutofillPersistNotification && !_appSettings.AutofillPasswordField
|
||||||
};
|
};
|
||||||
|
|
||||||
var autofillAlwaysTable = new FormTableView
|
var autofillAlwaysTable = new FormTableView(true)
|
||||||
{
|
{
|
||||||
Root = new TableRoot
|
Root = new TableRoot
|
||||||
{
|
{
|
||||||
|
@ -102,7 +122,6 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
var autofillPersistNotificationTable = new FormTableView
|
var autofillPersistNotificationTable = new FormTableView
|
||||||
{
|
{
|
||||||
NoHeader = true,
|
|
||||||
Root = new TableRoot
|
Root = new TableRoot
|
||||||
{
|
{
|
||||||
new TableSection(" ")
|
new TableSection(" ")
|
||||||
|
@ -125,7 +144,6 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
var autofillPasswordFieldTable = new FormTableView
|
var autofillPasswordFieldTable = new FormTableView
|
||||||
{
|
{
|
||||||
NoHeader = true,
|
|
||||||
Root = new TableRoot
|
Root = new TableRoot
|
||||||
{
|
{
|
||||||
new TableSection(" ")
|
new TableSection(" ")
|
||||||
|
@ -169,6 +187,7 @@ namespace Bit.App.Pages
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
|
|
||||||
AnalyticsCell.OnChanged += AnalyticsCell_Changed;
|
AnalyticsCell.OnChanged += AnalyticsCell_Changed;
|
||||||
|
CopyTotpCell.OnChanged += CopyTotpCell_OnChanged;
|
||||||
StackLayout.LayoutChanged += Layout_LayoutChanged;
|
StackLayout.LayoutChanged += Layout_LayoutChanged;
|
||||||
|
|
||||||
if(Device.RuntimePlatform == Device.Android)
|
if(Device.RuntimePlatform == Device.Android)
|
||||||
|
@ -184,6 +203,7 @@ namespace Bit.App.Pages
|
||||||
base.OnDisappearing();
|
base.OnDisappearing();
|
||||||
|
|
||||||
AnalyticsCell.OnChanged -= AnalyticsCell_Changed;
|
AnalyticsCell.OnChanged -= AnalyticsCell_Changed;
|
||||||
|
CopyTotpCell.OnChanged -= CopyTotpCell_OnChanged;
|
||||||
StackLayout.LayoutChanged -= Layout_LayoutChanged;
|
StackLayout.LayoutChanged -= Layout_LayoutChanged;
|
||||||
|
|
||||||
if(Device.RuntimePlatform == Device.Android)
|
if(Device.RuntimePlatform == Device.Android)
|
||||||
|
@ -211,6 +231,17 @@ namespace Bit.App.Pages
|
||||||
_googleAnalyticsService.SetAppOptOut(cell.On);
|
_googleAnalyticsService.SetAppOptOut(cell.On);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CopyTotpCell_OnChanged(object sender, ToggledEventArgs e)
|
||||||
|
{
|
||||||
|
var cell = sender as ExtendedSwitchCell;
|
||||||
|
if(cell == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_settings.AddOrUpdateValue(Constants.SettingDisableTotpCopy, cell.On);
|
||||||
|
}
|
||||||
|
|
||||||
private void AutofillAlwaysCell_OnChanged(object sender, ToggledEventArgs e)
|
private void AutofillAlwaysCell_OnChanged(object sender, ToggledEventArgs e)
|
||||||
{
|
{
|
||||||
var cell = sender as ExtendedSwitchCell;
|
var cell = sender as ExtendedSwitchCell;
|
||||||
|
@ -262,7 +293,7 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
private class FormTableView : ExtendedTableView
|
private class FormTableView : ExtendedTableView
|
||||||
{
|
{
|
||||||
public FormTableView()
|
public FormTableView(bool header = false)
|
||||||
{
|
{
|
||||||
Intent = TableIntent.Settings;
|
Intent = TableIntent.Settings;
|
||||||
EnableScrolling = false;
|
EnableScrolling = false;
|
||||||
|
@ -270,6 +301,7 @@ namespace Bit.App.Pages
|
||||||
EnableSelection = true;
|
EnableSelection = true;
|
||||||
VerticalOptions = LayoutOptions.Start;
|
VerticalOptions = LayoutOptions.Start;
|
||||||
NoFooter = true;
|
NoFooter = true;
|
||||||
|
NoHeader = !header;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
40
src/App/Resources/AppResources.Designer.cs
generated
40
src/App/Resources/AppResources.Designer.cs
generated
|
@ -574,6 +574,15 @@ namespace Bit.App.Resources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Copied TOTP!.
|
||||||
|
/// </summary>
|
||||||
|
public static string CopiedTotp {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CopiedTotp", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Copied username!.
|
/// Looks up a localized string similar to Copied username!.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -601,6 +610,15 @@ namespace Bit.App.Resources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Copy TOTP.
|
||||||
|
/// </summary>
|
||||||
|
public static string CopyTotp {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CopyTotp", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Copy Username.
|
/// Looks up a localized string similar to Copy Username.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -664,6 +682,24 @@ namespace Bit.App.Resources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Disable Automatic TOTP Copy.
|
||||||
|
/// </summary>
|
||||||
|
public static string DisableAutoTotpCopy {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("DisableAutoTotpCopy", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to If your login has an authenticator key attached to it, the TOTP verification code is automatically copied to your clipboard whenever you auto-fill the login..
|
||||||
|
/// </summary>
|
||||||
|
public static string DisableAutoTotpCopyDescription {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("DisableAutoTotpCopyDescription", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Disabled.
|
/// Looks up a localized string similar to Disabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -685,9 +721,9 @@ namespace Bit.App.Resources {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to We use analytics to better learn how the app is being used so that we can make it better. All data collection is completely anonymous..
|
/// Looks up a localized string similar to We use analytics to better learn how the app is being used so that we can make it better. All data collection is completely anonymous..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string DisbaleGADescription {
|
public static string DisableGADescription {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("DisbaleGADescription", resourceCulture);
|
return ResourceManager.GetString("DisableGADescription", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -822,7 +822,7 @@
|
||||||
<data name="ShareVaultDescription" xml:space="preserve">
|
<data name="ShareVaultDescription" xml:space="preserve">
|
||||||
<value>Create an organization to securely share your logins with other users.</value>
|
<value>Create an organization to securely share your logins with other users.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DisbaleGADescription" xml:space="preserve">
|
<data name="DisableGADescription" xml:space="preserve">
|
||||||
<value>We use analytics to better learn how the app is being used so that we can make it better. All data collection is completely anonymous.</value>
|
<value>We use analytics to better learn how the app is being used so that we can make it better. All data collection is completely anonymous.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Features" xml:space="preserve">
|
<data name="Features" xml:space="preserve">
|
||||||
|
@ -950,4 +950,16 @@
|
||||||
<data name="Photos" xml:space="preserve">
|
<data name="Photos" xml:space="preserve">
|
||||||
<value>Photos</value>
|
<value>Photos</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CopiedTotp" xml:space="preserve">
|
||||||
|
<value>Copied TOTP!</value>
|
||||||
|
</data>
|
||||||
|
<data name="CopyTotp" xml:space="preserve">
|
||||||
|
<value>Copy TOTP</value>
|
||||||
|
</data>
|
||||||
|
<data name="DisableAutoTotpCopyDescription" xml:space="preserve">
|
||||||
|
<value>If your login has an authenticator key attached to it, the TOTP verification code is automatically copied to your clipboard whenever you auto-fill the login.</value>
|
||||||
|
</data>
|
||||||
|
<data name="DisableAutoTotpCopy" xml:space="preserve">
|
||||||
|
<value>Disable Automatic TOTP Copy</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -191,7 +191,7 @@ namespace Bit.iOS.Extension
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CompleteUsernamePasswordRequest(string username, string password)
|
public void CompleteUsernamePasswordRequest(string username, string password, string totp)
|
||||||
{
|
{
|
||||||
NSDictionary itemData = null;
|
NSDictionary itemData = null;
|
||||||
if(_context.ProviderType == UTType.PropertyList)
|
if(_context.ProviderType == UTType.PropertyList)
|
||||||
|
@ -227,6 +227,11 @@ namespace Bit.iOS.Extension
|
||||||
Constants.AppExtensionOldPasswordKey, password);
|
Constants.AppExtensionOldPasswordKey, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!string.IsNullOrWhiteSpace(totp))
|
||||||
|
{
|
||||||
|
UIPasteboard.General.String = totp;
|
||||||
|
}
|
||||||
|
|
||||||
CompleteRequest(itemData);
|
CompleteRequest(itemData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -176,7 +176,8 @@ namespace Bit.iOS.Extension
|
||||||
}
|
}
|
||||||
else if(LoadingController != null)
|
else if(LoadingController != null)
|
||||||
{
|
{
|
||||||
LoadingController.CompleteUsernamePasswordRequest(UsernameCell.TextField.Text, PasswordCell.TextField.Text);
|
LoadingController.CompleteUsernamePasswordRequest(UsernameCell.TextField.Text, PasswordCell.TextField.Text,
|
||||||
|
null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(saveTask.Result.Errors.Count() > 0)
|
else if(saveTask.Result.Errors.Count() > 0)
|
||||||
|
|
|
@ -104,17 +104,22 @@ namespace Bit.iOS.Extension
|
||||||
private IEnumerable<LoginViewModel> _tableItems = new List<LoginViewModel>();
|
private IEnumerable<LoginViewModel> _tableItems = new List<LoginViewModel>();
|
||||||
private Context _context;
|
private Context _context;
|
||||||
private LoginListViewController _controller;
|
private LoginListViewController _controller;
|
||||||
|
private ILoginService _loginService;
|
||||||
|
private ISettings _settings;
|
||||||
|
private bool _isPremium;
|
||||||
|
|
||||||
public TableSource(LoginListViewController controller)
|
public TableSource(LoginListViewController controller)
|
||||||
{
|
{
|
||||||
_context = controller.Context;
|
_context = controller.Context;
|
||||||
_controller = controller;
|
_controller = controller;
|
||||||
|
_isPremium = Resolver.Resolve<ITokenService>()?.TokenPremium ?? false;
|
||||||
|
_loginService = Resolver.Resolve<ILoginService>();
|
||||||
|
_settings = Resolver.Resolve<ISettings>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoadItemsAsync()
|
public async Task LoadItemsAsync()
|
||||||
{
|
{
|
||||||
var loginService = Resolver.Resolve<ILoginService>();
|
var logins = await _loginService.GetAllAsync(_context.UrlString);
|
||||||
var logins = await loginService.GetAllAsync(_context.UrlString);
|
|
||||||
_tableItems = logins?.Item1?.Select(s => new LoginViewModel(s))
|
_tableItems = logins?.Item1?.Select(s => new LoginViewModel(s))
|
||||||
.OrderBy(s => s.Name)
|
.OrderBy(s => s.Name)
|
||||||
.ThenBy(s => s.Username)
|
.ThenBy(s => s.Username)
|
||||||
|
@ -184,9 +189,16 @@ namespace Bit.iOS.Extension
|
||||||
|
|
||||||
if(_controller.CanAutoFill() && !string.IsNullOrWhiteSpace(item.Password))
|
if(_controller.CanAutoFill() && !string.IsNullOrWhiteSpace(item.Password))
|
||||||
{
|
{
|
||||||
_controller.LoadingController.CompleteUsernamePasswordRequest(item.Username, item.Password);
|
string totp = null;
|
||||||
|
if(!_settings.GetValueOrDefault(App.Constants.SettingDisableTotpCopy, false))
|
||||||
|
{
|
||||||
|
totp = GetTotp(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
_controller.LoadingController.CompleteUsernamePasswordRequest(item.Username, item.Password, totp);
|
||||||
}
|
}
|
||||||
else if(!string.IsNullOrWhiteSpace(item.Username) || !string.IsNullOrWhiteSpace(item.Password))
|
else if(!string.IsNullOrWhiteSpace(item.Username) || !string.IsNullOrWhiteSpace(item.Password) ||
|
||||||
|
!string.IsNullOrWhiteSpace(item.Totp.Value))
|
||||||
{
|
{
|
||||||
var sheet = Dialogs.CreateActionSheet(item.Name, _controller);
|
var sheet = Dialogs.CreateActionSheet(item.Name, _controller);
|
||||||
if(!string.IsNullOrWhiteSpace(item.Username))
|
if(!string.IsNullOrWhiteSpace(item.Username))
|
||||||
|
@ -217,6 +229,26 @@ namespace Bit.iOS.Extension
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!string.IsNullOrWhiteSpace(item.Totp.Value))
|
||||||
|
{
|
||||||
|
sheet.AddAction(UIAlertAction.Create(AppResources.CopyTotp, UIAlertActionStyle.Default, a =>
|
||||||
|
{
|
||||||
|
var totp = GetTotp(item);
|
||||||
|
if(string.IsNullOrWhiteSpace(totp))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UIPasteboard clipboard = UIPasteboard.General;
|
||||||
|
clipboard.String = totp;
|
||||||
|
var alert = Dialogs.CreateMessageAlert(AppResources.CopiedTotp);
|
||||||
|
_controller.PresentViewController(alert, true, () =>
|
||||||
|
{
|
||||||
|
_controller.DismissViewController(true, null);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
sheet.AddAction(UIAlertAction.Create(AppResources.Cancel, UIAlertActionStyle.Cancel, null));
|
sheet.AddAction(UIAlertAction.Create(AppResources.Cancel, UIAlertActionStyle.Cancel, null));
|
||||||
_controller.PresentViewController(sheet, true, null);
|
_controller.PresentViewController(sheet, true, null);
|
||||||
}
|
}
|
||||||
|
@ -226,6 +258,20 @@ namespace Bit.iOS.Extension
|
||||||
_controller.PresentViewController(alert, true, null);
|
_controller.PresentViewController(alert, true, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetTotp(LoginViewModel item)
|
||||||
|
{
|
||||||
|
string totp = null;
|
||||||
|
if(_isPremium)
|
||||||
|
{
|
||||||
|
if(item != null && !string.IsNullOrWhiteSpace(item.Totp.Value))
|
||||||
|
{
|
||||||
|
totp = App.Utilities.Crypto.Totp(item.Totp.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Bit.iOS.Extension.Models
|
namespace Bit.iOS.Extension.Models
|
||||||
{
|
{
|
||||||
|
@ -11,6 +12,7 @@ namespace Bit.iOS.Extension.Models
|
||||||
Username = login.Username?.Decrypt(login.OrganizationId);
|
Username = login.Username?.Decrypt(login.OrganizationId);
|
||||||
Password = login.Password?.Decrypt(login.OrganizationId);
|
Password = login.Password?.Decrypt(login.OrganizationId);
|
||||||
Uri = login.Uri?.Decrypt(login.OrganizationId);
|
Uri = login.Uri?.Decrypt(login.OrganizationId);
|
||||||
|
Totp = new Lazy<string>(() => login.Totp?.Decrypt(login.OrganizationId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
@ -18,5 +20,6 @@ namespace Bit.iOS.Extension.Models
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
public string Uri { get; set; }
|
public string Uri { get; set; }
|
||||||
|
public Lazy<string> Totp { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue