mirror of
https://github.com/bitwarden/server.git
synced 2024-11-22 12:15:36 +01: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:
parent
cfc7fa071b
commit
152f1f7a9b
@ -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);
|
||||
|
@ -21,6 +21,7 @@ using NSubstitute;
|
||||
using NSubstitute.ReturnsExtensions;
|
||||
using Xunit;
|
||||
using ProviderUser = Bit.Core.Models.Table.Provider.ProviderUser;
|
||||
using Bit.Core.Context;
|
||||
|
||||
namespace Bit.CommCore.Test.Services
|
||||
{
|
||||
@ -111,53 +112,64 @@ namespace Bit.CommCore.Test.Services
|
||||
}
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task InviteUserAsync_ProviderIdIsInvalid_Throws(Provider provider, SutProvider<ProviderService> sutProvider)
|
||||
{
|
||||
provider.Id = default;
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.InviteUserAsync(provider.Id, default, default));
|
||||
public async Task InviteUserAsync_ProviderIdIsInvalid_Throws(ProviderUserInvite<string> invite, SutProvider<ProviderService> sutProvider)
|
||||
{
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.InviteUserAsync(invite));
|
||||
}
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task InviteUserAsync_EmailsInvalid_Throws(Provider provider, ProviderUserInvite providerUserInvite,
|
||||
SutProvider<ProviderService> sutProvider)
|
||||
{
|
||||
var providerRepository = sutProvider.GetDependency<IProviderRepository>();
|
||||
providerRepository.GetByIdAsync(provider.Id).Returns(provider);
|
||||
|
||||
providerUserInvite.Emails = null;
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.InviteUserAsync(provider.Id, default, providerUserInvite));
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task InviteUserAsync_InvalidPermissions_Throws(ProviderUserInvite<string> invite, SutProvider<ProviderService> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().ProviderManageUsers(invite.ProviderId).Returns(false);
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.InviteUserAsync(invite));
|
||||
}
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task InviteUserAsync_AlreadyInvited(Provider provider, ProviderUserInvite providerUserInvite,
|
||||
public async Task InviteUserAsync_EmailsInvalid_Throws(Provider provider, ProviderUserInvite<string> providerUserInvite,
|
||||
SutProvider<ProviderService> sutProvider)
|
||||
{
|
||||
var providerRepository = sutProvider.GetDependency<IProviderRepository>();
|
||||
providerRepository.GetByIdAsync(provider.Id).Returns(provider);
|
||||
providerRepository.GetByIdAsync(providerUserInvite.ProviderId).Returns(provider);
|
||||
|
||||
providerUserInvite.UserIdentifiers = null;
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.InviteUserAsync(providerUserInvite));
|
||||
}
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task InviteUserAsync_AlreadyInvited(Provider provider, ProviderUserInvite<string> providerUserInvite,
|
||||
SutProvider<ProviderService> sutProvider)
|
||||
{
|
||||
var providerRepository = sutProvider.GetDependency<IProviderRepository>();
|
||||
providerRepository.GetByIdAsync(providerUserInvite.ProviderId).Returns(provider);
|
||||
var providerUserRepository = sutProvider.GetDependency<IProviderUserRepository>();
|
||||
providerUserRepository.GetCountByProviderAsync(default, default, default).ReturnsForAnyArgs(1);
|
||||
|
||||
var result = await sutProvider.Sut.InviteUserAsync(provider.Id, default, providerUserInvite);
|
||||
var result = await sutProvider.Sut.InviteUserAsync(providerUserInvite);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task InviteUserAsync_Success(Provider provider, ProviderUserInvite providerUserInvite,
|
||||
public async Task InviteUserAsync_Success(Provider provider, ProviderUserInvite<string> providerUserInvite,
|
||||
SutProvider<ProviderService> sutProvider)
|
||||
{
|
||||
var providerRepository = sutProvider.GetDependency<IProviderRepository>();
|
||||
providerRepository.GetByIdAsync(provider.Id).Returns(provider);
|
||||
providerRepository.GetByIdAsync(providerUserInvite.ProviderId).Returns(provider);
|
||||
var providerUserRepository = sutProvider.GetDependency<IProviderUserRepository>();
|
||||
providerUserRepository.GetCountByProviderAsync(default, default, default).ReturnsForAnyArgs(0);
|
||||
|
||||
var result = await sutProvider.Sut.InviteUserAsync(provider.Id, default, providerUserInvite);
|
||||
Assert.Equal(providerUserInvite.Emails.Count(), result.Count);
|
||||
var result = await sutProvider.Sut.InviteUserAsync(providerUserInvite);
|
||||
Assert.Equal(providerUserInvite.UserIdentifiers.Count(), result.Count);
|
||||
Assert.True(result.TrueForAll(pu => pu.Status == ProviderUserStatusType.Invited), "Status must be invited");
|
||||
Assert.True(result.TrueForAll(pu => pu.ProviderId == provider.Id), "Provider Id must be correct");
|
||||
Assert.True(result.TrueForAll(pu => pu.ProviderId == providerUserInvite.ProviderId), "Provider Id must be correct");
|
||||
}
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task ResendInviteUserAsync_InvalidPermissions_Throws(ProviderUserInvite<Guid> invite, SutProvider<ProviderService> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().ProviderManageUsers(invite.ProviderId).Returns(false);
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.ResendInvitesAsync(invite));
|
||||
}
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task ResendInvitesAsync_Errors(Provider provider,
|
||||
@ -175,7 +187,12 @@ namespace Bit.CommCore.Test.Services
|
||||
var providerUserRepository = sutProvider.GetDependency<IProviderUserRepository>();
|
||||
providerUserRepository.GetManyAsync(default).ReturnsForAnyArgs(providerUsers.ToList());
|
||||
|
||||
var result = await sutProvider.Sut.ResendInvitesAsync(provider.Id, default, providerUsers.Select(pu => pu.Id));
|
||||
var invite = new ProviderUserInvite<Guid>
|
||||
{
|
||||
UserIdentifiers = providerUsers.Select(pu => pu.Id),
|
||||
ProviderId = provider.Id
|
||||
};
|
||||
var result = await sutProvider.Sut.ResendInvitesAsync(invite);
|
||||
Assert.Equal("", result[0].Item2);
|
||||
Assert.Equal("User invalid.", result[1].Item2);
|
||||
Assert.Equal("User invalid.", result[2].Item2);
|
||||
@ -197,7 +214,12 @@ namespace Bit.CommCore.Test.Services
|
||||
var providerUserRepository = sutProvider.GetDependency<IProviderUserRepository>();
|
||||
providerUserRepository.GetManyAsync(default).ReturnsForAnyArgs(providerUsers.ToList());
|
||||
|
||||
var result = await sutProvider.Sut.ResendInvitesAsync(provider.Id, default, providerUsers.Select(pu => pu.Id));
|
||||
var invite = new ProviderUserInvite<Guid>
|
||||
{
|
||||
UserIdentifiers = providerUsers.Select(pu => pu.Id),
|
||||
ProviderId = provider.Id
|
||||
};
|
||||
var result = await sutProvider.Sut.ResendInvitesAsync(invite);
|
||||
Assert.True(result.All(r => r.Item2 == ""));
|
||||
}
|
||||
|
||||
|
@ -133,5 +133,12 @@ namespace Bit.Admin.Controllers
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
public async Task<IActionResult> ResendInvite(Guid ownerId, Guid providerId)
|
||||
{
|
||||
await _providerService.ResendProviderSetupInviteEmailAsync(providerId, ownerId);
|
||||
TempData["InviteResentTo"] = ownerId;
|
||||
return RedirectToAction("Edit", new { id = providerId });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,17 +14,11 @@ namespace Bit.Admin.Models
|
||||
{
|
||||
Provider = provider;
|
||||
UserCount = providerUsers.Count();
|
||||
|
||||
ProviderAdmins = string.Join(", ",
|
||||
providerUsers
|
||||
.Where(u => u.Type == ProviderUserType.ProviderAdmin && u.Status == ProviderUserStatusType.Confirmed)
|
||||
.Select(u => u.Email));
|
||||
ProviderAdmins = providerUsers.Where(u => u.Type == ProviderUserType.ProviderAdmin);
|
||||
}
|
||||
|
||||
public int UserCount { get; set; }
|
||||
|
||||
public Provider Provider { get; set; }
|
||||
|
||||
public string ProviderAdmins { get; set; }
|
||||
public IEnumerable<ProviderUserUserDetails> ProviderAdmins { get; set; }
|
||||
}
|
||||
}
|
||||
|
58
src/Admin/Views/Providers/Admins.cshtml
Normal file
58
src/Admin/Views/Providers/Admins.cshtml
Normal file
@ -0,0 +1,58 @@
|
||||
@model ProviderViewModel
|
||||
<h2>Provider Admins</h2>
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 190px;">Email</th>
|
||||
<th style="width: 40px;">Status</th>
|
||||
<th style="width: 30px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if(!Model.ProviderAdmins.Any())
|
||||
{
|
||||
<tr>
|
||||
<td colspan="6">No results to list.</td>
|
||||
</tr>
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach(var admin in Model.ProviderAdmins)
|
||||
{
|
||||
<tr>
|
||||
<td class="align-middle">
|
||||
@admin.Email
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
@admin.Status
|
||||
</td>
|
||||
<td>
|
||||
@if(admin.Status.Equals(ProviderUserStatusType.Confirmed)
|
||||
&& @Model.Provider.Status.Equals(ProviderStatusType.Pending))
|
||||
{
|
||||
@if(@TempData["InviteResentTo"] != null && @TempData["InviteResentTo"].ToString() == @admin.UserId.Value.ToString())
|
||||
{
|
||||
<button class="btn btn-outline-success btn-sm disabled" disabled>Invite Resent!</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a class="btn btn-outline-secondary btn-sm"
|
||||
data-id="@admin.Id" asp-controller="Providers"
|
||||
asp-action="ResendInvite" asp-route-ownerId="@admin.UserId"
|
||||
asp-route-providerId="@Model.Provider.Id">
|
||||
Resend Setup Invite
|
||||
</a>
|
||||
}
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -7,6 +7,7 @@
|
||||
|
||||
<h2>Provider Information</h2>
|
||||
@await Html.PartialAsync("_ViewInformation", Model)
|
||||
@await Html.PartialAsync("Admins", Model)
|
||||
<form method="post" id="edit-form">
|
||||
<h2>General</h2>
|
||||
<div class="row">
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
<h2>Information</h2>
|
||||
@await Html.PartialAsync("_ViewInformation", Model)
|
||||
@await Html.PartialAsync("Admins", Model)
|
||||
<form asp-action="Delete" asp-route-id="@Model.Provider.Id"
|
||||
onsubmit="return confirm('Are you sure you want to delete this provider (@Model.Provider.Name)?')">
|
||||
<button class="btn btn-danger" type="submit">Delete</button>
|
||||
|
@ -9,9 +9,6 @@
|
||||
<dt class="col-sm-4 col-lg-3">Users</dt>
|
||||
<dd class="col-sm-8 col-lg-9">@Model.UserCount</dd>
|
||||
|
||||
<dt class="col-sm-4 col-lg-3">ProviderAdmins</dt>
|
||||
<dd class="col-sm-8 col-lg-9">@(string.IsNullOrWhiteSpace(Model.ProviderAdmins) ? "None" : Model.ProviderAdmins)</dd>
|
||||
|
||||
<dt class="col-sm-4 col-lg-3">Created</dt>
|
||||
<dd class="col-sm-8 col-lg-9">@Model.Provider.CreationDate.ToString()</dd>
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
@using Microsoft.AspNetCore.Identity
|
||||
@using Bit.Admin
|
||||
@using Bit.Admin.Models
|
||||
@using Bit.Core.Enums.Provider
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@addTagHelper "*, Admin"
|
||||
@addTagHelper "*, Admin"
|
||||
|
@ -67,8 +67,9 @@ namespace Bit.Api.Controllers
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.InviteUserAsync(providerId, userId.Value, new ProviderUserInvite(model));
|
||||
var invite = ProviderUserInviteFactory.CreateIntialInvite(model.Emails, model.Type.Value,
|
||||
_userService.GetProperUserId(User).Value, providerId);
|
||||
await _providerService.InviteUserAsync(invite);
|
||||
}
|
||||
|
||||
[HttpPost("reinvite")]
|
||||
@ -79,8 +80,8 @@ namespace Bit.Api.Controllers
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
var result = await _providerService.ResendInvitesAsync(providerId, userId.Value, model.Ids);
|
||||
var invite = ProviderUserInviteFactory.CreateReinvite(model.Ids, _userService.GetProperUserId(User).Value, providerId);
|
||||
var result = await _providerService.ResendInvitesAsync(invite);
|
||||
return new ListResponseModel<ProviderUserBulkResponseModel>(
|
||||
result.Select(t => new ProviderUserBulkResponseModel(t.Item1.Id, t.Item2)));
|
||||
}
|
||||
@ -93,8 +94,9 @@ namespace Bit.Api.Controllers
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User);
|
||||
await _providerService.ResendInvitesAsync(providerId, userId.Value, new [] { id });
|
||||
var invite = ProviderUserInviteFactory.CreateReinvite(new [] { id },
|
||||
_userService.GetProperUserId(User).Value, providerId);
|
||||
await _providerService.ResendInvitesAsync(invite);
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/accept")]
|
||||
|
@ -1,19 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Bit.Core.Enums.Provider;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Models.Business.Provider
|
||||
{
|
||||
public class ProviderUserInvite
|
||||
public class ProviderUserInvite<T>
|
||||
{
|
||||
public IEnumerable<string> Emails { get; set; }
|
||||
public IEnumerable<T> UserIdentifiers { get; set; }
|
||||
public ProviderUserType Type { get; set; }
|
||||
public Guid InvitingUserId { get; set; }
|
||||
public Guid ProviderId { get; set; }
|
||||
}
|
||||
|
||||
public ProviderUserInvite(ProviderUserInviteRequestModel requestModel)
|
||||
public static class ProviderUserInviteFactory
|
||||
{
|
||||
public static ProviderUserInvite<string> CreateIntialInvite(IEnumerable<string> inviteeEmails, ProviderUserType type, Guid invitingUserId, Guid providerId)
|
||||
{
|
||||
Emails = requestModel.Emails;
|
||||
Type = requestModel.Type.Value;
|
||||
return new ProviderUserInvite<string>
|
||||
{
|
||||
UserIdentifiers = inviteeEmails,
|
||||
Type = type,
|
||||
InvitingUserId = invitingUserId,
|
||||
ProviderId = providerId
|
||||
};
|
||||
}
|
||||
|
||||
public static ProviderUserInvite<Guid> CreateReinvite(IEnumerable<Guid> inviteeUserIds, Guid invitingUserId, Guid providerId)
|
||||
{
|
||||
return new ProviderUserInvite<Guid>
|
||||
{
|
||||
UserIdentifiers = inviteeUserIds,
|
||||
InvitingUserId = invitingUserId,
|
||||
ProviderId = providerId
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,9 +14,8 @@ namespace Bit.Core.Services
|
||||
Task<Provider> CompleteSetupAsync(Provider provider, Guid ownerUserId, string token, string key);
|
||||
Task UpdateAsync(Provider provider, bool updateBilling = false);
|
||||
|
||||
Task<List<ProviderUser>> InviteUserAsync(Guid providerId, Guid invitingUserId, ProviderUserInvite providerUserInvite);
|
||||
Task<List<Tuple<ProviderUser, string>>> ResendInvitesAsync(Guid providerId, Guid invitingUserId,
|
||||
IEnumerable<Guid> providerUsersId);
|
||||
Task<List<ProviderUser>> InviteUserAsync(ProviderUserInvite<string> invite);
|
||||
Task<List<Tuple<ProviderUser, string>>> ResendInvitesAsync(ProviderUserInvite<Guid> invite);
|
||||
Task<ProviderUser> AcceptUserAsync(Guid providerUserId, User user, string token);
|
||||
Task<List<Tuple<ProviderUser, string>>> ConfirmUsersAsync(Guid providerId, Dictionary<Guid, string> keys, Guid confirmingUserId);
|
||||
|
||||
@ -29,5 +28,7 @@ namespace Bit.Core.Services
|
||||
string clientOwnerEmail, User user);
|
||||
Task RemoveOrganizationAsync(Guid providerId, Guid providerOrganizationId, Guid removingUserId);
|
||||
Task LogProviderAccessToOrganizationAsync(Guid organizationId);
|
||||
Task ResendProviderSetupInviteEmailAsync(Guid providerId, Guid ownerId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,9 +16,9 @@ namespace Bit.Core.Services
|
||||
|
||||
public Task UpdateAsync(Provider provider, bool updateBilling = false) => throw new NotImplementedException();
|
||||
|
||||
public Task<List<ProviderUser>> InviteUserAsync(Guid providerId, Guid invitingUserId, ProviderUserInvite providerUserInvite) => throw new NotImplementedException();
|
||||
public Task<List<ProviderUser>> InviteUserAsync(ProviderUserInvite<string> invite) => throw new NotImplementedException();
|
||||
|
||||
public Task<List<Tuple<ProviderUser, string>>> ResendInvitesAsync(Guid providerId, Guid invitingUserId, IEnumerable<Guid> providerUsersId) => throw new NotImplementedException();
|
||||
public Task<List<Tuple<ProviderUser, string>>> ResendInvitesAsync(ProviderUserInvite<Guid> invite) => throw new NotImplementedException();
|
||||
|
||||
public Task<ProviderUser> AcceptUserAsync(Guid providerUserId, User user, string token) => throw new NotImplementedException();
|
||||
|
||||
@ -29,9 +29,13 @@ namespace Bit.Core.Services
|
||||
public Task<List<Tuple<ProviderUser, string>>> DeleteUsersAsync(Guid providerId, IEnumerable<Guid> providerUserIds, Guid deletingUserId) => throw new NotImplementedException();
|
||||
|
||||
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 RemoveOrganizationAsync(Guid providerId, Guid providerOrganizationId, Guid removingUserId) => throw new NotImplementedException();
|
||||
|
||||
public Task LogProviderAccessToOrganizationAsync(Guid organizationId) => throw new NotImplementedException();
|
||||
|
||||
public Task ResendProviderSetupInviteEmailAsync(Guid providerId, Guid userId) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ BEGIN
|
||||
[Date] >= @StartDate
|
||||
AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate)
|
||||
AND (@BeforeDate IS NULL OR [Date] < @BeforeDate)
|
||||
AND [Providerid] = @ProviderId
|
||||
AND [ProviderId] = @ProviderId
|
||||
ORDER BY [Date] DESC
|
||||
OFFSET 0 ROWS
|
||||
FETCH NEXT @PageSize ROWS ONLY
|
||||
|
Loading…
Reference in New Issue
Block a user