Bitwarden-app-android-iphon.../src/App/Pages/Accounts/LoginPasswordlessViewModel.cs

176 lines
6.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class LoginPasswordlessViewModel : BaseViewModel
{
private IDeviceActionService _deviceActionService;
private IAuthService _authService;
private IPlatformUtilsService _platformUtilsService;
private ILogger _logger;
private LoginPasswordlessDetails _resquest;
private CancellationTokenSource _requestTimeCts;
private Task _requestTimeTask;
private const int REQUEST_TIME_UPDATE_PERIOD_IN_MINUTES = 5;
public LoginPasswordlessViewModel()
{
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_authService = ServiceContainer.Resolve<IAuthService>("authService");
_logger = ServiceContainer.Resolve<ILogger>("logger");
PageTitle = AppResources.LogInRequested;
AcceptRequestCommand = new AsyncCommand(() => PasswordlessLoginAsync(true),
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
RejectRequestCommand = new AsyncCommand(() => PasswordlessLoginAsync(false),
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
}
public ICommand AcceptRequestCommand { get; }
public ICommand RejectRequestCommand { get; }
public string LogInAttemptByLabel => LoginRequest != null ? string.Format(AppResources.LogInAttemptByXOnY, LoginRequest.Email, LoginRequest.Origin) : string.Empty;
public string TimeOfRequestText => CreateRequestDate(LoginRequest?.RequestDate);
public bool ShowIpAddress => !string.IsNullOrEmpty(LoginRequest?.IpAddress);
public LoginPasswordlessDetails LoginRequest
{
get => _resquest;
set
{
SetProperty(ref _resquest, value, additionalPropertyNames: new string[]
{
nameof(LogInAttemptByLabel),
nameof(TimeOfRequestText),
nameof(ShowIpAddress),
});
}
}
public void StopRequestTimeUpdater()
{
try
{
_requestTimeCts?.Cancel();
_requestTimeCts?.Dispose();
}
catch (Exception ex)
{
_logger.Exception(ex);
}
}
public void StartRequestTimeUpdater()
{
try
{
_requestTimeCts?.Cancel();
_requestTimeCts = new CancellationTokenSource();
_requestTimeTask = new TimerTask(_logger, UpdateRequestTime, _requestTimeCts).RunPeriodic(TimeSpan.FromMinutes(REQUEST_TIME_UPDATE_PERIOD_IN_MINUTES));
}
catch (Exception ex)
{
_logger.Exception(ex);
}
}
private async Task UpdateRequestTime()
{
TriggerPropertyChanged(nameof(TimeOfRequestText));
if (LoginRequest?.IsExpired ?? false)
{
StopRequestTimeUpdater();
await _platformUtilsService.ShowDialogAsync(AppResources.LoginRequestHasAlreadyExpired);
await Page.Navigation.PopModalAsync();
}
}
private async Task PasswordlessLoginAsync(bool approveRequest)
{
if (LoginRequest.IsExpired)
{
await _platformUtilsService.ShowDialogAsync(AppResources.LoginRequestHasAlreadyExpired);
await Page.Navigation.PopModalAsync();
return;
}
var loginRequestData = await _authService.GetPasswordlessLoginRequestByIdAsync(LoginRequest.Id);
if (loginRequestData.IsAnswered)
{
await _platformUtilsService.ShowDialogAsync(AppResources.ThisRequestIsNoLongerValid);
await Page.Navigation.PopModalAsync();
return;
}
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
await _authService.PasswordlessLoginAsync(LoginRequest.Id, LoginRequest.PubKey, approveRequest);
await _deviceActionService.HideLoadingAsync();
await Page.Navigation.PopModalAsync();
_platformUtilsService.ShowToast("info", null, approveRequest ? AppResources.LogInAccepted : AppResources.LogInDenied);
StopRequestTimeUpdater();
}
private string CreateRequestDate(DateTime? requestDate)
{
if (!requestDate.HasValue)
{
return string.Empty;
}
if (DateTime.UtcNow < requestDate.Value.ToUniversalTime().AddMinutes(REQUEST_TIME_UPDATE_PERIOD_IN_MINUTES))
{
return AppResources.JustNow;
}
return string.Format(AppResources.XMinutesAgo, DateTime.UtcNow.Minute - requestDate.Value.ToUniversalTime().Minute);
}
}
public class LoginPasswordlessDetails
{
public string Id { get; set; }
public string Key { get; set; }
public string PubKey { get; set; }
public string Origin { get; set; }
public string Email { get; set; }
public string FingerprintPhrase { get; set; }
public DateTime RequestDate { get; set; }
public string DeviceType { get; set; }
public string IpAddress { get; set; }
public bool IsExpired => RequestDate.ToUniversalTime().AddMinutes(Constants.PasswordlessNotificationTimeoutInMinutes) < DateTime.UtcNow;
}
}