mirror of
https://github.com/bitwarden/mobile
synced 2025-12-23 19:53:50 +00:00
[PM-1208] Add device related api endpoint. Add AccoundDecryptOptions model and property to user Account.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Bit.App.Models;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -9,7 +9,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
|
||||
private readonly LoginApproveDeviceViewModel _vm;
|
||||
public LoginApproveDevicePage()
|
||||
public LoginApproveDevicePage(AppOptions appOptions = null)
|
||||
{
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as LoginApproveDeviceViewModel;
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities.AccountManagement;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Request;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -12,6 +18,8 @@ namespace Bit.App.Pages
|
||||
private bool _approveWithMyOtherDeviceEnabled;
|
||||
private bool _requestAdminApprovalEnabled;
|
||||
private bool _approveWithMasterPasswordEnabled;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IApiService _apiService;
|
||||
|
||||
public ICommand ApproveWithMyOtherDeviceCommand { get; }
|
||||
public ICommand RequestAdminApprovalCommand { get; }
|
||||
@@ -19,7 +27,11 @@ namespace Bit.App.Pages
|
||||
|
||||
public LoginApproveDeviceViewModel()
|
||||
{
|
||||
_stateService = ServiceContainer.Resolve<IStateService>();
|
||||
_apiService = ServiceContainer.Resolve<IApiService>();
|
||||
|
||||
PageTitle = AppResources.LoggedIn;
|
||||
|
||||
ApproveWithMyOtherDeviceCommand = new AsyncCommand(InitAsync,
|
||||
onException: ex => HandleException(ex),
|
||||
allowsMultipleExecutions: false);
|
||||
@@ -59,9 +71,25 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
ApproveWithMyOtherDeviceEnabled = true;
|
||||
RequestAdminApprovalEnabled = true;
|
||||
ApproveWithMasterPasswordEnabled = true;
|
||||
try
|
||||
{
|
||||
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
|
||||
RequestAdminApprovalEnabled = decryptOptions.TrustedDeviceOption.HasAdminApproval;
|
||||
ApproveWithMasterPasswordEnabled = decryptOptions.HasMasterPassword;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleException(ex);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ApproveWithMyOtherDeviceEnabled = await _apiService.GetDevicesExistenceByTypes(DeviceTypeExtensions.GetDesktopAndMobileTypes().ToArray());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +110,11 @@ namespace Bit.App.Pages
|
||||
{
|
||||
RestoreAppOptionsFromCopy();
|
||||
await AppHelpers.ClearPreviousPage();
|
||||
|
||||
// Just for testing the screen
|
||||
Application.Current.MainPage = new NavigationPage(new LoginApproveDevicePage(_appOptions));
|
||||
return;
|
||||
|
||||
if (await _vaultTimeoutService.IsLockedAsync())
|
||||
{
|
||||
Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions));
|
||||
|
||||
@@ -92,5 +92,7 @@ namespace Bit.Core.Abstractions
|
||||
Task<string> GetUsernameFromAsync(ForwardedEmailServiceType service, UsernameGeneratorConfig config);
|
||||
Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier);
|
||||
Task<OrganizationDomainSsoDetailsResponse> GetOrgDomainSsoDetailsAsync(string email);
|
||||
Task<bool> GetDevicesExistenceByTypes(DeviceType[] deviceTypes);
|
||||
Task<DeviceResponse> PutUpdateTrustedDeviceKeys(UpdateTrustedDeviceKeysRequest request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,6 +172,7 @@ namespace Bit.Core.Abstractions
|
||||
Task<string> GetAvatarColorAsync(string userId = null);
|
||||
Task<string> GetPreLoginEmailAsync();
|
||||
Task SetPreLoginEmailAsync(string value);
|
||||
Task<AccountDecryptionOptions> GetAccountDecryptionOptions(string userId = null);
|
||||
string GetLocale();
|
||||
void SetLocale(string locale);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
namespace Bit.Core.Enums
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Bit.Core.Enums
|
||||
{
|
||||
public enum DeviceType : byte
|
||||
{
|
||||
@@ -24,4 +27,24 @@
|
||||
VivaldiExtension = 19,
|
||||
SafariExtension = 20
|
||||
}
|
||||
|
||||
public static class DeviceTypeExtensions
|
||||
{
|
||||
public static List<DeviceType> GetMobileTypes() => new List<DeviceType>
|
||||
{
|
||||
DeviceType.Android,
|
||||
DeviceType.AndroidAmazon,
|
||||
DeviceType.iOS
|
||||
};
|
||||
|
||||
public static List<DeviceType> GetDesktopTypes() => new List<DeviceType>
|
||||
{
|
||||
DeviceType.WindowsDesktop,
|
||||
DeviceType.MacOsDesktop,
|
||||
DeviceType.LinuxDesktop,
|
||||
DeviceType.UWP,
|
||||
};
|
||||
|
||||
public static List<DeviceType> GetDesktopAndMobileTypes() => GetMobileTypes().Concat(GetDesktopTypes()).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ namespace Bit.Core.Models.Domain
|
||||
HasPremiumPersonally = copy.HasPremiumPersonally;
|
||||
AvatarColor = copy.AvatarColor;
|
||||
ForcePasswordResetReason = copy.ForcePasswordResetReason;
|
||||
UserDecryptionOptions = copy.UserDecryptionOptions;
|
||||
}
|
||||
|
||||
public string UserId;
|
||||
@@ -68,6 +69,7 @@ namespace Bit.Core.Models.Domain
|
||||
public bool? EmailVerified;
|
||||
public bool? HasPremiumPersonally;
|
||||
public ForcePasswordResetReason? ForcePasswordResetReason;
|
||||
public AccountDecryptionOptions UserDecryptionOptions;
|
||||
}
|
||||
|
||||
public class AccountTokens
|
||||
|
||||
21
src/Core/Models/Domain/AccountDecryptionOptions.cs
Normal file
21
src/Core/Models/Domain/AccountDecryptionOptions.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
namespace Bit.Core.Models.Domain
|
||||
{
|
||||
public class AccountDecryptionOptions
|
||||
{
|
||||
public bool HasMasterPassword { get; set; }
|
||||
public TrustedDeviceOption TrustedDeviceOption { get; set; }
|
||||
public KeyConnectorOption KeyConnectorOption { get; set; }
|
||||
}
|
||||
|
||||
public class TrustedDeviceOption
|
||||
{
|
||||
public bool HasAdminApproval { get; set; }
|
||||
}
|
||||
|
||||
public class KeyConnectorOption
|
||||
{
|
||||
public bool KeyConnectorUrl { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
12
src/Core/Models/Request/UpdateTrustedDeviceKeysRequest.cs
Normal file
12
src/Core/Models/Request/UpdateTrustedDeviceKeysRequest.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
namespace Bit.Core.Models.Request
|
||||
{
|
||||
public class UpdateTrustedDeviceKeysRequest
|
||||
{
|
||||
public string DeviceIdentifier { get; set; }
|
||||
public string DevicePublicKeyEncryptedUserKey { get; set; }
|
||||
public string UserKeyEncryptedDevicePublicKey { get; set; }
|
||||
public string DeviceKeyEncryptedDevicePrivateKey { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
17
src/Core/Models/Response/DeviceResponse.cs
Normal file
17
src/Core/Models/Response/DeviceResponse.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Models.Response
|
||||
{
|
||||
public class DeviceResponse
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Identifier { get; set; }
|
||||
public DeviceType Type { get; set; }
|
||||
public string CreationDate { get; set; }
|
||||
public string RevisionDate { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace Bit.Core.Models.Response
|
||||
public bool ForcePasswordReset { get; set; }
|
||||
public string KeyConnectorUrl { get; set; }
|
||||
public MasterPasswordPolicyOptions MasterPasswordPolicy { get; set; }
|
||||
public AccountDecryptionOptions UserDecryptionOptions { get; set; }
|
||||
[JsonIgnore]
|
||||
public KdfConfig KdfConfig => new KdfConfig(Kdf, KdfIterations, KdfMemory, KdfParallelism);
|
||||
}
|
||||
|
||||
@@ -395,12 +395,34 @@ namespace Bit.Core.Services
|
||||
|
||||
#region Device APIs
|
||||
|
||||
|
||||
public Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier)
|
||||
{
|
||||
return SendAsync<object, bool>(HttpMethod.Get, "/devices/knowndevice", null, false, true, (message) =>
|
||||
{
|
||||
message.Headers.Add("X-Device-Identifier", deviceIdentifier);
|
||||
message.Headers.Add("X-Request-Email", CoreHelpers.Base64UrlEncode(Encoding.UTF8.GetBytes(email)));
|
||||
});
|
||||
}
|
||||
|
||||
public Task PutDeviceTokenAsync(string identifier, DeviceTokenRequest request)
|
||||
{
|
||||
return SendAsync<DeviceTokenRequest, object>(
|
||||
HttpMethod.Put, $"/devices/identifier/{identifier}/token", request, true, false);
|
||||
}
|
||||
|
||||
public Task<bool> GetDevicesExistenceByTypes(DeviceType[] deviceTypes)
|
||||
{
|
||||
return SendAsync<DeviceType[], bool>(
|
||||
HttpMethod.Post, "/devices/exist-by-types", deviceTypes, true, true);
|
||||
}
|
||||
|
||||
public Task<DeviceResponse> PutUpdateTrustedDeviceKeys(UpdateTrustedDeviceKeysRequest request)
|
||||
{
|
||||
return SendAsync<UpdateTrustedDeviceKeysRequest, DeviceResponse>(
|
||||
HttpMethod.Put, $"/devices/${request.DeviceIdentifier}/keys", request, true, true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event APIs
|
||||
@@ -574,15 +596,6 @@ namespace Bit.Core.Services
|
||||
return SendAsync<object, PasswordlessLoginResponse>(HttpMethod.Put, $"/auth-requests/{id}", request, true, true);
|
||||
}
|
||||
|
||||
public Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier)
|
||||
{
|
||||
return SendAsync<object, bool>(HttpMethod.Get, "/devices/knowndevice", null, false, true, (message) =>
|
||||
{
|
||||
message.Headers.Add("X-Device-Identifier", deviceIdentifier);
|
||||
message.Headers.Add("X-Request-Email", CoreHelpers.Base64UrlEncode(Encoding.UTF8.GetBytes(email)));
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
@@ -459,6 +459,7 @@ namespace Bit.Core.Services
|
||||
ForcePasswordResetReason = result.ForcePasswordReset
|
||||
? ForcePasswordResetReason.AdminForcePasswordReset
|
||||
: (ForcePasswordResetReason?)null,
|
||||
UserDecryptionOptions = tokenResponse.UserDecryptionOptions,
|
||||
},
|
||||
new Account.AccountTokens()
|
||||
{
|
||||
|
||||
@@ -1280,6 +1280,13 @@ namespace Bit.Core.Services
|
||||
await SetValueAsync(Constants.PreLoginEmailKey, value, options);
|
||||
}
|
||||
|
||||
public async Task<AccountDecryptionOptions> GetAccountDecryptionOptions(string userId = null)
|
||||
{
|
||||
return (await GetAccountAsync(
|
||||
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
|
||||
))?.Profile?.UserDecryptionOptions;
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
[Obsolete("Use IStorageMediatorService instead")]
|
||||
|
||||
Reference in New Issue
Block a user