mirror of
https://github.com/bitwarden/server.git
synced 2025-06-20 02:48:03 -05:00
Add sync response
This commit is contained in:
parent
83c84a7cc0
commit
90ef67b05c
@ -3,6 +3,7 @@ using Bit.Api.AdminConsole.Models.Response;
|
||||
using Bit.Api.Auth.Models.Request;
|
||||
using Bit.Api.Auth.Models.Request.Accounts;
|
||||
using Bit.Api.Auth.Models.Request.WebAuthn;
|
||||
using Bit.Api.KeyManagement.Queries;
|
||||
using Bit.Api.KeyManagement.Validators;
|
||||
using Bit.Api.Models.Request.Accounts;
|
||||
using Bit.Api.Models.Response;
|
||||
@ -59,6 +60,7 @@ public class AccountsController : Controller
|
||||
_organizationUserValidator;
|
||||
private readonly IRotationValidator<IEnumerable<WebAuthnLoginRotateKeyRequestModel>, IEnumerable<WebAuthnLoginRotateKeyData>>
|
||||
_webauthnKeyValidator;
|
||||
private readonly IUserAccountKeysQuery _userAccountKeysQuery;
|
||||
|
||||
|
||||
public AccountsController(
|
||||
@ -79,7 +81,8 @@ public class AccountsController : Controller
|
||||
emergencyAccessValidator,
|
||||
IRotationValidator<IEnumerable<ResetPasswordWithOrgIdRequestModel>, IReadOnlyList<OrganizationUser>>
|
||||
organizationUserValidator,
|
||||
IRotationValidator<IEnumerable<WebAuthnLoginRotateKeyRequestModel>, IEnumerable<WebAuthnLoginRotateKeyData>> webAuthnKeyValidator
|
||||
IRotationValidator<IEnumerable<WebAuthnLoginRotateKeyRequestModel>, IEnumerable<WebAuthnLoginRotateKeyData>> webAuthnKeyValidator,
|
||||
IUserAccountKeysQuery userAccountKeysQuery
|
||||
)
|
||||
{
|
||||
_organizationService = organizationService;
|
||||
@ -98,6 +101,7 @@ public class AccountsController : Controller
|
||||
_emergencyAccessValidator = emergencyAccessValidator;
|
||||
_organizationUserValidator = organizationUserValidator;
|
||||
_webauthnKeyValidator = webAuthnKeyValidator;
|
||||
_userAccountKeysQuery = userAccountKeysQuery;
|
||||
}
|
||||
|
||||
|
||||
@ -397,7 +401,9 @@ public class AccountsController : Controller
|
||||
var hasPremiumFromOrg = await _userService.HasPremiumFromOrganization(user);
|
||||
var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
|
||||
|
||||
var response = new ProfileResponseModel(user, organizationUserDetails, providerUserDetails,
|
||||
var userAccountKeysData = await _userAccountKeysQuery.Run(user);
|
||||
|
||||
var response = new ProfileResponseModel(user, userAccountKeysData, organizationUserDetails, providerUserDetails,
|
||||
providerUserOrganizationDetails, twoFactorEnabled,
|
||||
hasPremiumFromOrg, organizationIdsClaimingActiveUser);
|
||||
return response;
|
||||
@ -430,8 +436,9 @@ public class AccountsController : Controller
|
||||
var twoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
||||
var hasPremiumFromOrg = await _userService.HasPremiumFromOrganization(user);
|
||||
var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
|
||||
var userAccountKeysData = await _userAccountKeysQuery.Run(user);
|
||||
|
||||
var response = new ProfileResponseModel(user, null, null, null, twoFactorEnabled, hasPremiumFromOrg, organizationIdsClaimingActiveUser);
|
||||
var response = new ProfileResponseModel(user, userAccountKeysData, null, null, null, twoFactorEnabled, hasPremiumFromOrg, organizationIdsClaimingActiveUser);
|
||||
return response;
|
||||
}
|
||||
|
||||
@ -449,8 +456,9 @@ public class AccountsController : Controller
|
||||
var userTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
||||
var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user);
|
||||
var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
|
||||
var userAccountKeysData = await _userAccountKeysQuery.Run(user);
|
||||
|
||||
var response = new ProfileResponseModel(user, null, null, null, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationIdsClaimingActiveUser);
|
||||
var response = new ProfileResponseModel(user, userAccountKeysData, null, null, null, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationIdsClaimingActiveUser);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#nullable enable
|
||||
using Bit.Api.KeyManagement.Queries;
|
||||
using Bit.Api.Models.Request;
|
||||
using Bit.Api.Models.Request.Accounts;
|
||||
using Bit.Api.Models.Response;
|
||||
@ -20,7 +21,8 @@ namespace Bit.Api.Billing.Controllers;
|
||||
[Authorize("Application")]
|
||||
public class AccountsController(
|
||||
IUserService userService,
|
||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery) : Controller
|
||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||
IUserAccountKeysQuery userAccountKeysQuery) : Controller
|
||||
{
|
||||
[HttpPost("premium")]
|
||||
public async Task<PaymentResponseModel> PostPremiumAsync(
|
||||
@ -57,8 +59,9 @@ public class AccountsController(
|
||||
var userTwoFactorEnabled = await twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
||||
var userHasPremiumFromOrganization = await userService.HasPremiumFromOrganization(user);
|
||||
var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
|
||||
var userAccountKeysData = await userAccountKeysQuery.Run(user);
|
||||
|
||||
var profile = new ProfileResponseModel(user, null, null, null, userTwoFactorEnabled,
|
||||
var profile = new ProfileResponseModel(user, userAccountKeysData, null, null, null, userTwoFactorEnabled,
|
||||
userHasPremiumFromOrganization, organizationIdsClaimingActiveUser);
|
||||
return new PaymentResponseModel
|
||||
{
|
||||
|
@ -3,28 +3,36 @@ using Bit.Core.Models.Api;
|
||||
|
||||
namespace Bit.Api.Models.Response;
|
||||
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// This response model is used to return keys of a user - downstream of the user key - to the client.
|
||||
/// This includes the private keys (signature/encryption), and proof tying one to another. This could
|
||||
/// also be used to contain further user-owned keys in the future (per-vault keys, etc). This should
|
||||
/// not be used to contain keys not just owned by the user (e.g. organization keys).
|
||||
/// This response model is used to return the the asymmetric encryption keys,
|
||||
/// and signature keys of an entity. This includes the private keys of the key pairs,
|
||||
/// (private key, signing key), and the public keys of the key pairs (unsigned public key,
|
||||
/// signed public key, verification key).
|
||||
/// </summary>
|
||||
public class PrivateAccountKeysResponseModel : ResponseModel
|
||||
public class PrivateKeysResponseModel : ResponseModel
|
||||
{
|
||||
public PrivateAccountKeysResponseModel(UserAccountKeysData accountKeys) : base("accountKeys")
|
||||
public PrivateKeysResponseModel(UserAccountKeysData accountKeys) : base("accountKeys")
|
||||
{
|
||||
if (accountKeys != null)
|
||||
if (accountKeys == null)
|
||||
{
|
||||
SignatureKeyPair = accountKeys.signatureKeyPairData;
|
||||
throw new ArgumentNullException(nameof(accountKeys));
|
||||
}
|
||||
|
||||
if (accountKeys.SignatureKeyPairData != null)
|
||||
{
|
||||
SignatureKeyPair = accountKeys.SignatureKeyPairData;
|
||||
}
|
||||
PublicKeyEncryptionKeyPair = accountKeys.PublicKeyEncryptionKeyPairData;
|
||||
}
|
||||
|
||||
public PrivateAccountKeysResponseModel() : base("accountKeys")
|
||||
public PrivateKeysResponseModel() : base("privateKeys")
|
||||
{
|
||||
}
|
||||
|
||||
public SignatureKeyPairData SignatureKeyPair { get; set; }
|
||||
public PublicKeyEncryptionKeyPairData PublicKeyEncryptionKeyPair { get; set; }
|
||||
// Not all accounts have signature keys, but all accounts have public encryption keys.
|
||||
public SignatureKeyPairData? SignatureKeyPair { get; set; }
|
||||
public required PublicKeyEncryptionKeyPairData PublicKeyEncryptionKeyPair { get; set; }
|
||||
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
using Bit.Core.Models.Api;
|
||||
|
||||
namespace Bit.Api.KeyManagement.Models.Response;
|
||||
|
||||
public class UserKeyResponseModel : ResponseModel
|
||||
{
|
||||
public UserKeyResponseModel(Guid id, string key)
|
||||
: base("userKey")
|
||||
{
|
||||
UserId = id;
|
||||
PublicKey = key;
|
||||
}
|
||||
|
||||
public Guid UserId { get; set; }
|
||||
public string PublicKey { get; set; }
|
||||
}
|
25
src/Api/KeyManagement/Queries/UserAccountKeysQuery.cs
Normal file
25
src/Api/KeyManagement/Queries/UserAccountKeysQuery.cs
Normal file
@ -0,0 +1,25 @@
|
||||
#nullable enable
|
||||
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.KeyManagement.Repositories;
|
||||
|
||||
namespace Bit.Api.KeyManagement.Queries;
|
||||
|
||||
public interface IUserAccountKeysQuery
|
||||
{
|
||||
Task<UserAccountKeysData> Run(User user);
|
||||
}
|
||||
|
||||
public class UserAccountKeysQuery(IUserSignatureKeyPairRepository signatureKeyPairRepository) : IUserAccountKeysQuery
|
||||
{
|
||||
public async Task<UserAccountKeysData> Run(User user)
|
||||
{
|
||||
var userAccountKeysData = new UserAccountKeysData
|
||||
{
|
||||
PublicKeyEncryptionKeyPairData = user.GetPublicKeyEncryptionKeyPair(),
|
||||
SignatureKeyPairData = await signatureKeyPairRepository.GetByUserIdAsync(user.Id)
|
||||
};
|
||||
return userAccountKeysData;
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
using Bit.Api.AdminConsole.Models.Response.Providers;
|
||||
using Bit.Core.AdminConsole.Models.Data.Provider;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||
|
||||
@ -10,6 +11,7 @@ namespace Bit.Api.Models.Response;
|
||||
public class ProfileResponseModel : ResponseModel
|
||||
{
|
||||
public ProfileResponseModel(User user,
|
||||
UserAccountKeysData userAccountKeysData,
|
||||
IEnumerable<OrganizationUserOrganizationDetails> organizationsUserDetails,
|
||||
IEnumerable<ProviderUserProviderDetails> providerUserDetails,
|
||||
IEnumerable<ProviderUserOrganizationDetails> providerUserOrganizationDetails,
|
||||
@ -32,6 +34,7 @@ public class ProfileResponseModel : ResponseModel
|
||||
TwoFactorEnabled = twoFactorEnabled;
|
||||
Key = user.Key;
|
||||
PrivateKey = user.PrivateKey;
|
||||
AccountKeys = userAccountKeysData;
|
||||
SecurityStamp = user.SecurityStamp;
|
||||
ForcePasswordReset = user.ForcePasswordReset;
|
||||
UsesKeyConnector = user.UsesKeyConnector;
|
||||
@ -57,7 +60,9 @@ public class ProfileResponseModel : ResponseModel
|
||||
public string Culture { get; set; }
|
||||
public bool TwoFactorEnabled { get; set; }
|
||||
public string Key { get; set; }
|
||||
[Obsolete("Use AccountKeys instead.")]
|
||||
public string PrivateKey { get; set; }
|
||||
public UserAccountKeysData AccountKeys { get; set; }
|
||||
public string SecurityStamp { get; set; }
|
||||
public bool ForcePasswordReset { get; set; }
|
||||
public bool UsesKeyConnector { get; set; }
|
||||
|
@ -8,6 +8,8 @@ using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.KeyManagement.Repositories;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@ -39,6 +41,7 @@ public class SyncController : Controller
|
||||
private readonly IFeatureService _featureService;
|
||||
private readonly IApplicationCacheService _applicationCacheService;
|
||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||
private readonly IUserSignatureKeyPairRepository _userSignatureKeyPairRepository;
|
||||
|
||||
public SyncController(
|
||||
IUserService userService,
|
||||
@ -54,7 +57,8 @@ public class SyncController : Controller
|
||||
ICurrentContext currentContext,
|
||||
IFeatureService featureService,
|
||||
IApplicationCacheService applicationCacheService,
|
||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery)
|
||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||
IUserSignatureKeyPairRepository userSignatureKeyPairRepository)
|
||||
{
|
||||
_userService = userService;
|
||||
_folderRepository = folderRepository;
|
||||
@ -70,6 +74,7 @@ public class SyncController : Controller
|
||||
_featureService = featureService;
|
||||
_applicationCacheService = applicationCacheService;
|
||||
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
||||
_userSignatureKeyPairRepository = userSignatureKeyPairRepository;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
@ -112,8 +117,14 @@ public class SyncController : Controller
|
||||
var organizationIdsClaimingActiveUser = organizationClaimingActiveUser.Select(o => o.Id);
|
||||
|
||||
var organizationAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
||||
var signingKeys = await _userSignatureKeyPairRepository.GetByUserIdAsync(user.Id);
|
||||
var userAccountKeysData = new UserAccountKeysData
|
||||
{
|
||||
PublicKeyEncryptionKeyPairData = user.GetPublicKeyEncryptionKeyPair(),
|
||||
SignatureKeyPairData = signingKeys,
|
||||
};
|
||||
|
||||
var response = new SyncResponseModel(_globalSettings, user, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationAbilities,
|
||||
var response = new SyncResponseModel(_globalSettings, user, userAccountKeysData, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationAbilities,
|
||||
organizationIdsClaimingActiveUser, organizationUserDetails, providerUserDetails, providerUserOrganizationDetails,
|
||||
folders, collections, ciphers, collectionCiphersGroupDict, excludeDomains, policies, sends);
|
||||
return response;
|
||||
|
@ -4,6 +4,7 @@ using Bit.Api.Tools.Models.Response;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Models.Data.Provider;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Data.Organizations;
|
||||
@ -20,6 +21,7 @@ public class SyncResponseModel : ResponseModel
|
||||
public SyncResponseModel(
|
||||
GlobalSettings globalSettings,
|
||||
User user,
|
||||
UserAccountKeysData userAccountKeysData,
|
||||
bool userTwoFactorEnabled,
|
||||
bool userHasPremiumFromOrganization,
|
||||
IDictionary<Guid, OrganizationAbility> organizationAbilities,
|
||||
@ -36,7 +38,7 @@ public class SyncResponseModel : ResponseModel
|
||||
IEnumerable<Send> sends)
|
||||
: base("sync")
|
||||
{
|
||||
Profile = new ProfileResponseModel(user, organizationUserDetails, providerUserDetails,
|
||||
Profile = new ProfileResponseModel(user, userAccountKeysData, organizationUserDetails, providerUserDetails,
|
||||
providerUserOrganizationDetails, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationIdsClaimingingUser);
|
||||
Folders = folders.Select(f => new FolderResponseModel(f));
|
||||
Ciphers = ciphers.Select(cipher =>
|
||||
|
@ -3,6 +3,7 @@ using System.Text.Json;
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Auth.Models;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
@ -254,4 +255,14 @@ public class User : ITableObject<Guid>, IStorableSubscriber, IRevisable, ITwoFac
|
||||
{
|
||||
return MasterPassword != null;
|
||||
}
|
||||
|
||||
public PublicKeyEncryptionKeyPairData GetPublicKeyEncryptionKeyPair()
|
||||
{
|
||||
return new PublicKeyEncryptionKeyPairData
|
||||
{
|
||||
WrappedPrivateKey = PrivateKey,
|
||||
SignedPublicKey = SignedPublicKey,
|
||||
PublicKey = PublicKey
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
namespace Bit.Core.KeyManagement.Models.Data;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class UserAccountKeysData
|
||||
{
|
||||
public PublicKeyEncryptionKeyPairData PublicKeyEncryptionKeyPairData { get; set; }
|
||||
public SignatureKeyPairData signatureKeyPairData { get; set; }
|
||||
public required PublicKeyEncryptionKeyPairData PublicKeyEncryptionKeyPairData { get; set; }
|
||||
public SignatureKeyPairData? SignatureKeyPairData { get; set; }
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ using Bit.Api.Auth.Controllers;
|
||||
using Bit.Api.Auth.Models.Request;
|
||||
using Bit.Api.Auth.Models.Request.Accounts;
|
||||
using Bit.Api.Auth.Models.Request.WebAuthn;
|
||||
using Bit.Api.KeyManagement.Queries;
|
||||
using Bit.Api.KeyManagement.Validators;
|
||||
using Bit.Api.Tools.Models.Request;
|
||||
using Bit.Api.Vault.Models.Request;
|
||||
@ -54,7 +55,7 @@ public class AccountsControllerTests : IDisposable
|
||||
_resetPasswordValidator;
|
||||
private readonly IRotationValidator<IEnumerable<WebAuthnLoginRotateKeyRequestModel>, IEnumerable<WebAuthnLoginRotateKeyData>>
|
||||
_webauthnKeyRotationValidator;
|
||||
|
||||
private readonly IUserAccountKeysQuery _userAccountKeysQuery;
|
||||
|
||||
public AccountsControllerTests()
|
||||
{
|
||||
@ -79,6 +80,7 @@ public class AccountsControllerTests : IDisposable
|
||||
_resetPasswordValidator = Substitute
|
||||
.For<IRotationValidator<IEnumerable<ResetPasswordWithOrgIdRequestModel>,
|
||||
IReadOnlyList<OrganizationUser>>>();
|
||||
_userAccountKeysQuery = Substitute.For<IUserAccountKeysQuery>();
|
||||
|
||||
_sut = new AccountsController(
|
||||
_organizationService,
|
||||
@ -96,7 +98,8 @@ public class AccountsControllerTests : IDisposable
|
||||
_sendValidator,
|
||||
_emergencyAccessValidator,
|
||||
_resetPasswordValidator,
|
||||
_webauthnKeyRotationValidator
|
||||
_webauthnKeyRotationValidator,
|
||||
_userAccountKeysQuery
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user