mirror of
https://github.com/bitwarden/server.git
synced 2025-05-16 17:15:40 -05:00
bulk invite apis
This commit is contained in:
parent
413d49f93b
commit
c582929daf
@ -92,7 +92,7 @@ namespace Bit.Api.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
var userId = _userService.GetProperUserId(User);
|
var userId = _userService.GetProperUserId(User);
|
||||||
var result = await _organizationService.InviteUserAsync(orgGuidId, userId.Value, model.Email, model.Type.Value,
|
var result = await _organizationService.InviteUserAsync(orgGuidId, userId.Value, model.Emails, model.Type.Value,
|
||||||
model.AccessAll, null, model.Collections?.Select(c => c.ToSelectionReadOnly()));
|
model.AccessAll, null, model.Collections?.Select(c => c.ToSelectionReadOnly()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,41 @@
|
|||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Bit.Core.Models.Api
|
namespace Bit.Core.Models.Api
|
||||||
{
|
{
|
||||||
public class OrganizationUserInviteRequestModel
|
public class OrganizationUserInviteRequestModel : IValidatableObject
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
[EmailAddress]
|
public IEnumerable<string> Emails { get; set; }
|
||||||
public string Email { get; set; }
|
|
||||||
[Required]
|
[Required]
|
||||||
public Enums.OrganizationUserType? Type { get; set; }
|
public Enums.OrganizationUserType? Type { get; set; }
|
||||||
public bool AccessAll { get; set; }
|
public bool AccessAll { get; set; }
|
||||||
public IEnumerable<SelectionReadOnlyRequestModel> Collections { get; set; }
|
public IEnumerable<SelectionReadOnlyRequestModel> Collections { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||||
|
{
|
||||||
|
if(!Emails.Any())
|
||||||
|
{
|
||||||
|
yield return new ValidationResult("An email is required.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Emails.Count() > 20)
|
||||||
|
{
|
||||||
|
yield return new ValidationResult("You can only invite up to 20 users at a time.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var attr = new EmailAddressAttribute();
|
||||||
|
for(int i = 0; i < Emails.Count(); i++)
|
||||||
|
{
|
||||||
|
if(!attr.IsValid(Emails.ElementAt(i)))
|
||||||
|
{
|
||||||
|
yield return new ValidationResult($"Email #{i + 1} is not valid.", new string[] { nameof(Emails) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class OrganizationUserAcceptRequestModel
|
public class OrganizationUserAcceptRequestModel
|
||||||
|
@ -23,6 +23,8 @@ namespace Bit.Core.Services
|
|||||||
Task UpdateAsync(Organization organization, bool updateBilling = false);
|
Task UpdateAsync(Organization organization, bool updateBilling = false);
|
||||||
Task<OrganizationUser> InviteUserAsync(Guid organizationId, Guid invitingUserId, string email,
|
Task<OrganizationUser> InviteUserAsync(Guid organizationId, Guid invitingUserId, string email,
|
||||||
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<SelectionReadOnly> collections);
|
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<SelectionReadOnly> collections);
|
||||||
|
Task<List<OrganizationUser>> InviteUserAsync(Guid organizationId, Guid invitingUserId, IEnumerable<string> emails,
|
||||||
|
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<SelectionReadOnly> collections);
|
||||||
Task ResendInviteAsync(Guid organizationId, Guid invitingUserId, Guid organizationUserId);
|
Task ResendInviteAsync(Guid organizationId, Guid invitingUserId, Guid organizationUserId);
|
||||||
Task<OrganizationUser> AcceptUserAsync(Guid organizationUserId, User user, string token);
|
Task<OrganizationUser> AcceptUserAsync(Guid organizationUserId, User user, string token);
|
||||||
Task<OrganizationUser> ConfirmUserAsync(Guid organizationId, Guid organizationUserId, string key, Guid confirmingUserId);
|
Task<OrganizationUser> ConfirmUserAsync(Guid organizationId, Guid organizationUserId, string key, Guid confirmingUserId);
|
||||||
|
@ -685,6 +685,15 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task<OrganizationUser> InviteUserAsync(Guid organizationId, Guid invitingUserId, string email,
|
public async Task<OrganizationUser> InviteUserAsync(Guid organizationId, Guid invitingUserId, string email,
|
||||||
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<SelectionReadOnly> collections)
|
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<SelectionReadOnly> collections)
|
||||||
|
{
|
||||||
|
var result = await InviteUserAsync(organizationId, invitingUserId, new List<string> { email }, type, accessAll,
|
||||||
|
externalId, collections);
|
||||||
|
return result.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<OrganizationUser>> InviteUserAsync(Guid organizationId, Guid invitingUserId,
|
||||||
|
IEnumerable<string> emails, OrganizationUserType type, bool accessAll, string externalId,
|
||||||
|
IEnumerable<SelectionReadOnly> collections)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
||||||
if(organization == null)
|
if(organization == null)
|
||||||
@ -695,45 +704,52 @@ namespace Bit.Core.Services
|
|||||||
if(organization.Seats.HasValue)
|
if(organization.Seats.HasValue)
|
||||||
{
|
{
|
||||||
var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organizationId);
|
var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organizationId);
|
||||||
if(userCount >= organization.Seats.Value)
|
var availableSeats = organization.Seats.Value - userCount;
|
||||||
|
if(availableSeats >= emails.Count())
|
||||||
{
|
{
|
||||||
throw new BadRequestException("You have reached the maximum number of users " +
|
throw new BadRequestException("You have reached the maximum number of users " +
|
||||||
$"({organization.Seats.Value}) for this organization.");
|
$"({organization.Seats.Value}) for this organization.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure user is not already invited
|
var orgUsers = new List<OrganizationUser>();
|
||||||
var existingOrgUser = await _organizationUserRepository.GetByOrganizationAsync(organizationId, email);
|
foreach(var email in emails)
|
||||||
if(existingOrgUser != null)
|
|
||||||
{
|
{
|
||||||
throw new BadRequestException("User already invited.");
|
// Make sure user is not already invited
|
||||||
|
var existingOrgUser = await _organizationUserRepository.GetByOrganizationAsync(organizationId, email);
|
||||||
|
if(existingOrgUser != null)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("User already invited.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var orgUser = new OrganizationUser
|
||||||
|
{
|
||||||
|
OrganizationId = organizationId,
|
||||||
|
UserId = null,
|
||||||
|
Email = email.ToLowerInvariant(),
|
||||||
|
Key = null,
|
||||||
|
Type = type,
|
||||||
|
Status = OrganizationUserStatusType.Invited,
|
||||||
|
AccessAll = accessAll,
|
||||||
|
ExternalId = externalId,
|
||||||
|
CreationDate = DateTime.UtcNow,
|
||||||
|
RevisionDate = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!orgUser.AccessAll && collections.Any())
|
||||||
|
{
|
||||||
|
await _organizationUserRepository.CreateAsync(orgUser, collections);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _organizationUserRepository.CreateAsync(orgUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
await SendInviteAsync(orgUser);
|
||||||
|
orgUsers.Add(orgUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
var orgUser = new OrganizationUser
|
return orgUsers;
|
||||||
{
|
|
||||||
OrganizationId = organizationId,
|
|
||||||
UserId = null,
|
|
||||||
Email = email.ToLowerInvariant(),
|
|
||||||
Key = null,
|
|
||||||
Type = type,
|
|
||||||
Status = OrganizationUserStatusType.Invited,
|
|
||||||
AccessAll = accessAll,
|
|
||||||
ExternalId = externalId,
|
|
||||||
CreationDate = DateTime.UtcNow,
|
|
||||||
RevisionDate = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
|
|
||||||
if(!orgUser.AccessAll && collections.Any())
|
|
||||||
{
|
|
||||||
await _organizationUserRepository.CreateAsync(orgUser, collections);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _organizationUserRepository.CreateAsync(orgUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
await SendInviteAsync(orgUser);
|
|
||||||
return orgUser;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ResendInviteAsync(Guid organizationId, Guid invitingUserId, Guid organizationUserId)
|
public async Task ResendInviteAsync(Guid organizationId, Guid invitingUserId, Guid organizationUserId)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user