1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-26 12:55:17 +01:00

import groups and users for org via api

This commit is contained in:
Kyle Spearrin 2017-05-13 12:00:40 -04:00
parent 5d595d4cf9
commit 0333b47237
6 changed files with 160 additions and 4 deletions

View File

@ -131,7 +131,7 @@ namespace Bit.Api.Controllers
}
[HttpPost("import")]
public async Task PostImport([FromBody]ImportRequestModel model)
public async Task PostImport([FromBody]ImportPasswordsRequestModel model)
{
var userId = _userService.GetProperUserId(User).Value;
var folders = model.Folders.Select(f => f.ToFolder(userId)).ToList();

View File

@ -228,5 +228,17 @@ namespace Bit.Api.Controllers
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);
}
}
}

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Bit.Core.Models.Api
{
public class ImportRequestModel
public class ImportPasswordsRequestModel
{
private LoginRequestModel[] _logins;

View File

@ -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);
}
}
}
}

View File

@ -36,6 +36,8 @@ namespace Bit.Core.Services
if(group.Id == default(Guid))
{
group.CreationDate = group.RevisionDate = DateTime.UtcNow;
if(collections == null)
{
await _groupRepository.CreateAsync(group);
@ -47,6 +49,8 @@ namespace Bit.Core.Services
}
else
{
group.RevisionDate = DateTime.UtcNow;
if(collections == null)
{
await _groupRepository.ReplaceAsync(group);

View File

@ -21,6 +21,7 @@ namespace Bit.Core.Services
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly ICollectionRepository _collectionRepository;
private readonly IUserRepository _userRepository;
private readonly IGroupRepository _groupRepository;
private readonly IDataProtector _dataProtector;
private readonly IMailService _mailService;
private readonly IPushService _pushService;
@ -30,6 +31,7 @@ namespace Bit.Core.Services
IOrganizationUserRepository organizationUserRepository,
ICollectionRepository collectionRepository,
IUserRepository userRepository,
IGroupRepository groupRepository,
IDataProtectionProvider dataProtectionProvider,
IMailService mailService,
IPushService pushService)
@ -38,6 +40,7 @@ namespace Bit.Core.Services
_organizationUserRepository = organizationUserRepository;
_collectionRepository = collectionRepository;
_userRepository = userRepository;
_groupRepository = groupRepository;
_dataProtector = dataProtectionProvider.CreateProtector("OrganizationServiceDataProtector");
_mailService = mailService;
_pushService = pushService;
@ -895,11 +898,105 @@ namespace Bit.Core.Services
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)
{
var owners = await _organizationUserRepository.GetManyByOrganizationAsync(organizationId,
Enums.OrganizationUserType.Owner);
return owners.Where(o => o.Status == Enums.OrganizationUserStatusType.Confirmed);
OrganizationUserType.Owner);
return owners.Where(o => o.Status == OrganizationUserStatusType.Confirmed);
}
}
}