mirror of
https://github.com/bitwarden/server.git
synced 2024-12-23 17:07:42 +01:00
Record when a provider user accesses a clients vault (#1496)
* Record when a provider user accesses a clients vault * Do not allow removal from provider unless owner exists * PR Review * Null safe event processing * append `Async` to async methods
This commit is contained in:
parent
744e8f1a13
commit
cfc7fa071b
@ -27,6 +27,7 @@ namespace Bit.CommCore.Services
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IOrganizationService _organizationService;
|
||||
@ -34,11 +35,13 @@ namespace Bit.CommCore.Services
|
||||
public ProviderService(IProviderRepository providerRepository, IProviderUserRepository providerUserRepository,
|
||||
IProviderOrganizationRepository providerOrganizationRepository, IUserRepository userRepository,
|
||||
IUserService userService, IOrganizationService organizationService, IMailService mailService,
|
||||
IDataProtectionProvider dataProtectionProvider, IEventService eventService, GlobalSettings globalSettings)
|
||||
IDataProtectionProvider dataProtectionProvider, IEventService eventService,
|
||||
IOrganizationRepository organizationRepository, GlobalSettings globalSettings)
|
||||
{
|
||||
_providerRepository = providerRepository;
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_providerOrganizationRepository = providerOrganizationRepository;
|
||||
_organizationRepository = organizationRepository;
|
||||
_userRepository = userRepository;
|
||||
_userService = userService;
|
||||
_organizationService = organizationService;
|
||||
@ -402,7 +405,7 @@ namespace Bit.CommCore.Services
|
||||
return providerOrganization;
|
||||
}
|
||||
|
||||
public async Task RemoveOrganization(Guid providerId, Guid providerOrganizationId, Guid removingUserId)
|
||||
public async Task RemoveOrganizationAsync(Guid providerId, Guid providerOrganizationId, Guid removingUserId)
|
||||
{
|
||||
var providerOrganization = await _providerOrganizationRepository.GetByIdAsync(providerOrganizationId);
|
||||
if (providerOrganization == null || providerOrganization.ProviderId != providerId)
|
||||
@ -410,7 +413,7 @@ namespace Bit.CommCore.Services
|
||||
throw new BadRequestException("Invalid organization.");
|
||||
}
|
||||
|
||||
if (!await _organizationService.HasConfirmedOwnersExceptAsync(providerOrganization.OrganizationId, new Guid[] {}))
|
||||
if (!await _organizationService.HasConfirmedOwnersExceptAsync(providerOrganization.OrganizationId, new Guid[] { }, includeProvider: false))
|
||||
{
|
||||
throw new BadRequestException("Organization needs to have at least one confirmed owner.");
|
||||
}
|
||||
@ -419,6 +422,26 @@ namespace Bit.CommCore.Services
|
||||
await _eventService.LogProviderOrganizationEventAsync(providerOrganization, EventType.ProviderOrganization_Removed);
|
||||
}
|
||||
|
||||
public async Task LogProviderAccessToOrganizationAsync(Guid organizationId)
|
||||
{
|
||||
if (organizationId == default)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var providerOrganization = await _providerOrganizationRepository.GetByOrganizationId(organizationId);
|
||||
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
||||
if (providerOrganization != null)
|
||||
{
|
||||
await _eventService.LogProviderOrganizationEventAsync(providerOrganization, EventType.ProviderOrganization_VaultAccessed);
|
||||
}
|
||||
if (organization != null)
|
||||
{
|
||||
await _eventService.LogOrganizationEventAsync(organization, EventType.Organization_VaultAccessed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task SendInviteAsync(ProviderUser providerUser, Provider provider)
|
||||
{
|
||||
var nowMillis = CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow);
|
||||
|
@ -465,7 +465,7 @@ namespace Bit.CommCore.Test.Services
|
||||
.ReturnsNull();
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.RemoveOrganization(provider.Id, providerOrganization.Id, user.Id));
|
||||
() => sutProvider.Sut.RemoveOrganizationAsync(provider.Id, providerOrganization.Id, user.Id));
|
||||
Assert.Equal("Invalid organization.", exception.Message);
|
||||
}
|
||||
|
||||
@ -478,7 +478,7 @@ namespace Bit.CommCore.Test.Services
|
||||
.Returns(providerOrganization);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.RemoveOrganization(provider.Id, providerOrganization.Id, user.Id));
|
||||
() => sutProvider.Sut.RemoveOrganizationAsync(provider.Id, providerOrganization.Id, user.Id));
|
||||
Assert.Equal("Invalid organization.", exception.Message);
|
||||
}
|
||||
|
||||
@ -490,11 +490,11 @@ namespace Bit.CommCore.Test.Services
|
||||
sutProvider.GetDependency<IProviderRepository>().GetByIdAsync(provider.Id).Returns(provider);
|
||||
sutProvider.GetDependency<IProviderOrganizationRepository>().GetByIdAsync(providerOrganization.Id)
|
||||
.Returns(providerOrganization);
|
||||
sutProvider.GetDependency<IOrganizationService>().HasConfirmedOwnersExceptAsync(default, default)
|
||||
sutProvider.GetDependency<IOrganizationService>().HasConfirmedOwnersExceptAsync(default, default, default)
|
||||
.ReturnsForAnyArgs(false);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.RemoveOrganization(provider.Id, providerOrganization.Id, user.Id));
|
||||
() => sutProvider.Sut.RemoveOrganizationAsync(provider.Id, providerOrganization.Id, user.Id));
|
||||
Assert.Equal("Organization needs to have at least one confirmed owner.", exception.Message);
|
||||
}
|
||||
|
||||
@ -506,10 +506,10 @@ namespace Bit.CommCore.Test.Services
|
||||
sutProvider.GetDependency<IProviderRepository>().GetByIdAsync(provider.Id).Returns(provider);
|
||||
var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>();
|
||||
providerOrganizationRepository.GetByIdAsync(providerOrganization.Id).Returns(providerOrganization);
|
||||
sutProvider.GetDependency<IOrganizationService>().HasConfirmedOwnersExceptAsync(default, default)
|
||||
sutProvider.GetDependency<IOrganizationService>().HasConfirmedOwnersExceptAsync(default, default, default)
|
||||
.ReturnsForAnyArgs(true);
|
||||
|
||||
await sutProvider.Sut.RemoveOrganization(provider.Id, providerOrganization.Id, user.Id);
|
||||
await sutProvider.Sut.RemoveOrganizationAsync(provider.Id, providerOrganization.Id, user.Id);
|
||||
await providerOrganizationRepository.Received().DeleteAsync(providerOrganization);
|
||||
await sutProvider.GetDependency<IEventService>().Received()
|
||||
.LogProviderOrganizationEventAsync(providerOrganization, EventType.ProviderOrganization_Removed);
|
||||
|
@ -30,6 +30,7 @@ namespace Bit.Api.Controllers
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IAttachmentStorageService _attachmentStorageService;
|
||||
private readonly IProviderService _providerService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly ILogger<CiphersController> _logger;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
@ -40,6 +41,7 @@ namespace Bit.Api.Controllers
|
||||
ICipherService cipherService,
|
||||
IUserService userService,
|
||||
IAttachmentStorageService attachmentStorageService,
|
||||
IProviderService providerService,
|
||||
ICurrentContext currentContext,
|
||||
ILogger<CiphersController> logger,
|
||||
GlobalSettings globalSettings)
|
||||
@ -49,6 +51,7 @@ namespace Bit.Api.Controllers
|
||||
_cipherService = cipherService;
|
||||
_userService = userService;
|
||||
_attachmentStorageService = attachmentStorageService;
|
||||
_providerService = providerService;
|
||||
_currentContext = currentContext;
|
||||
_logger = logger;
|
||||
_globalSettings = globalSettings;
|
||||
@ -224,6 +227,12 @@ namespace Bit.Api.Controllers
|
||||
|
||||
var responses = ciphers.Select(c => new CipherMiniDetailsResponseModel(c, _globalSettings,
|
||||
collectionCiphersGroupDict));
|
||||
|
||||
var providerId = await _currentContext.ProviderIdForOrg(orgIdGuid);
|
||||
if (providerId.HasValue)
|
||||
{
|
||||
await _providerService.LogProviderAccessToOrganizationAsync(orgIdGuid);
|
||||
}
|
||||
return new ListResponseModel<CipherMiniDetailsResponseModel>(responses);
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ namespace Bit.Api.Controllers
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.RemoveOrganization(providerId, id, userId.Value);
|
||||
await _providerService.RemoveOrganizationAsync(providerId, id, userId.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,7 @@
|
||||
Organization_Updated = 1600,
|
||||
Organization_PurgedVault = 1601,
|
||||
// Organization_ClientExportedVault = 1602,
|
||||
Organization_VaultAccessed = 1603,
|
||||
|
||||
Policy_Updated = 1700,
|
||||
|
||||
@ -63,5 +64,6 @@
|
||||
ProviderOrganization_Created = 1900,
|
||||
ProviderOrganization_Added = 1901,
|
||||
ProviderOrganization_Removed = 1902,
|
||||
ProviderOrganization_VaultAccessed = 1903,
|
||||
}
|
||||
}
|
||||
|
@ -59,6 +59,6 @@ namespace Bit.Core.Services
|
||||
Task RotateApiKeyAsync(Organization organization);
|
||||
Task DeleteSsoUserAsync(Guid userId, Guid? organizationId);
|
||||
Task<Organization> UpdateOrganizationKeysAsync(Guid orgId, string publicKey, string privateKey);
|
||||
Task<bool> HasConfirmedOwnersExceptAsync(Guid organizationId, IEnumerable<Guid> organizationUsersId);
|
||||
Task<bool> HasConfirmedOwnersExceptAsync(Guid organizationId, IEnumerable<Guid> organizationUsersId, bool includeProvider = true);
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ namespace Bit.Core.Services
|
||||
Task AddOrganization(Guid providerId, Guid organizationId, Guid addingUserId, string key);
|
||||
Task<ProviderOrganization> CreateOrganizationAsync(Guid providerId, OrganizationSignup organizationSignup,
|
||||
string clientOwnerEmail, User user);
|
||||
Task RemoveOrganization(Guid providerId, Guid providerOrganizationId, Guid removingUserId);
|
||||
Task RemoveOrganizationAsync(Guid providerId, Guid providerOrganizationId, Guid removingUserId);
|
||||
Task LogProviderAccessToOrganizationAsync(Guid organizationId);
|
||||
}
|
||||
}
|
||||
|
@ -1689,11 +1689,16 @@ namespace Bit.Core.Services
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<bool> HasConfirmedOwnersExceptAsync(Guid organizationId, IEnumerable<Guid> organizationUsersId)
|
||||
public async Task<bool> HasConfirmedOwnersExceptAsync(Guid organizationId, IEnumerable<Guid> organizationUsersId, bool includeProvider = true)
|
||||
{
|
||||
var confirmedOwners = await GetConfirmedOwnersAsync(organizationId);
|
||||
var confirmedOwnersIds = confirmedOwners.Select(u => u.Id);
|
||||
return confirmedOwnersIds.Except(organizationUsersId).Any();
|
||||
bool hasOtherOwner = confirmedOwnersIds.Except(organizationUsersId).Any();
|
||||
if (!hasOtherOwner && includeProvider)
|
||||
{
|
||||
return (await _currentContext.ProviderIdForOrg(organizationId)).HasValue;
|
||||
}
|
||||
return hasOtherOwner;
|
||||
}
|
||||
|
||||
public async Task UpdateUserGroupsAsync(OrganizationUser organizationUser, IEnumerable<Guid> groupIds, Guid? loggedInUserId)
|
||||
|
@ -31,6 +31,7 @@ namespace Bit.Core.Services
|
||||
public Task AddOrganization(Guid providerId, Guid organizationId, Guid addingUserId, string key) => throw new NotImplementedException();
|
||||
public Task<ProviderOrganization> CreateOrganizationAsync(Guid providerId, OrganizationSignup organizationSignup, string clientOwnerEmail, User user) => throw new NotImplementedException();
|
||||
|
||||
public Task RemoveOrganization(Guid providerId, Guid providerOrganizationId, Guid removingUserId) => throw new NotImplementedException();
|
||||
public Task RemoveOrganizationAsync(Guid providerId, Guid providerOrganizationId, Guid removingUserId) => throw new NotImplementedException();
|
||||
public Task LogProviderAccessToOrganizationAsync(Guid organizationId) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user