1
0
mirror of https://github.com/bitwarden/server synced 2025-12-17 00:33:23 +00:00

Allow Resending Provider Setup Emails From The Admin Portal (#1497)

* Added a button for resending provider setup emails

* Fixed a case typo in a stored procedure

* Turned a couple lines of code into a method call

* Added service level validation against inviting users for MSP invites

* Code review improvements for provider invites

created a factory for provider user invites

wrote tests for provider invite permissions"

* changed a few exception types
This commit is contained in:
Addison Beck
2021-08-05 10:39:05 -04:00
committed by GitHub
parent cfc7fa071b
commit 152f1f7a9b
14 changed files with 210 additions and 73 deletions

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.Enums.Provider;
using Bit.Core.Exceptions;
@@ -31,12 +32,14 @@ namespace Bit.CommCore.Services
private readonly IUserRepository _userRepository;
private readonly IUserService _userService;
private readonly IOrganizationService _organizationService;
private readonly ICurrentContext _currentContext;
public ProviderService(IProviderRepository providerRepository, IProviderUserRepository providerUserRepository,
IProviderOrganizationRepository providerOrganizationRepository, IUserRepository userRepository,
IUserService userService, IOrganizationService organizationService, IMailService mailService,
IDataProtectionProvider dataProtectionProvider, IEventService eventService,
IOrganizationRepository organizationRepository, GlobalSettings globalSettings)
IOrganizationRepository organizationRepository, GlobalSettings globalSettings,
ICurrentContext currentContext)
{
_providerRepository = providerRepository;
_providerUserRepository = providerUserRepository;
@@ -49,6 +52,7 @@ namespace Bit.CommCore.Services
_eventService = eventService;
_globalSettings = globalSettings;
_dataProtector = dataProtectionProvider.CreateProtector("ProviderServiceDataProtector");
_currentContext = currentContext;
}
public async Task CreateAsync(string ownerEmail)
@@ -75,9 +79,7 @@ namespace Bit.CommCore.Services
Status = ProviderUserStatusType.Confirmed,
};
await _providerUserRepository.CreateAsync(providerUser);
var token = _dataProtector.Protect($"ProviderSetupInvite {provider.Id} {owner.Email} {CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow)}");
await _mailService.SendProviderSetupInviteEmailAsync(provider, token, owner.Email);
await SendProviderSetupInviteEmailAsync(provider, owner.Email);
}
public async Task<Provider> CompleteSetupAsync(Provider provider, Guid ownerUserId, string token, string key)
@@ -118,27 +120,34 @@ namespace Bit.CommCore.Services
{
if (provider.Id == default)
{
throw new ApplicationException("Cannot create provider this way.");
throw new ArgumentException("Cannot create provider this way.");
}
await _providerRepository.ReplaceAsync(provider);
}
public async Task<List<ProviderUser>> InviteUserAsync(Guid providerId, Guid invitingUserId,
ProviderUserInvite invite)
public async Task<List<ProviderUser>> InviteUserAsync(ProviderUserInvite<string> invite)
{
var provider = await _providerRepository.GetByIdAsync(providerId);
if (provider == null || invite?.Emails == null || !invite.Emails.Any())
if (!_currentContext.ProviderManageUsers(invite.ProviderId))
{
throw new InvalidOperationException("Invalid permissions.");
}
var emails = invite?.UserIdentifiers;
var invitingUser = await _providerUserRepository.GetByProviderUserAsync(invite.ProviderId, invite.InvitingUserId);
var provider = await _providerRepository.GetByIdAsync(invite.ProviderId);
if (provider == null || emails == null || !emails.Any())
{
throw new NotFoundException();
}
var providerUsers = new List<ProviderUser>();
foreach (var email in invite.Emails)
foreach (var email in emails)
{
// Make sure user is not already invited
var existingProviderUserCount =
await _providerUserRepository.GetCountByProviderAsync(providerId, email, false);
await _providerUserRepository.GetCountByProviderAsync(invite.ProviderId, email, false);
if (existingProviderUserCount > 0)
{
continue;
@@ -146,7 +155,7 @@ namespace Bit.CommCore.Services
var providerUser = new ProviderUser
{
ProviderId = providerId,
ProviderId = invite.ProviderId,
UserId = null,
Email = email.ToLowerInvariant(),
Key = null,
@@ -167,16 +176,20 @@ namespace Bit.CommCore.Services
return providerUsers;
}
public async Task<List<Tuple<ProviderUser, string>>> ResendInvitesAsync(Guid providerId, Guid invitingUserId,
IEnumerable<Guid> providerUsersId)
public async Task<List<Tuple<ProviderUser, string>>> ResendInvitesAsync(ProviderUserInvite<Guid> invite)
{
var providerUsers = await _providerUserRepository.GetManyAsync(providerUsersId);
var provider = await _providerRepository.GetByIdAsync(providerId);
if (!_currentContext.ProviderManageUsers(invite.ProviderId))
{
throw new BadRequestException("Invalid permissions.");
}
var providerUsers = await _providerUserRepository.GetManyAsync(invite.UserIdentifiers);
var provider = await _providerRepository.GetByIdAsync(invite.ProviderId);
var result = new List<Tuple<ProviderUser, string>>();
foreach (var providerUser in providerUsers)
{
if (providerUser.Status != ProviderUserStatusType.Invited || providerUser.ProviderId != providerId)
if (providerUser.Status != ProviderUserStatusType.Invited || providerUser.ProviderId != invite.ProviderId)
{
result.Add(Tuple.Create(providerUser, "User invalid."));
continue;
@@ -422,6 +435,23 @@ namespace Bit.CommCore.Services
await _eventService.LogProviderOrganizationEventAsync(providerOrganization, EventType.ProviderOrganization_Removed);
}
public async Task ResendProviderSetupInviteEmailAsync(Guid providerId, Guid ownerId)
{
var provider = await _providerRepository.GetByIdAsync(providerId);
var owner = await _userRepository.GetByIdAsync(ownerId);
if (owner == null)
{
throw new BadRequestException("Invalid owner.");
}
await SendProviderSetupInviteEmailAsync(provider, owner.Email);
}
private async Task SendProviderSetupInviteEmailAsync(Provider provider, string ownerEmail)
{
var token = _dataProtector.Protect($"ProviderSetupInvite {provider.Id} {ownerEmail} {CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow)}");
await _mailService.SendProviderSetupInviteEmailAsync(provider, token, ownerEmail);
}
public async Task LogProviderAccessToOrganizationAsync(Guid organizationId)
{
if (organizationId == default)
@@ -441,7 +471,6 @@ namespace Bit.CommCore.Services
}
}
private async Task SendInviteAsync(ProviderUser providerUser, Provider provider)
{
var nowMillis = CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow);