mirror of
https://github.com/bitwarden/server.git
synced 2024-11-29 13:25:17 +01:00
import groups and users for org via api
This commit is contained in:
parent
5d595d4cf9
commit
0333b47237
@ -131,7 +131,7 @@ namespace Bit.Api.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("import")]
|
[HttpPost("import")]
|
||||||
public async Task PostImport([FromBody]ImportRequestModel model)
|
public async Task PostImport([FromBody]ImportPasswordsRequestModel model)
|
||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var folders = model.Folders.Select(f => f.ToFolder(userId)).ToList();
|
var folders = model.Folders.Select(f => f.ToFolder(userId)).ToList();
|
||||||
|
@ -228,5 +228,17 @@ namespace Bit.Api.Controllers
|
|||||||
await _organizationService.DeleteAsync(organization);
|
await _organizationService.DeleteAsync(organization);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("{id}/import")]
|
||||||
|
public Task Import(string id, [FromBody]ImportOrganizationUsersRequestModel model)
|
||||||
|
{
|
||||||
|
var orgIdGuid = new Guid(id);
|
||||||
|
if(!_currentContext.OrganizationAdmin(orgIdGuid))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Bit.Core.Models.Api
|
namespace Bit.Core.Models.Api
|
||||||
{
|
{
|
||||||
public class ImportRequestModel
|
public class ImportPasswordsRequestModel
|
||||||
{
|
{
|
||||||
private LoginRequestModel[] _logins;
|
private LoginRequestModel[] _logins;
|
||||||
|
|
@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Api
|
||||||
|
{
|
||||||
|
public class ImportOrganizationUsersRequestModel
|
||||||
|
{
|
||||||
|
public Group[] Groups { get; set; }
|
||||||
|
public User[] Users { get; set; }
|
||||||
|
|
||||||
|
public class Group
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string Name { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string ExternalId { get; set; }
|
||||||
|
|
||||||
|
public Table.Group ToGroup(Guid organizationId)
|
||||||
|
{
|
||||||
|
return new Table.Group
|
||||||
|
{
|
||||||
|
OrganizationId = organizationId,
|
||||||
|
Name = Name,
|
||||||
|
ExternalId = ExternalId
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class User
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[EmailAddress]
|
||||||
|
public string Email { get; set; }
|
||||||
|
public IEnumerable<string> ExternalGroupIds { get; set; }
|
||||||
|
|
||||||
|
public KeyValuePair<string, IEnumerable<string>> ToKvp()
|
||||||
|
{
|
||||||
|
return new KeyValuePair<string, IEnumerable<string>>(Email, ExternalGroupIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -36,6 +36,8 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
if(group.Id == default(Guid))
|
if(group.Id == default(Guid))
|
||||||
{
|
{
|
||||||
|
group.CreationDate = group.RevisionDate = DateTime.UtcNow;
|
||||||
|
|
||||||
if(collections == null)
|
if(collections == null)
|
||||||
{
|
{
|
||||||
await _groupRepository.CreateAsync(group);
|
await _groupRepository.CreateAsync(group);
|
||||||
@ -47,6 +49,8 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
group.RevisionDate = DateTime.UtcNow;
|
||||||
|
|
||||||
if(collections == null)
|
if(collections == null)
|
||||||
{
|
{
|
||||||
await _groupRepository.ReplaceAsync(group);
|
await _groupRepository.ReplaceAsync(group);
|
||||||
|
@ -21,6 +21,7 @@ namespace Bit.Core.Services
|
|||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly ICollectionRepository _collectionRepository;
|
private readonly ICollectionRepository _collectionRepository;
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
|
private readonly IGroupRepository _groupRepository;
|
||||||
private readonly IDataProtector _dataProtector;
|
private readonly IDataProtector _dataProtector;
|
||||||
private readonly IMailService _mailService;
|
private readonly IMailService _mailService;
|
||||||
private readonly IPushService _pushService;
|
private readonly IPushService _pushService;
|
||||||
@ -30,6 +31,7 @@ namespace Bit.Core.Services
|
|||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
ICollectionRepository collectionRepository,
|
ICollectionRepository collectionRepository,
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
|
IGroupRepository groupRepository,
|
||||||
IDataProtectionProvider dataProtectionProvider,
|
IDataProtectionProvider dataProtectionProvider,
|
||||||
IMailService mailService,
|
IMailService mailService,
|
||||||
IPushService pushService)
|
IPushService pushService)
|
||||||
@ -38,6 +40,7 @@ namespace Bit.Core.Services
|
|||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_collectionRepository = collectionRepository;
|
_collectionRepository = collectionRepository;
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
|
_groupRepository = groupRepository;
|
||||||
_dataProtector = dataProtectionProvider.CreateProtector("OrganizationServiceDataProtector");
|
_dataProtector = dataProtectionProvider.CreateProtector("OrganizationServiceDataProtector");
|
||||||
_mailService = mailService;
|
_mailService = mailService;
|
||||||
_pushService = pushService;
|
_pushService = pushService;
|
||||||
@ -895,11 +898,105 @@ namespace Bit.Core.Services
|
|||||||
await _organizationUserRepository.DeleteAsync(orgUser);
|
await _organizationUserRepository.DeleteAsync(orgUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task ImportAsync(Guid organizationId, Guid importingUserId, IEnumerable<Group> groups,
|
||||||
|
IEnumerable<KeyValuePair<string, IEnumerable<string>>> users)
|
||||||
|
{
|
||||||
|
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
||||||
|
if(organization == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!organization.UseGroups)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Organization cannot use groups.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Groups
|
||||||
|
var existingGroups = (await _groupRepository.GetManyByOrganizationIdAsync(organizationId)).ToList();
|
||||||
|
var existingGroupsDict = existingGroups.ToDictionary(g => g.ExternalId);
|
||||||
|
|
||||||
|
var newGroups = groups.Where(g => !existingGroupsDict.ContainsKey(g.ExternalId));
|
||||||
|
var updateGroups = existingGroups.Where(eg => groups.Any(g => g.ExternalId == eg.ExternalId && g.Name != eg.Name));
|
||||||
|
|
||||||
|
foreach(var group in newGroups)
|
||||||
|
{
|
||||||
|
group.CreationDate = group.RevisionDate = DateTime.UtcNow;
|
||||||
|
await _groupRepository.CreateAsync(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(var group in updateGroups)
|
||||||
|
{
|
||||||
|
group.RevisionDate = DateTime.UtcNow;
|
||||||
|
group.Name = existingGroupsDict[group.ExternalId].Name;
|
||||||
|
await _groupRepository.ReplaceAsync(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the newly created groups to existing groups so that we have a complete list to reference below for users.
|
||||||
|
existingGroups.AddRange(newGroups);
|
||||||
|
existingGroupsDict = existingGroups.ToDictionary(g => g.ExternalId);
|
||||||
|
|
||||||
|
// Users
|
||||||
|
var existingUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(organizationId);
|
||||||
|
var existingUsersDict = existingUsers.ToDictionary(u => u.Email);
|
||||||
|
|
||||||
|
var newUsers = users.Where(u => !existingUsersDict.ContainsKey(u.Key)).ToList();
|
||||||
|
var updateUsers = users.Where(u => existingUsersDict.ContainsKey(u.Key));
|
||||||
|
|
||||||
|
var seatsAvailable = int.MaxValue;
|
||||||
|
if(organization.Seats.HasValue)
|
||||||
|
{
|
||||||
|
var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organizationId);
|
||||||
|
seatsAvailable = organization.Seats.Value - userCount;
|
||||||
|
if(seatsAvailable < newUsers.Count)
|
||||||
|
{
|
||||||
|
// throw exception?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(var user in newUsers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var newUser = await InviteUserAsync(organizationId, importingUserId, user.Key, OrganizationUserType.User,
|
||||||
|
false, new List<SelectionReadOnly>());
|
||||||
|
|
||||||
|
var groupsIdsForUser = user.Value.Where(id => existingGroupsDict.ContainsKey(id))
|
||||||
|
.Select(id => existingGroupsDict[id].Id).ToList();
|
||||||
|
if(groupsIdsForUser.Any())
|
||||||
|
{
|
||||||
|
await _organizationUserRepository.UpdateGroupsAsync(newUser.Id, groupsIdsForUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(BadRequestException)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(var user in updateUsers)
|
||||||
|
{
|
||||||
|
if(!existingUsersDict.ContainsKey(user.Key))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingUser = existingUsersDict[user.Key];
|
||||||
|
var groupsIdsForUser = user.Value.Where(id => existingGroupsDict.ContainsKey(id))
|
||||||
|
.Select(id => existingGroupsDict[id].Id).ToList();
|
||||||
|
if(groupsIdsForUser.Any())
|
||||||
|
{
|
||||||
|
await _organizationUserRepository.UpdateGroupsAsync(existingUser.Id, groupsIdsForUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<IEnumerable<OrganizationUser>> GetConfirmedOwnersAsync(Guid organizationId)
|
private async Task<IEnumerable<OrganizationUser>> GetConfirmedOwnersAsync(Guid organizationId)
|
||||||
{
|
{
|
||||||
var owners = await _organizationUserRepository.GetManyByOrganizationAsync(organizationId,
|
var owners = await _organizationUserRepository.GetManyByOrganizationAsync(organizationId,
|
||||||
Enums.OrganizationUserType.Owner);
|
OrganizationUserType.Owner);
|
||||||
return owners.Where(o => o.Status == Enums.OrganizationUserStatusType.Confirmed);
|
return owners.Where(o => o.Status == OrganizationUserStatusType.Confirmed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user