2022-02-25 21:47:21 +01:00
|
|
|
|
using System;
|
2021-12-10 21:41:36 +01:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2022-08-17 23:10:16 +02:00
|
|
|
|
using System.Threading;
|
2021-12-10 21:41:36 +01:00
|
|
|
|
using System.Threading.Tasks;
|
2022-07-05 23:14:46 +02:00
|
|
|
|
using System.Windows.Input;
|
2021-12-10 21:41:36 +01:00
|
|
|
|
using Bit.App.Abstractions;
|
2022-09-09 20:58:11 +02:00
|
|
|
|
using Bit.App.Lists.ItemViewModels.CustomFields;
|
2019-04-24 17:23:03 +02:00
|
|
|
|
using Bit.App.Resources;
|
2019-04-27 05:58:15 +02:00
|
|
|
|
using Bit.App.Utilities;
|
2022-01-21 10:31:03 +01:00
|
|
|
|
using Bit.Core;
|
2019-04-24 17:23:03 +02:00
|
|
|
|
using Bit.Core.Abstractions;
|
2021-05-21 15:13:54 +02:00
|
|
|
|
using Bit.Core.Enums;
|
2019-05-10 19:47:59 +02:00
|
|
|
|
using Bit.Core.Exceptions;
|
2019-04-24 17:23:03 +02:00
|
|
|
|
using Bit.Core.Models.View;
|
|
|
|
|
using Bit.Core.Utilities;
|
2022-07-05 23:14:46 +02:00
|
|
|
|
using Xamarin.CommunityToolkit.ObjectModel;
|
2019-04-26 06:26:09 +02:00
|
|
|
|
using Xamarin.Forms;
|
2019-04-24 17:23:03 +02:00
|
|
|
|
|
|
|
|
|
namespace Bit.App.Pages
|
|
|
|
|
{
|
2022-09-09 20:58:11 +02:00
|
|
|
|
public class CipherDetailsPageViewModel : BaseCipherViewModel, IPasswordPromptable
|
2019-04-24 17:23:03 +02:00
|
|
|
|
{
|
|
|
|
|
private readonly ICipherService _cipherService;
|
2022-02-23 18:40:17 +01:00
|
|
|
|
private readonly IStateService _stateService;
|
2022-08-17 23:10:16 +02:00
|
|
|
|
private readonly IAuditService _auditService;
|
2019-04-26 06:26:09 +02:00
|
|
|
|
private readonly ITotpService _totpService;
|
2019-05-10 19:47:59 +02:00
|
|
|
|
private readonly IMessagingService _messagingService;
|
2019-07-12 23:29:40 +02:00
|
|
|
|
private readonly IEventService _eventService;
|
2021-05-21 15:13:54 +02:00
|
|
|
|
private readonly IPasswordRepromptService _passwordRepromptService;
|
2021-10-28 18:52:41 +02:00
|
|
|
|
private readonly ILocalizeService _localizeService;
|
2022-09-09 20:58:11 +02:00
|
|
|
|
private readonly ICustomFieldItemFactory _customFieldItemFactory;
|
2021-12-10 21:41:36 +01:00
|
|
|
|
private readonly IClipboardService _clipboardService;
|
2022-12-07 17:39:20 +01:00
|
|
|
|
private readonly IWatchDeviceService _watchDeviceService;
|
2021-12-10 21:41:36 +01:00
|
|
|
|
|
2022-09-09 20:58:11 +02:00
|
|
|
|
private List<ICustomFieldItemViewModel> _fields;
|
2019-04-24 17:23:03 +02:00
|
|
|
|
private bool _canAccessPremium;
|
2019-04-27 05:37:21 +02:00
|
|
|
|
private bool _showPassword;
|
2021-05-21 15:13:54 +02:00
|
|
|
|
private bool _showCardNumber;
|
2019-04-27 05:37:21 +02:00
|
|
|
|
private bool _showCardCode;
|
2019-04-26 06:26:09 +02:00
|
|
|
|
private string _totpCode;
|
|
|
|
|
private string _totpCodeFormatted;
|
|
|
|
|
private string _totpSec;
|
2022-09-26 18:51:03 +02:00
|
|
|
|
private double _totpInterval = Constants.TotpDefaultTimer;
|
2019-04-26 06:26:09 +02:00
|
|
|
|
private bool _totpLow;
|
2019-07-12 23:29:40 +02:00
|
|
|
|
private string _previousCipherId;
|
2020-03-03 04:14:14 +01:00
|
|
|
|
private byte[] _attachmentData;
|
|
|
|
|
private string _attachmentFilename;
|
2021-05-21 15:13:54 +02:00
|
|
|
|
private bool _passwordReprompted;
|
2022-08-17 23:10:16 +02:00
|
|
|
|
private TotpHelper _totpTickHelper;
|
|
|
|
|
private CancellationTokenSource _totpTickCancellationToken;
|
|
|
|
|
private Task _totpTickTask;
|
2019-04-24 17:23:03 +02:00
|
|
|
|
|
2022-07-27 18:46:56 +02:00
|
|
|
|
public CipherDetailsPageViewModel()
|
2019-04-24 17:23:03 +02:00
|
|
|
|
{
|
|
|
|
|
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
|
2022-02-23 18:40:17 +01:00
|
|
|
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
2022-08-17 23:10:16 +02:00
|
|
|
|
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
|
2019-04-26 06:26:09 +02:00
|
|
|
|
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
2019-05-10 19:47:59 +02:00
|
|
|
|
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
2019-07-12 23:29:40 +02:00
|
|
|
|
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
|
2021-05-21 15:13:54 +02:00
|
|
|
|
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
2021-10-28 18:52:41 +02:00
|
|
|
|
_localizeService = ServiceContainer.Resolve<ILocalizeService>("localizeService");
|
2022-09-09 20:58:11 +02:00
|
|
|
|
_customFieldItemFactory = ServiceContainer.Resolve<ICustomFieldItemFactory>("customFieldItemFactory");
|
2021-12-10 21:41:36 +01:00
|
|
|
|
_clipboardService = ServiceContainer.Resolve<IClipboardService>("clipboardService");
|
2022-12-07 17:39:20 +01:00
|
|
|
|
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
|
2021-12-10 21:41:36 +01:00
|
|
|
|
|
2022-07-05 23:14:46 +02:00
|
|
|
|
CopyCommand = new AsyncCommand<string>((id) => CopyAsync(id, null), onException: ex => _logger.Exception(ex), allowsMultipleExecutions: false);
|
|
|
|
|
CopyUriCommand = new AsyncCommand<LoginUriView>(uriView => CopyAsync("LoginUri", uriView.Uri), onException: ex => _logger.Exception(ex), allowsMultipleExecutions: false);
|
|
|
|
|
CopyFieldCommand = new AsyncCommand<FieldView>(field => CopyAsync(field.Type == FieldType.Hidden ? "H_FieldValue" : "FieldValue", field.Value), onException: ex => _logger.Exception(ex), allowsMultipleExecutions: false);
|
2023-07-26 22:59:49 +02:00
|
|
|
|
LaunchUriCommand = new Command<ILaunchableView>(LaunchUri);
|
|
|
|
|
CloneCommand = new AsyncCommand(CloneAsync, onException: ex => HandleException(ex), allowsMultipleExecutions: false);
|
2019-04-27 05:37:21 +02:00
|
|
|
|
TogglePasswordCommand = new Command(TogglePassword);
|
2021-05-21 15:13:54 +02:00
|
|
|
|
ToggleCardNumberCommand = new Command(ToggleCardNumber);
|
2019-04-27 05:37:21 +02:00
|
|
|
|
ToggleCardCodeCommand = new Command(ToggleCardCode);
|
2022-07-27 18:46:56 +02:00
|
|
|
|
DownloadAttachmentCommand = new AsyncCommand<AttachmentView>(DownloadAttachmentAsync, allowsMultipleExecutions: false);
|
2019-04-24 17:23:03 +02:00
|
|
|
|
|
|
|
|
|
PageTitle = AppResources.ViewItem;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-05 23:14:46 +02:00
|
|
|
|
public ICommand CopyCommand { get; set; }
|
|
|
|
|
public ICommand CopyUriCommand { get; set; }
|
|
|
|
|
public ICommand CopyFieldCommand { get; set; }
|
2019-04-26 22:58:20 +02:00
|
|
|
|
public Command LaunchUriCommand { get; set; }
|
2023-07-26 22:59:49 +02:00
|
|
|
|
public ICommand CloneCommand { get; set; }
|
2019-04-27 05:37:21 +02:00
|
|
|
|
public Command TogglePasswordCommand { get; set; }
|
2021-05-21 15:13:54 +02:00
|
|
|
|
public Command ToggleCardNumberCommand { get; set; }
|
2019-04-27 05:37:21 +02:00
|
|
|
|
public Command ToggleCardCodeCommand { get; set; }
|
2022-07-27 18:46:56 +02:00
|
|
|
|
public AsyncCommand<AttachmentView> DownloadAttachmentCommand { get; set; }
|
2019-04-26 06:26:09 +02:00
|
|
|
|
public string CipherId { get; set; }
|
2022-07-27 18:46:56 +02:00
|
|
|
|
protected override string[] AdditionalPropertiesToRaiseOnCipherChanged => new string[]
|
|
|
|
|
{
|
|
|
|
|
nameof(IsLogin),
|
|
|
|
|
nameof(IsIdentity),
|
|
|
|
|
nameof(IsCard),
|
|
|
|
|
nameof(IsSecureNote),
|
|
|
|
|
nameof(ShowUris),
|
|
|
|
|
nameof(ShowAttachments),
|
|
|
|
|
nameof(ShowTotp),
|
|
|
|
|
nameof(ColoredPassword),
|
|
|
|
|
nameof(UpdatedText),
|
|
|
|
|
nameof(PasswordUpdatedText),
|
|
|
|
|
nameof(PasswordHistoryText),
|
|
|
|
|
nameof(ShowIdentityAddress),
|
|
|
|
|
nameof(IsDeleted),
|
|
|
|
|
nameof(CanEdit),
|
2022-08-17 23:10:16 +02:00
|
|
|
|
nameof(ShowUpgradePremiumTotpText)
|
2022-07-27 18:46:56 +02:00
|
|
|
|
};
|
2022-09-09 20:58:11 +02:00
|
|
|
|
public List<ICustomFieldItemViewModel> Fields
|
2019-04-27 03:53:39 +02:00
|
|
|
|
{
|
|
|
|
|
get => _fields;
|
|
|
|
|
set => SetProperty(ref _fields, value);
|
|
|
|
|
}
|
2019-04-24 17:23:03 +02:00
|
|
|
|
public bool CanAccessPremium
|
|
|
|
|
{
|
|
|
|
|
get => _canAccessPremium;
|
|
|
|
|
set => SetProperty(ref _canAccessPremium, value);
|
|
|
|
|
}
|
2019-04-27 05:37:21 +02:00
|
|
|
|
public bool ShowPassword
|
|
|
|
|
{
|
|
|
|
|
get => _showPassword;
|
|
|
|
|
set => SetProperty(ref _showPassword, value,
|
|
|
|
|
additionalPropertyNames: new string[]
|
|
|
|
|
{
|
2022-06-01 17:02:28 +02:00
|
|
|
|
nameof(ShowPasswordIcon),
|
|
|
|
|
nameof(PasswordVisibilityAccessibilityText)
|
2019-04-27 05:37:21 +02:00
|
|
|
|
});
|
|
|
|
|
}
|
2021-05-21 15:13:54 +02:00
|
|
|
|
public bool ShowCardNumber
|
|
|
|
|
{
|
|
|
|
|
get => _showCardNumber;
|
|
|
|
|
set => SetProperty(ref _showCardNumber, value,
|
|
|
|
|
additionalPropertyNames: new string[]
|
|
|
|
|
{
|
|
|
|
|
nameof(ShowCardNumberIcon)
|
|
|
|
|
});
|
|
|
|
|
}
|
2019-04-27 05:37:21 +02:00
|
|
|
|
public bool ShowCardCode
|
|
|
|
|
{
|
|
|
|
|
get => _showCardCode;
|
|
|
|
|
set => SetProperty(ref _showCardCode, value,
|
|
|
|
|
additionalPropertyNames: new string[]
|
|
|
|
|
{
|
|
|
|
|
nameof(ShowCardCodeIcon)
|
|
|
|
|
});
|
|
|
|
|
}
|
2019-04-29 20:35:44 +02:00
|
|
|
|
public bool IsLogin => Cipher?.Type == Core.Enums.CipherType.Login;
|
|
|
|
|
public bool IsIdentity => Cipher?.Type == Core.Enums.CipherType.Identity;
|
|
|
|
|
public bool IsCard => Cipher?.Type == Core.Enums.CipherType.Card;
|
|
|
|
|
public bool IsSecureNote => Cipher?.Type == Core.Enums.CipherType.SecureNote;
|
2022-08-26 20:32:02 +02:00
|
|
|
|
public FormattedString ColoredPassword => GeneratedValueFormatter.Format(Cipher.Login.Password);
|
2019-04-30 15:50:35 +02:00
|
|
|
|
public FormattedString UpdatedText
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
var fs = new FormattedString();
|
|
|
|
|
fs.Spans.Add(new Span
|
|
|
|
|
{
|
|
|
|
|
Text = string.Format("{0}:", AppResources.DateUpdated),
|
|
|
|
|
FontAttributes = FontAttributes.Bold
|
|
|
|
|
});
|
|
|
|
|
fs.Spans.Add(new Span
|
|
|
|
|
{
|
|
|
|
|
Text = string.Format(" {0} {1}",
|
2021-10-28 18:52:41 +02:00
|
|
|
|
_localizeService.GetLocaleShortDate(Cipher.RevisionDate.ToLocalTime()),
|
|
|
|
|
_localizeService.GetLocaleShortTime(Cipher.RevisionDate.ToLocalTime()))
|
2019-04-30 15:50:35 +02:00
|
|
|
|
});
|
|
|
|
|
return fs;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public FormattedString PasswordUpdatedText
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
var fs = new FormattedString();
|
|
|
|
|
fs.Spans.Add(new Span
|
|
|
|
|
{
|
|
|
|
|
Text = string.Format("{0}:", AppResources.DatePasswordUpdated),
|
|
|
|
|
FontAttributes = FontAttributes.Bold
|
|
|
|
|
});
|
|
|
|
|
fs.Spans.Add(new Span
|
|
|
|
|
{
|
|
|
|
|
Text = string.Format(" {0} {1}",
|
2021-10-28 18:52:41 +02:00
|
|
|
|
_localizeService.GetLocaleShortDate(Cipher.PasswordRevisionDisplayDate?.ToLocalTime()),
|
|
|
|
|
_localizeService.GetLocaleShortTime(Cipher.PasswordRevisionDisplayDate?.ToLocalTime()))
|
2019-04-30 15:50:35 +02:00
|
|
|
|
});
|
|
|
|
|
return fs;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public FormattedString PasswordHistoryText
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
var fs = new FormattedString();
|
|
|
|
|
fs.Spans.Add(new Span
|
|
|
|
|
{
|
|
|
|
|
Text = string.Format("{0}:", AppResources.PasswordHistory),
|
|
|
|
|
FontAttributes = FontAttributes.Bold
|
|
|
|
|
});
|
|
|
|
|
fs.Spans.Add(new Span
|
|
|
|
|
{
|
|
|
|
|
Text = string.Format(" {0}", Cipher.PasswordHistory.Count.ToString()),
|
2020-05-29 18:26:36 +02:00
|
|
|
|
TextColor = ThemeManager.GetResourceColor("PrimaryColor")
|
2019-04-30 15:50:35 +02:00
|
|
|
|
});
|
|
|
|
|
return fs;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-17 23:10:16 +02:00
|
|
|
|
|
2022-09-14 15:45:39 +02:00
|
|
|
|
public bool ShowUpgradePremiumTotpText => !CanAccessPremium && !Cipher.OrganizationUseTotp && ShowTotp;
|
2019-04-29 20:35:44 +02:00
|
|
|
|
public bool ShowUris => IsLogin && Cipher.Login.HasUris;
|
2019-04-29 16:20:29 +02:00
|
|
|
|
public bool ShowIdentityAddress => IsIdentity && (
|
2019-04-29 20:35:44 +02:00
|
|
|
|
!string.IsNullOrWhiteSpace(Cipher.Identity.Address1) ||
|
|
|
|
|
!string.IsNullOrWhiteSpace(Cipher.Identity.City) ||
|
|
|
|
|
!string.IsNullOrWhiteSpace(Cipher.Identity.Country));
|
|
|
|
|
public bool ShowAttachments => Cipher.HasAttachments && (CanAccessPremium || Cipher.OrganizationId != null);
|
2022-08-17 23:10:16 +02:00
|
|
|
|
public bool ShowTotp => IsLogin && !string.IsNullOrWhiteSpace(Cipher.Login.Totp);
|
2022-01-21 10:31:03 +01:00
|
|
|
|
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
|
|
|
|
public string ShowCardNumberIcon => ShowCardNumber ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
|
|
|
|
public string ShowCardCodeIcon => ShowCardCode ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
2022-06-01 17:02:28 +02:00
|
|
|
|
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
2019-04-26 06:26:09 +02:00
|
|
|
|
public string TotpCodeFormatted
|
|
|
|
|
{
|
2022-09-14 15:45:39 +02:00
|
|
|
|
get => ShowUpgradePremiumTotpText ? string.Empty : _totpCodeFormatted;
|
2019-04-26 22:58:20 +02:00
|
|
|
|
set => SetProperty(ref _totpCodeFormatted, value,
|
|
|
|
|
additionalPropertyNames: new string[]
|
|
|
|
|
{
|
|
|
|
|
nameof(ShowTotp)
|
|
|
|
|
});
|
2019-04-26 06:26:09 +02:00
|
|
|
|
}
|
|
|
|
|
public string TotpSec
|
|
|
|
|
{
|
|
|
|
|
get => _totpSec;
|
2022-08-17 23:10:16 +02:00
|
|
|
|
set => SetProperty(ref _totpSec, value,
|
|
|
|
|
additionalPropertyNames: new string[]
|
|
|
|
|
{
|
|
|
|
|
nameof(TotpProgress)
|
|
|
|
|
});
|
2019-04-26 06:26:09 +02:00
|
|
|
|
}
|
|
|
|
|
public bool TotpLow
|
|
|
|
|
{
|
|
|
|
|
get => _totpLow;
|
2019-04-26 18:03:29 +02:00
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
SetProperty(ref _totpLow, value);
|
2020-05-29 18:26:36 +02:00
|
|
|
|
Page.Resources["textTotp"] = ThemeManager.Resources()[value ? "text-danger" : "text-default"];
|
2019-04-26 18:03:29 +02:00
|
|
|
|
}
|
2019-04-26 06:26:09 +02:00
|
|
|
|
}
|
2022-09-26 18:51:03 +02:00
|
|
|
|
public double TotpProgress => string.IsNullOrEmpty(TotpSec) ? 0 : double.Parse(TotpSec) * 100 / _totpInterval;
|
2020-05-20 19:35:20 +02:00
|
|
|
|
public bool IsDeleted => Cipher.IsDeleted;
|
|
|
|
|
public bool CanEdit => !Cipher.IsDeleted;
|
2023-07-27 23:35:09 +02:00
|
|
|
|
public bool CanClone => Cipher.IsClonable;
|
2019-04-24 17:23:03 +02:00
|
|
|
|
|
2019-06-05 23:25:12 +02:00
|
|
|
|
public async Task<bool> LoadAsync(Action finishedLoadingAction = null)
|
2019-04-24 17:23:03 +02:00
|
|
|
|
{
|
|
|
|
|
var cipher = await _cipherService.GetAsync(CipherId);
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (cipher == null)
|
2019-05-30 14:40:10 +02:00
|
|
|
|
{
|
2019-06-05 23:25:12 +02:00
|
|
|
|
finishedLoadingAction?.Invoke();
|
2019-05-30 14:40:10 +02:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-04-24 17:23:03 +02:00
|
|
|
|
Cipher = await cipher.DecryptAsync();
|
2022-02-23 18:40:17 +01:00
|
|
|
|
CanAccessPremium = await _stateService.CanAccessPremiumAsync();
|
2022-09-09 20:58:11 +02:00
|
|
|
|
|
|
|
|
|
Fields = Cipher.Fields?
|
|
|
|
|
.Select(f => _customFieldItemFactory.CreateCustomFieldItem(f, false, Cipher, this, CopyFieldCommand, null))
|
|
|
|
|
.ToList();
|
2019-04-24 17:23:03 +02:00
|
|
|
|
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (Cipher.Type == Core.Enums.CipherType.Login && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) &&
|
2019-04-26 06:26:09 +02:00
|
|
|
|
(Cipher.OrganizationUseTotp || CanAccessPremium))
|
|
|
|
|
{
|
2022-08-17 23:10:16 +02:00
|
|
|
|
_totpTickHelper = new TotpHelper(Cipher);
|
|
|
|
|
_totpTickCancellationToken?.Cancel();
|
2022-09-26 18:51:03 +02:00
|
|
|
|
_totpInterval = _totpTickHelper.Interval;
|
2022-08-17 23:10:16 +02:00
|
|
|
|
_totpTickCancellationToken = new CancellationTokenSource();
|
|
|
|
|
_totpTickTask = new TimerTask(_logger, StartCiphersTotpTick, _totpTickCancellationToken).RunPeriodic();
|
2019-04-26 06:26:09 +02:00
|
|
|
|
}
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (_previousCipherId != CipherId)
|
2019-07-12 23:29:40 +02:00
|
|
|
|
{
|
|
|
|
|
var task = _eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientViewed, CipherId);
|
|
|
|
|
}
|
|
|
|
|
_previousCipherId = CipherId;
|
2019-06-05 23:25:12 +02:00
|
|
|
|
finishedLoadingAction?.Invoke();
|
2019-05-30 14:40:10 +02:00
|
|
|
|
return true;
|
2019-04-26 06:26:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-08-17 23:10:16 +02:00
|
|
|
|
private async void StartCiphersTotpTick()
|
2019-04-26 06:26:09 +02:00
|
|
|
|
{
|
2022-08-17 23:10:16 +02:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await _totpTickHelper.GenerateNewTotpValues();
|
|
|
|
|
TotpSec = _totpTickHelper.TotpSec;
|
|
|
|
|
TotpCodeFormatted = _totpTickHelper.TotpCodeFormatted;
|
2022-09-26 18:51:03 +02:00
|
|
|
|
_totpInterval = _totpTickHelper.Interval;
|
2022-08-17 23:10:16 +02:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
_logger.Exception(ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task StopCiphersTotpTick()
|
|
|
|
|
{
|
|
|
|
|
_totpTickCancellationToken?.Cancel();
|
|
|
|
|
if (_totpTickTask != null)
|
|
|
|
|
{
|
|
|
|
|
await _totpTickTask;
|
|
|
|
|
}
|
2019-04-26 06:26:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 15:13:54 +02:00
|
|
|
|
public async void TogglePassword()
|
2019-04-27 05:37:21 +02:00
|
|
|
|
{
|
2021-05-21 15:13:54 +02:00
|
|
|
|
if (!await PromptPasswordAsync())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-27 05:37:21 +02:00
|
|
|
|
ShowPassword = !ShowPassword;
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (ShowPassword)
|
2019-07-12 23:29:40 +02:00
|
|
|
|
{
|
|
|
|
|
var task = _eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientToggledPasswordVisible, CipherId);
|
|
|
|
|
}
|
2019-04-27 05:37:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 15:13:54 +02:00
|
|
|
|
public async void ToggleCardNumber()
|
|
|
|
|
{
|
|
|
|
|
if (!await PromptPasswordAsync())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ShowCardNumber = !ShowCardNumber;
|
|
|
|
|
if (ShowCardNumber)
|
|
|
|
|
{
|
|
|
|
|
var task = _eventService.CollectAsync(
|
|
|
|
|
Core.Enums.EventType.Cipher_ClientToggledCardNumberVisible, CipherId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async void ToggleCardCode()
|
2019-04-27 05:37:21 +02:00
|
|
|
|
{
|
2021-05-21 15:13:54 +02:00
|
|
|
|
if (!await PromptPasswordAsync())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-04-27 05:37:21 +02:00
|
|
|
|
ShowCardCode = !ShowCardCode;
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (ShowCardCode)
|
2019-07-12 23:29:40 +02:00
|
|
|
|
{
|
|
|
|
|
var task = _eventService.CollectAsync(
|
|
|
|
|
Core.Enums.EventType.Cipher_ClientToggledCardCodeVisible, CipherId);
|
|
|
|
|
}
|
2019-04-27 05:37:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-10 19:47:59 +02:00
|
|
|
|
public async Task<bool> DeleteAsync()
|
|
|
|
|
{
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
2019-06-04 04:43:52 +02:00
|
|
|
|
{
|
|
|
|
|
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
|
|
|
|
|
AppResources.InternetConnectionRequiredTitle);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-05-20 19:35:20 +02:00
|
|
|
|
var confirmed = await _platformUtilsService.ShowDialogAsync(
|
|
|
|
|
Cipher.IsDeleted ? AppResources.DoYouReallyWantToPermanentlyDeleteCipher : AppResources.DoYouReallyWantToSoftDeleteCipher,
|
2019-05-30 14:35:50 +02:00
|
|
|
|
null, AppResources.Yes, AppResources.Cancel);
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (!confirmed)
|
2019-05-10 19:47:59 +02:00
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
try
|
|
|
|
|
{
|
2020-05-20 19:35:20 +02:00
|
|
|
|
await _deviceActionService.ShowLoadingAsync(Cipher.IsDeleted ? AppResources.Deleting : AppResources.SoftDeleting);
|
|
|
|
|
if (Cipher.IsDeleted)
|
|
|
|
|
{
|
|
|
|
|
await _cipherService.DeleteWithServerAsync(Cipher.Id);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
await _cipherService.SoftDeleteWithServerAsync(Cipher.Id);
|
|
|
|
|
}
|
|
|
|
|
await _deviceActionService.HideLoadingAsync();
|
2022-12-07 17:39:20 +01:00
|
|
|
|
|
|
|
|
|
_watchDeviceService.SyncDataToWatchAsync().FireAndForget();
|
|
|
|
|
|
2020-05-20 19:35:20 +02:00
|
|
|
|
_platformUtilsService.ShowToast("success", null,
|
|
|
|
|
Cipher.IsDeleted ? AppResources.ItemDeleted : AppResources.ItemSoftDeleted);
|
|
|
|
|
_messagingService.Send(Cipher.IsDeleted ? "deletedCipher" : "softDeletedCipher", Cipher);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (ApiException e)
|
|
|
|
|
{
|
|
|
|
|
await _deviceActionService.HideLoadingAsync();
|
|
|
|
|
if (e?.Error != null)
|
|
|
|
|
{
|
|
|
|
|
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
|
|
|
|
|
AppResources.AnErrorHasOccurred);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<bool> RestoreAsync()
|
|
|
|
|
{
|
|
|
|
|
if (!IsDeleted)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
|
|
|
|
{
|
|
|
|
|
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
|
|
|
|
|
AppResources.InternetConnectionRequiredTitle);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.DoYouReallyWantToRestoreCipher,
|
|
|
|
|
null, AppResources.Yes, AppResources.Cancel);
|
|
|
|
|
if (!confirmed)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await _deviceActionService.ShowLoadingAsync(AppResources.Restoring);
|
|
|
|
|
await _cipherService.RestoreWithServerAsync(Cipher.Id);
|
2019-05-10 19:47:59 +02:00
|
|
|
|
await _deviceActionService.HideLoadingAsync();
|
2020-05-20 19:35:20 +02:00
|
|
|
|
_platformUtilsService.ShowToast("success", null, AppResources.ItemRestored);
|
|
|
|
|
_messagingService.Send("restoredCipher", Cipher);
|
2019-05-10 19:47:59 +02:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2020-03-28 14:16:28 +01:00
|
|
|
|
catch (ApiException e)
|
2019-05-10 19:47:59 +02:00
|
|
|
|
{
|
|
|
|
|
await _deviceActionService.HideLoadingAsync();
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (e?.Error != null)
|
2019-10-22 22:37:40 +02:00
|
|
|
|
{
|
|
|
|
|
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
|
|
|
|
|
AppResources.AnErrorHasOccurred);
|
|
|
|
|
}
|
2019-05-10 19:47:59 +02:00
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-26 06:26:09 +02:00
|
|
|
|
private async Task TotpUpdateCodeAsync()
|
|
|
|
|
{
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (Cipher == null || Cipher.Type != Core.Enums.CipherType.Login || Cipher.Login.Totp == null)
|
2019-04-26 06:26:09 +02:00
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
_totpCode = await _totpService.GetCodeAsync(Cipher.Login.Totp);
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (_totpCode != null)
|
2019-04-26 06:26:09 +02:00
|
|
|
|
{
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (_totpCode.Length > 4)
|
2019-04-26 06:26:09 +02:00
|
|
|
|
{
|
|
|
|
|
var half = (int)Math.Floor(_totpCode.Length / 2M);
|
|
|
|
|
TotpCodeFormatted = string.Format("{0} {1}", _totpCode.Substring(0, half),
|
|
|
|
|
_totpCode.Substring(half));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TotpCodeFormatted = _totpCode;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TotpCodeFormatted = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task TotpTickAsync(int intervalSeconds)
|
|
|
|
|
{
|
|
|
|
|
var epoc = CoreHelpers.EpocUtcNow() / 1000;
|
|
|
|
|
var mod = epoc % intervalSeconds;
|
|
|
|
|
var totpSec = intervalSeconds - mod;
|
|
|
|
|
TotpSec = totpSec.ToString();
|
|
|
|
|
TotpLow = totpSec < 7;
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (mod == 0)
|
2019-04-26 06:26:09 +02:00
|
|
|
|
{
|
|
|
|
|
await TotpUpdateCodeAsync();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-27 18:46:56 +02:00
|
|
|
|
private async Task DownloadAttachmentAsync(AttachmentView attachment)
|
2019-04-27 06:19:44 +02:00
|
|
|
|
{
|
2022-07-27 18:46:56 +02:00
|
|
|
|
try
|
2019-04-29 22:09:27 +02:00
|
|
|
|
{
|
2022-07-27 18:46:56 +02:00
|
|
|
|
if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None)
|
2019-04-29 22:09:27 +02:00
|
|
|
|
{
|
2022-07-27 18:46:56 +02:00
|
|
|
|
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
|
|
|
|
|
AppResources.InternetConnectionRequiredTitle);
|
2019-04-29 22:09:27 +02:00
|
|
|
|
return;
|
|
|
|
|
}
|
2022-07-27 18:46:56 +02:00
|
|
|
|
if (Cipher.OrganizationId == null && !CanAccessPremium)
|
2020-03-03 04:14:14 +01:00
|
|
|
|
{
|
2022-07-27 18:46:56 +02:00
|
|
|
|
await _platformUtilsService.ShowDialogAsync(AppResources.PremiumRequired);
|
2020-03-03 04:14:14 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
2022-07-27 18:46:56 +02:00
|
|
|
|
if (attachment.FileSize >= 10485760) // 10 MB
|
|
|
|
|
{
|
|
|
|
|
var confirmed = await _platformUtilsService.ShowDialogAsync(
|
|
|
|
|
string.Format(AppResources.AttachmentLargeWarning, attachment.SizeName), null,
|
|
|
|
|
AppResources.Yes, AppResources.No);
|
|
|
|
|
if (!confirmed)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-03 04:14:14 +01:00
|
|
|
|
|
2022-07-27 18:46:56 +02:00
|
|
|
|
var canOpenFile = true;
|
2022-10-11 23:19:32 +02:00
|
|
|
|
if (!_fileService.CanOpenFile(attachment.FileName))
|
2022-07-27 18:46:56 +02:00
|
|
|
|
{
|
|
|
|
|
if (Device.RuntimePlatform == Device.iOS)
|
|
|
|
|
{
|
|
|
|
|
// iOS is currently hardcoded to always return CanOpenFile == true, but should it ever return false
|
|
|
|
|
// for any reason we want to be sure to catch it here.
|
|
|
|
|
await _platformUtilsService.ShowDialogAsync(AppResources.UnableToOpenFile);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-04-29 22:09:27 +02:00
|
|
|
|
|
2022-07-27 18:46:56 +02:00
|
|
|
|
canOpenFile = false;
|
|
|
|
|
}
|
2021-06-11 15:20:21 +02:00
|
|
|
|
|
2022-07-27 18:46:56 +02:00
|
|
|
|
if (!await PromptPasswordAsync())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await _deviceActionService.ShowLoadingAsync(AppResources.Downloading);
|
2021-03-31 01:42:43 +02:00
|
|
|
|
var data = await _cipherService.DownloadAndDecryptAttachmentAsync(Cipher.Id, attachment, Cipher.OrganizationId);
|
2019-04-29 22:09:27 +02:00
|
|
|
|
await _deviceActionService.HideLoadingAsync();
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (data == null)
|
2019-04-29 22:09:27 +02:00
|
|
|
|
{
|
|
|
|
|
await _platformUtilsService.ShowDialogAsync(AppResources.UnableToDownloadFile);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-03-03 04:14:14 +01:00
|
|
|
|
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (Device.RuntimePlatform == Device.Android)
|
2019-04-29 22:09:27 +02:00
|
|
|
|
{
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (canOpenFile)
|
2020-03-03 04:14:14 +01:00
|
|
|
|
{
|
|
|
|
|
// We can open this attachment directly, so give the user the option to open or save
|
|
|
|
|
PromptOpenOrSave(data, attachment);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// We can't open this attachment so go directly to save
|
|
|
|
|
SaveAttachment(data, attachment);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
OpenAttachment(data, attachment);
|
2019-04-29 22:09:27 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-07-27 18:46:56 +02:00
|
|
|
|
catch (Exception ex)
|
2019-04-29 22:09:27 +02:00
|
|
|
|
{
|
2022-07-27 18:46:56 +02:00
|
|
|
|
_logger.Exception(ex);
|
2019-04-29 22:09:27 +02:00
|
|
|
|
await _deviceActionService.HideLoadingAsync();
|
2022-07-27 18:46:56 +02:00
|
|
|
|
await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred);
|
2019-04-29 22:09:27 +02:00
|
|
|
|
}
|
2019-04-29 20:35:44 +02:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-03 04:14:14 +01:00
|
|
|
|
public async void PromptOpenOrSave(byte[] data, AttachmentView attachment)
|
|
|
|
|
{
|
|
|
|
|
var selection = await Page.DisplayActionSheet(attachment.FileName, AppResources.Cancel, null,
|
|
|
|
|
AppResources.Open, AppResources.Save);
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (selection == AppResources.Open)
|
2020-03-03 04:14:14 +01:00
|
|
|
|
{
|
|
|
|
|
OpenAttachment(data, attachment);
|
|
|
|
|
}
|
2020-03-28 14:16:28 +01:00
|
|
|
|
else if (selection == AppResources.Save)
|
2020-03-03 04:14:14 +01:00
|
|
|
|
{
|
|
|
|
|
SaveAttachment(data, attachment);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async void OpenAttachment(byte[] data, AttachmentView attachment)
|
|
|
|
|
{
|
2022-10-11 23:19:32 +02:00
|
|
|
|
if (!_fileService.OpenFile(data, attachment.Id, attachment.FileName))
|
2020-03-03 04:14:14 +01:00
|
|
|
|
{
|
|
|
|
|
await _platformUtilsService.ShowDialogAsync(AppResources.UnableToOpenFile);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async void SaveAttachment(byte[] data, AttachmentView attachment)
|
|
|
|
|
{
|
|
|
|
|
_attachmentData = data;
|
|
|
|
|
_attachmentFilename = attachment.FileName;
|
2022-10-11 23:19:32 +02:00
|
|
|
|
if (!_fileService.SaveFile(_attachmentData, null, _attachmentFilename, null))
|
2020-03-03 04:14:14 +01:00
|
|
|
|
{
|
|
|
|
|
ClearAttachmentData();
|
|
|
|
|
await _platformUtilsService.ShowDialogAsync(AppResources.UnableToSaveAttachment);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async void SaveFileSelected(string contentUri, string filename)
|
|
|
|
|
{
|
2022-10-11 23:19:32 +02:00
|
|
|
|
if (_fileService.SaveFile(_attachmentData, null, filename ?? _attachmentFilename, contentUri))
|
2020-03-03 04:14:14 +01:00
|
|
|
|
{
|
|
|
|
|
ClearAttachmentData();
|
|
|
|
|
_platformUtilsService.ShowToast("success", null, AppResources.SaveAttachmentSuccess);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClearAttachmentData();
|
|
|
|
|
await _platformUtilsService.ShowDialogAsync(AppResources.UnableToSaveAttachment);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ClearAttachmentData()
|
|
|
|
|
{
|
|
|
|
|
_attachmentData = null;
|
|
|
|
|
_attachmentFilename = null;
|
|
|
|
|
}
|
2022-04-26 17:21:17 +02:00
|
|
|
|
|
2022-07-05 23:14:46 +02:00
|
|
|
|
private async Task CopyAsync(string id, string text = null)
|
2019-04-26 06:26:09 +02:00
|
|
|
|
{
|
2021-05-21 15:13:54 +02:00
|
|
|
|
if (_passwordRepromptService.ProtectedFields.Contains(id) && !await PromptPasswordAsync())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-26 06:26:09 +02:00
|
|
|
|
string name = null;
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (id == "LoginUsername")
|
2019-04-26 06:26:09 +02:00
|
|
|
|
{
|
|
|
|
|
text = Cipher.Login.Username;
|
|
|
|
|
name = AppResources.Username;
|
|
|
|
|
}
|
2020-03-28 14:16:28 +01:00
|
|
|
|
else if (id == "LoginPassword")
|
2019-04-26 06:26:09 +02:00
|
|
|
|
{
|
|
|
|
|
text = Cipher.Login.Password;
|
|
|
|
|
name = AppResources.Password;
|
|
|
|
|
}
|
2020-03-28 14:16:28 +01:00
|
|
|
|
else if (id == "LoginTotp")
|
2019-04-26 06:26:09 +02:00
|
|
|
|
{
|
2022-08-17 23:10:16 +02:00
|
|
|
|
text = TotpCodeFormatted.Replace(" ", string.Empty);
|
2019-04-26 06:26:09 +02:00
|
|
|
|
name = AppResources.VerificationCodeTotp;
|
|
|
|
|
}
|
2020-03-28 14:16:28 +01:00
|
|
|
|
else if (id == "LoginUri")
|
2019-04-26 22:58:20 +02:00
|
|
|
|
{
|
|
|
|
|
name = AppResources.URI;
|
|
|
|
|
}
|
2020-03-28 14:16:28 +01:00
|
|
|
|
else if (id == "FieldValue" || id == "H_FieldValue")
|
2019-04-29 19:51:05 +02:00
|
|
|
|
{
|
|
|
|
|
name = AppResources.Value;
|
|
|
|
|
}
|
2020-03-28 14:16:28 +01:00
|
|
|
|
else if (id == "CardNumber")
|
2019-04-29 16:20:29 +02:00
|
|
|
|
{
|
|
|
|
|
text = Cipher.Card.Number;
|
|
|
|
|
name = AppResources.Number;
|
|
|
|
|
}
|
2020-03-28 14:16:28 +01:00
|
|
|
|
else if (id == "CardCode")
|
2019-04-29 16:20:29 +02:00
|
|
|
|
{
|
|
|
|
|
text = Cipher.Card.Code;
|
|
|
|
|
name = AppResources.SecurityCode;
|
|
|
|
|
}
|
2019-04-26 06:26:09 +02:00
|
|
|
|
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (text != null)
|
2019-04-26 06:26:09 +02:00
|
|
|
|
{
|
2021-12-10 21:41:36 +01:00
|
|
|
|
await _clipboardService.CopyTextAsync(text);
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (!string.IsNullOrWhiteSpace(name))
|
2019-04-26 06:26:09 +02:00
|
|
|
|
{
|
2022-06-10 18:02:17 +02:00
|
|
|
|
_platformUtilsService.ShowToastForCopiedValue(name);
|
2019-04-26 06:26:09 +02:00
|
|
|
|
}
|
2020-03-28 14:16:28 +01:00
|
|
|
|
if (id == "LoginPassword")
|
2019-07-12 23:29:40 +02:00
|
|
|
|
{
|
|
|
|
|
await _eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedPassword, CipherId);
|
|
|
|
|
}
|
2020-03-28 14:16:28 +01:00
|
|
|
|
else if (id == "CardCode")
|
2019-07-12 23:29:40 +02:00
|
|
|
|
{
|
|
|
|
|
await _eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedCardCode, CipherId);
|
|
|
|
|
}
|
2020-03-28 14:16:28 +01:00
|
|
|
|
else if (id == "H_FieldValue")
|
2019-07-12 23:29:40 +02:00
|
|
|
|
{
|
|
|
|
|
await _eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedHiddenField, CipherId);
|
|
|
|
|
}
|
2019-04-26 06:26:09 +02:00
|
|
|
|
}
|
2019-04-24 17:23:03 +02:00
|
|
|
|
}
|
2019-04-26 22:58:20 +02:00
|
|
|
|
|
2023-07-26 22:59:49 +02:00
|
|
|
|
private void LaunchUri(ILaunchableView launchableView)
|
|
|
|
|
{
|
|
|
|
|
if (launchableView.CanLaunch && (Page as BaseContentPage).DoOnce())
|
|
|
|
|
{
|
|
|
|
|
_platformUtilsService.LaunchUri(launchableView.LaunchUri);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task CloneAsync()
|
2019-04-26 22:58:20 +02:00
|
|
|
|
{
|
2023-07-26 22:59:49 +02:00
|
|
|
|
if (!await CanCloneAsync() || !await PromptPasswordAsync())
|
2019-04-26 22:58:20 +02:00
|
|
|
|
{
|
2023-07-26 22:59:49 +02:00
|
|
|
|
return;
|
2019-04-26 22:58:20 +02:00
|
|
|
|
}
|
2023-07-26 22:59:49 +02:00
|
|
|
|
|
|
|
|
|
var page = new CipherAddEditPage(CipherId, cloneMode: true, cipherDetailsPage: Page as CipherDetailsPage);
|
|
|
|
|
await Page.Navigation.PushModalAsync(new NavigationPage(page));
|
2019-04-26 22:58:20 +02:00
|
|
|
|
}
|
2021-05-21 15:13:54 +02:00
|
|
|
|
|
2022-09-09 20:58:11 +02:00
|
|
|
|
public async Task<bool> PromptPasswordAsync()
|
2021-05-21 15:13:54 +02:00
|
|
|
|
{
|
2023-08-17 21:19:35 +02:00
|
|
|
|
if (_passwordReprompted)
|
2021-05-21 15:13:54 +02:00
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-17 21:19:35 +02:00
|
|
|
|
return _passwordReprompted = await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(Cipher.Reprompt);
|
2021-05-21 15:13:54 +02:00
|
|
|
|
}
|
2023-07-26 22:59:49 +02:00
|
|
|
|
|
|
|
|
|
private async Task<bool> CanCloneAsync()
|
|
|
|
|
{
|
2023-10-12 21:51:19 +02:00
|
|
|
|
if (!Cipher.HasFido2Credential)
|
2023-07-26 22:59:49 +02:00
|
|
|
|
{
|
2023-09-22 16:55:35 +02:00
|
|
|
|
return true;
|
2023-07-26 22:59:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-09-22 16:55:35 +02:00
|
|
|
|
return await _platformUtilsService.ShowDialogAsync(AppResources.ThePasskeyWillNotBeCopiedToTheClonedItemDoYouWantToContinueCloningThisItem, AppResources.PasskeyWillNotBeCopied, AppResources.Yes, AppResources.No);
|
2023-07-26 22:59:49 +02:00
|
|
|
|
}
|
2019-04-24 17:23:03 +02:00
|
|
|
|
}
|
|
|
|
|
}
|