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

organization user apis, hardening, completeness

This commit is contained in:
Kyle Spearrin
2017-03-23 00:17:34 -04:00
parent eaeb18a46b
commit b7254519f0
9 changed files with 172 additions and 32 deletions

View File

@@ -7,6 +7,7 @@ using Bit.Core.Models.Table;
using Bit.Core.Utilities;
using Bit.Core.Exceptions;
using System.Collections.Generic;
using Microsoft.AspNetCore.DataProtection;
namespace Bit.Core.Services
{
@@ -17,19 +18,25 @@ namespace Bit.Core.Services
private readonly ISubvaultRepository _subvaultRepository;
private readonly ISubvaultUserRepository _subvaultUserRepository;
private readonly IUserRepository _userRepository;
private readonly IDataProtector _dataProtector;
private readonly IMailService _mailService;
public OrganizationService(
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
ISubvaultRepository subvaultRepository,
ISubvaultUserRepository subvaultUserRepository,
IUserRepository userRepository)
IUserRepository userRepository,
IDataProtectionProvider dataProtectionProvider,
IMailService mailService)
{
_organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository;
_subvaultRepository = subvaultRepository;
_subvaultUserRepository = subvaultUserRepository;
_userRepository = userRepository;
_dataProtector = dataProtectionProvider.CreateProtector("OrganizationServiceDataProtector");
_mailService = mailService;
}
public async Task<Tuple<Organization, OrganizationUser>> SignUpAsync(OrganizationSignup signup)
@@ -90,9 +97,18 @@ namespace Bit.Core.Services
}
}
public async Task<OrganizationUser> InviteUserAsync(Guid organizationId, string email, Enums.OrganizationUserType type,
IEnumerable<SubvaultUser> subvaults)
public async Task<OrganizationUser> InviteUserAsync(Guid organizationId, Guid invitingUserId, string email,
Enums.OrganizationUserType type, IEnumerable<SubvaultUser> subvaults)
{
if(!(await OrganizationUserHasAdminRightsAsync(organizationId, invitingUserId)))
{
throw new BadRequestException("Cannot invite users.");
}
// TODO: make sure user is not already invited
// TODO: validate subvaults?
var orgUser = new OrganizationUser
{
OrganizationId = organizationId,
@@ -107,21 +123,70 @@ namespace Bit.Core.Services
await _organizationUserRepository.CreateAsync(orgUser);
await SaveUserSubvaultsAsync(orgUser, subvaults, true);
// TODO: send email
await SendInviteAsync(organizationId, email);
return orgUser;
}
public async Task ResendInviteAsync(Guid organizationId, Guid invitingUserId, Guid organizationUserId)
{
if(!(await OrganizationUserHasAdminRightsAsync(organizationId, invitingUserId)))
{
throw new BadRequestException("Cannot invite users.");
}
var orgUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
if(orgUser == null || orgUser.OrganizationId != organizationId ||
orgUser.Status == Enums.OrganizationUserStatusType.Invited)
{
throw new BadRequestException("User invalid.");
}
await SendInviteAsync(organizationId, orgUser.Email);
}
private async Task SendInviteAsync(Guid organizationId, string email)
{
var token = _dataProtector.Protect(
$"OrganizationInvite {organizationId} {email} {CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow)}");
await _mailService.SendOrganizationInviteEmailAsync("Organization Name", email, token);
}
public async Task<OrganizationUser> AcceptUserAsync(Guid organizationUserId, User user, string token)
{
var orgUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
if(orgUser.Email != user.Email)
if(orgUser == null || orgUser.Email != user.Email)
{
throw new BadRequestException("User invalid.");
}
// TODO: validate token
if(orgUser.Status != Enums.OrganizationUserStatusType.Invited)
{
throw new BadRequestException("Already accepted.");
}
var tokenValidationFailed = true;
try
{
var unprotectedData = _dataProtector.Unprotect(token);
var dataParts = unprotectedData.Split(' ');
if(dataParts.Length == 4 && dataParts[0] == "OrganizationInvite" &&
new Guid(dataParts[1]) == orgUser.OrganizationId && dataParts[2] == user.Email)
{
var creationTime = CoreHelpers.FromEpocMilliseconds(Convert.ToInt64(dataParts[3]));
tokenValidationFailed = creationTime.AddDays(5) < DateTime.UtcNow;
}
}
catch
{
tokenValidationFailed = true;
}
if(tokenValidationFailed)
{
throw new BadRequestException("Invalid token.");
}
orgUser.Status = Enums.OrganizationUserStatusType.Accepted;
orgUser.UserId = orgUser.Id;
@@ -133,12 +198,19 @@ namespace Bit.Core.Services
return orgUser;
}
public async Task<OrganizationUser> ConfirmUserAsync(Guid organizationUserId, string key)
public async Task<OrganizationUser> ConfirmUserAsync(Guid organizationId, Guid organizationUserId, string key,
Guid confirmingUserId)
{
var orgUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
if(orgUser.Status != Enums.OrganizationUserStatusType.Accepted)
if(!(await OrganizationUserHasAdminRightsAsync(organizationId, confirmingUserId)))
{
throw new BadRequestException("User not accepted.");
throw new BadRequestException("Cannot confirm users.");
}
var orgUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
if(orgUser == null || orgUser.Status != Enums.OrganizationUserStatusType.Accepted ||
orgUser.OrganizationId != organizationId)
{
throw new BadRequestException("User not valid.");
}
orgUser.Status = Enums.OrganizationUserStatusType.Confirmed;
@@ -151,17 +223,52 @@ namespace Bit.Core.Services
return orgUser;
}
public async Task SaveUserAsync(OrganizationUser user, IEnumerable<SubvaultUser> subvaults)
public async Task SaveUserAsync(OrganizationUser user, Guid savingUserId, IEnumerable<SubvaultUser> subvaults)
{
if(user.Id.Equals(default(Guid)))
{
throw new BadRequestException("Invite the user first.");
}
if(!(await OrganizationUserHasAdminRightsAsync(user.OrganizationId, savingUserId)))
{
throw new BadRequestException("Cannot update users.");
}
// TODO: validate subvaults?
await _organizationUserRepository.ReplaceAsync(user);
await SaveUserSubvaultsAsync(user, subvaults, false);
}
public async Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid deletingUserId)
{
if(!(await OrganizationUserHasAdminRightsAsync(organizationId, deletingUserId)))
{
throw new BadRequestException("Cannot delete users.");
}
var orgUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
if(orgUser == null || orgUser.OrganizationId != organizationId)
{
throw new BadRequestException("User not valid.");
}
await _organizationUserRepository.DeleteAsync(orgUser);
}
private async Task<bool> OrganizationUserHasAdminRightsAsync(Guid organizationId, Guid userId)
{
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(organizationId, userId);
if(orgUser == null)
{
return false;
}
return orgUser.Status == Enums.OrganizationUserStatusType.Confirmed &&
orgUser.Type != Enums.OrganizationUserType.User;
}
private async Task SaveUserSubvaultsAsync(OrganizationUser user, IEnumerable<SubvaultUser> subvaults, bool newUser)
{
if(subvaults == null)