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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Bit.App.Models;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -9,7 +9,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
|
|
||||||
private readonly LoginApproveDeviceViewModel _vm;
|
private readonly LoginApproveDeviceViewModel _vm;
|
||||||
public LoginApproveDevicePage()
|
public LoginApproveDevicePage(AppOptions appOptions = null)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as LoginApproveDeviceViewModel;
|
_vm = BindingContext as LoginApproveDeviceViewModel;
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
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;
|
using Xamarin.CommunityToolkit.ObjectModel;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -12,6 +18,8 @@ namespace Bit.App.Pages
|
|||||||
private bool _approveWithMyOtherDeviceEnabled;
|
private bool _approveWithMyOtherDeviceEnabled;
|
||||||
private bool _requestAdminApprovalEnabled;
|
private bool _requestAdminApprovalEnabled;
|
||||||
private bool _approveWithMasterPasswordEnabled;
|
private bool _approveWithMasterPasswordEnabled;
|
||||||
|
private readonly IStateService _stateService;
|
||||||
|
private readonly IApiService _apiService;
|
||||||
|
|
||||||
public ICommand ApproveWithMyOtherDeviceCommand { get; }
|
public ICommand ApproveWithMyOtherDeviceCommand { get; }
|
||||||
public ICommand RequestAdminApprovalCommand { get; }
|
public ICommand RequestAdminApprovalCommand { get; }
|
||||||
@@ -19,7 +27,11 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public LoginApproveDeviceViewModel()
|
public LoginApproveDeviceViewModel()
|
||||||
{
|
{
|
||||||
|
_stateService = ServiceContainer.Resolve<IStateService>();
|
||||||
|
_apiService = ServiceContainer.Resolve<IApiService>();
|
||||||
|
|
||||||
PageTitle = AppResources.LoggedIn;
|
PageTitle = AppResources.LoggedIn;
|
||||||
|
|
||||||
ApproveWithMyOtherDeviceCommand = new AsyncCommand(InitAsync,
|
ApproveWithMyOtherDeviceCommand = new AsyncCommand(InitAsync,
|
||||||
onException: ex => HandleException(ex),
|
onException: ex => HandleException(ex),
|
||||||
allowsMultipleExecutions: false);
|
allowsMultipleExecutions: false);
|
||||||
@@ -59,9 +71,25 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
ApproveWithMyOtherDeviceEnabled = true;
|
try
|
||||||
RequestAdminApprovalEnabled = true;
|
{
|
||||||
ApproveWithMasterPasswordEnabled = true;
|
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();
|
RestoreAppOptionsFromCopy();
|
||||||
await AppHelpers.ClearPreviousPage();
|
await AppHelpers.ClearPreviousPage();
|
||||||
|
|
||||||
|
// Just for testing the screen
|
||||||
|
Application.Current.MainPage = new NavigationPage(new LoginApproveDevicePage(_appOptions));
|
||||||
|
return;
|
||||||
|
|
||||||
if (await _vaultTimeoutService.IsLockedAsync())
|
if (await _vaultTimeoutService.IsLockedAsync())
|
||||||
{
|
{
|
||||||
Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions));
|
Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions));
|
||||||
|
|||||||
@@ -92,5 +92,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<string> GetUsernameFromAsync(ForwardedEmailServiceType service, UsernameGeneratorConfig config);
|
Task<string> GetUsernameFromAsync(ForwardedEmailServiceType service, UsernameGeneratorConfig config);
|
||||||
Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier);
|
Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier);
|
||||||
Task<OrganizationDomainSsoDetailsResponse> GetOrgDomainSsoDetailsAsync(string email);
|
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> GetAvatarColorAsync(string userId = null);
|
||||||
Task<string> GetPreLoginEmailAsync();
|
Task<string> GetPreLoginEmailAsync();
|
||||||
Task SetPreLoginEmailAsync(string value);
|
Task SetPreLoginEmailAsync(string value);
|
||||||
|
Task<AccountDecryptionOptions> GetAccountDecryptionOptions(string userId = null);
|
||||||
string GetLocale();
|
string GetLocale();
|
||||||
void SetLocale(string locale);
|
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
|
public enum DeviceType : byte
|
||||||
{
|
{
|
||||||
@@ -24,4 +27,24 @@
|
|||||||
VivaldiExtension = 19,
|
VivaldiExtension = 19,
|
||||||
SafariExtension = 20
|
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;
|
HasPremiumPersonally = copy.HasPremiumPersonally;
|
||||||
AvatarColor = copy.AvatarColor;
|
AvatarColor = copy.AvatarColor;
|
||||||
ForcePasswordResetReason = copy.ForcePasswordResetReason;
|
ForcePasswordResetReason = copy.ForcePasswordResetReason;
|
||||||
|
UserDecryptionOptions = copy.UserDecryptionOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string UserId;
|
public string UserId;
|
||||||
@@ -68,6 +69,7 @@ namespace Bit.Core.Models.Domain
|
|||||||
public bool? EmailVerified;
|
public bool? EmailVerified;
|
||||||
public bool? HasPremiumPersonally;
|
public bool? HasPremiumPersonally;
|
||||||
public ForcePasswordResetReason? ForcePasswordResetReason;
|
public ForcePasswordResetReason? ForcePasswordResetReason;
|
||||||
|
public AccountDecryptionOptions UserDecryptionOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AccountTokens
|
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 bool ForcePasswordReset { get; set; }
|
||||||
public string KeyConnectorUrl { get; set; }
|
public string KeyConnectorUrl { get; set; }
|
||||||
public MasterPasswordPolicyOptions MasterPasswordPolicy { get; set; }
|
public MasterPasswordPolicyOptions MasterPasswordPolicy { get; set; }
|
||||||
|
public AccountDecryptionOptions UserDecryptionOptions { get; set; }
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public KdfConfig KdfConfig => new KdfConfig(Kdf, KdfIterations, KdfMemory, KdfParallelism);
|
public KdfConfig KdfConfig => new KdfConfig(Kdf, KdfIterations, KdfMemory, KdfParallelism);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -395,12 +395,34 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
#region Device APIs
|
#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)
|
public Task PutDeviceTokenAsync(string identifier, DeviceTokenRequest request)
|
||||||
{
|
{
|
||||||
return SendAsync<DeviceTokenRequest, object>(
|
return SendAsync<DeviceTokenRequest, object>(
|
||||||
HttpMethod.Put, $"/devices/identifier/{identifier}/token", request, true, false);
|
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
|
#endregion
|
||||||
|
|
||||||
#region Event APIs
|
#region Event APIs
|
||||||
@@ -574,15 +596,6 @@ namespace Bit.Core.Services
|
|||||||
return SendAsync<object, PasswordlessLoginResponse>(HttpMethod.Put, $"/auth-requests/{id}", request, true, true);
|
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
|
#endregion
|
||||||
|
|
||||||
#region Helpers
|
#region Helpers
|
||||||
|
|||||||
@@ -459,6 +459,7 @@ namespace Bit.Core.Services
|
|||||||
ForcePasswordResetReason = result.ForcePasswordReset
|
ForcePasswordResetReason = result.ForcePasswordReset
|
||||||
? ForcePasswordResetReason.AdminForcePasswordReset
|
? ForcePasswordResetReason.AdminForcePasswordReset
|
||||||
: (ForcePasswordResetReason?)null,
|
: (ForcePasswordResetReason?)null,
|
||||||
|
UserDecryptionOptions = tokenResponse.UserDecryptionOptions,
|
||||||
},
|
},
|
||||||
new Account.AccountTokens()
|
new Account.AccountTokens()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1280,6 +1280,13 @@ namespace Bit.Core.Services
|
|||||||
await SetValueAsync(Constants.PreLoginEmailKey, value, options);
|
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
|
// Helpers
|
||||||
|
|
||||||
[Obsolete("Use IStorageMediatorService instead")]
|
[Obsolete("Use IStorageMediatorService instead")]
|
||||||
|
|||||||
Reference in New Issue
Block a user