mirror of
https://github.com/bitwarden/server.git
synced 2025-02-23 03:01:23 +01:00
org user subvaults apis
This commit is contained in:
parent
4a9206b992
commit
cfb4d1453c
@ -58,7 +58,8 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task Invite(string orgId, [FromBody]OrganizationUserInviteRequestModel model)
|
public async Task Invite(string orgId, [FromBody]OrganizationUserInviteRequestModel model)
|
||||||
{
|
{
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
var result = await _organizationService.InviteUserAsync(new Guid(orgId), model.Email);
|
var result = await _organizationService.InviteUserAsync(new Guid(orgId), model.Email,
|
||||||
|
model.Subvaults.Select(s => s.ToSubvaultUser()));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("{id}/accept")]
|
[HttpPut("{id}/accept")]
|
||||||
|
@ -7,6 +7,7 @@ namespace Bit.Core.Models.Api
|
|||||||
public class OrganizationUserInviteRequestModel
|
public class OrganizationUserInviteRequestModel
|
||||||
{
|
{
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
|
public IEnumerable<OrganizationUserSubvaultRequestModel> Subvaults { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class OrganizationUserAcceptRequestModel
|
public class OrganizationUserAcceptRequestModel
|
||||||
@ -22,31 +23,35 @@ namespace Bit.Core.Models.Api
|
|||||||
public class OrganizationUserUpdateRequestModel
|
public class OrganizationUserUpdateRequestModel
|
||||||
{
|
{
|
||||||
public Enums.OrganizationUserType Type { get; set; }
|
public Enums.OrganizationUserType Type { get; set; }
|
||||||
public IEnumerable<Subvault> Subvaults { get; set; }
|
public IEnumerable<OrganizationUserSubvaultRequestModel> Subvaults { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class Subvault
|
public class OrganizationUserSubvaultRequestModel
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string SubvaultId { get; set; }
|
||||||
|
public bool Admin { get; set; }
|
||||||
|
public bool ReadOnly { get; set; }
|
||||||
|
|
||||||
|
public SubvaultUser ToSubvaultUser()
|
||||||
{
|
{
|
||||||
public string Id { get; set; }
|
var subvault = new SubvaultUser
|
||||||
public string SubvaultId { get; set; }
|
|
||||||
public bool Admin { get; set; }
|
|
||||||
public bool ReadOnly { get; set; }
|
|
||||||
|
|
||||||
public SubvaultUser ToSubvaultUser()
|
|
||||||
{
|
{
|
||||||
var user = new SubvaultUser
|
Admin = Admin,
|
||||||
{
|
ReadOnly = ReadOnly
|
||||||
SubvaultId = new Guid(SubvaultId),
|
};
|
||||||
Admin = Admin,
|
|
||||||
ReadOnly = ReadOnly
|
|
||||||
};
|
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(Id))
|
if(!string.IsNullOrWhiteSpace(SubvaultId))
|
||||||
{
|
{
|
||||||
user.Id = new Guid(Id);
|
subvault.SubvaultId = new Guid(SubvaultId);
|
||||||
}
|
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!string.IsNullOrWhiteSpace(Id))
|
||||||
|
{
|
||||||
|
subvault.Id = new Guid(Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return subvault;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Bit.Core.Models.Table;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Bit.Core.Models.Api
|
namespace Bit.Core.Models.Api
|
||||||
@ -36,12 +35,13 @@ namespace Bit.Core.Models.Api
|
|||||||
public class OrganizationUserDetailsResponseModel : OrganizationUserResponseModel
|
public class OrganizationUserDetailsResponseModel : OrganizationUserResponseModel
|
||||||
{
|
{
|
||||||
public OrganizationUserDetailsResponseModel(OrganizationUserUserDetails organizationUser,
|
public OrganizationUserDetailsResponseModel(OrganizationUserUserDetails organizationUser,
|
||||||
IEnumerable<Subvault> subvaults)
|
IEnumerable<SubvaultUserDetails> subvaults)
|
||||||
: base(organizationUser, "organizationUserDetails")
|
: base(organizationUser, "organizationUserDetails")
|
||||||
{
|
{
|
||||||
Subvaults = new ListResponseModel<SubvaultResponseModel>(subvaults.Select(s => new SubvaultResponseModel(s)));
|
Subvaults = new ListResponseModel<OrganizationUserSubvaultResponseModel>(
|
||||||
|
subvaults.Select(s => new OrganizationUserSubvaultResponseModel(s)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListResponseModel<SubvaultResponseModel> Subvaults { get; set; }
|
public ListResponseModel<OrganizationUserSubvaultResponseModel> Subvaults { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Api
|
||||||
|
{
|
||||||
|
public class OrganizationUserSubvaultResponseModel : ResponseModel
|
||||||
|
{
|
||||||
|
public OrganizationUserSubvaultResponseModel(SubvaultUserDetails details,
|
||||||
|
string obj = "organizationUserSubvault")
|
||||||
|
: base(obj)
|
||||||
|
{
|
||||||
|
if(details == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(details));
|
||||||
|
}
|
||||||
|
|
||||||
|
Id = details.Id.ToString();
|
||||||
|
Name = details.Name;
|
||||||
|
SubvaultId = details.SubvaultId.ToString();
|
||||||
|
ReadOnly = details.ReadOnly;
|
||||||
|
Admin = details.Admin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string SubvaultId { get; set; }
|
||||||
|
public bool ReadOnly { get; set; }
|
||||||
|
public bool Admin { get; set; }
|
||||||
|
}
|
||||||
|
}
|
14
src/Core/Models/Data/SubvaultUserDetails.cs
Normal file
14
src/Core/Models/Data/SubvaultUserDetails.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Data
|
||||||
|
{
|
||||||
|
public class SubvaultUserDetails
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid OrganizationUserId { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public Guid SubvaultId { get; set; }
|
||||||
|
public bool ReadOnly { get; set; }
|
||||||
|
public bool Admin { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@ namespace Bit.Core.Repositories
|
|||||||
public interface IOrganizationUserRepository : IRepository<OrganizationUser, Guid>
|
public interface IOrganizationUserRepository : IRepository<OrganizationUser, Guid>
|
||||||
{
|
{
|
||||||
Task<OrganizationUser> GetByOrganizationAsync(Guid organizationId, Guid userId);
|
Task<OrganizationUser> GetByOrganizationAsync(Guid organizationId, Guid userId);
|
||||||
Task<Tuple<OrganizationUserUserDetails, ICollection<Subvault>>> GetDetailsByIdAsync(Guid id);
|
Task<Tuple<OrganizationUserUserDetails, ICollection<SubvaultUserDetails>>> GetDetailsByIdAsync(Guid id);
|
||||||
Task<ICollection<OrganizationUserUserDetails>> GetManyDetailsByOrganizationAsync(Guid organizationId);
|
Task<ICollection<OrganizationUserUserDetails>> GetManyDetailsByOrganizationAsync(Guid organizationId);
|
||||||
Task<ICollection<OrganizationUserOrganizationDetails>> GetManyDetailsByUserAsync(Guid userId);
|
Task<ICollection<OrganizationUserOrganizationDetails>> GetManyDetailsByUserAsync(Guid userId);
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Tuple<OrganizationUserUserDetails, ICollection<Subvault>>> GetDetailsByIdAsync(Guid id)
|
public async Task<Tuple<OrganizationUserUserDetails, ICollection<SubvaultUserDetails>>> GetDetailsByIdAsync(Guid id)
|
||||||
{
|
{
|
||||||
using(var connection = new SqlConnection(ConnectionString))
|
using(var connection = new SqlConnection(ConnectionString))
|
||||||
{
|
{
|
||||||
@ -43,8 +43,8 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
var user = (await results.ReadAsync<OrganizationUserUserDetails>()).SingleOrDefault();
|
var user = (await results.ReadAsync<OrganizationUserUserDetails>()).SingleOrDefault();
|
||||||
var subvaults = (await results.ReadAsync<Subvault>()).ToList();
|
var subvaults = (await results.ReadAsync<SubvaultUserDetails>()).ToList();
|
||||||
return new Tuple<OrganizationUserUserDetails, ICollection<Subvault>>(user, subvaults);
|
return new Tuple<OrganizationUserUserDetails, ICollection<SubvaultUserDetails>>(user, subvaults);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@ namespace Bit.Core.Services
|
|||||||
public interface IOrganizationService
|
public interface IOrganizationService
|
||||||
{
|
{
|
||||||
Task<Tuple<Organization, OrganizationUser>> SignUpAsync(OrganizationSignup organizationSignup);
|
Task<Tuple<Organization, OrganizationUser>> SignUpAsync(OrganizationSignup organizationSignup);
|
||||||
Task<OrganizationUser> InviteUserAsync(Guid organizationId, string email);
|
Task<OrganizationUser> InviteUserAsync(Guid organizationId, string email, IEnumerable<SubvaultUser> subvaults);
|
||||||
Task<OrganizationUser> AcceptUserAsync(Guid organizationUserId, User user, string token);
|
Task<OrganizationUser> AcceptUserAsync(Guid organizationUserId, User user, string token);
|
||||||
Task<OrganizationUser> ConfirmUserAsync(Guid organizationUserId, string key);
|
Task<OrganizationUser> ConfirmUserAsync(Guid organizationUserId, string key);
|
||||||
Task<OrganizationUser> SaveUserAsync(OrganizationUser user, IEnumerable<SubvaultUser> subvaults);
|
Task SaveUserAsync(OrganizationUser user, IEnumerable<SubvaultUser> subvaults);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,8 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<OrganizationUser> InviteUserAsync(Guid organizationId, string email)
|
public async Task<OrganizationUser> InviteUserAsync(Guid organizationId, string email,
|
||||||
|
IEnumerable<SubvaultUser> subvaults)
|
||||||
{
|
{
|
||||||
var orgUser = new OrganizationUser
|
var orgUser = new OrganizationUser
|
||||||
{
|
{
|
||||||
@ -105,6 +106,7 @@ namespace Bit.Core.Services
|
|||||||
};
|
};
|
||||||
|
|
||||||
await _organizationUserRepository.CreateAsync(orgUser);
|
await _organizationUserRepository.CreateAsync(orgUser);
|
||||||
|
await SaveUserSubvaultsAsync(orgUser, subvaults, true);
|
||||||
|
|
||||||
// TODO: send email
|
// TODO: send email
|
||||||
|
|
||||||
@ -149,7 +151,7 @@ namespace Bit.Core.Services
|
|||||||
return orgUser;
|
return orgUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<OrganizationUser> SaveUserAsync(OrganizationUser user, IEnumerable<SubvaultUser> subvaults)
|
public async Task SaveUserAsync(OrganizationUser user, IEnumerable<SubvaultUser> subvaults)
|
||||||
{
|
{
|
||||||
if(user.Id.Equals(default(Guid)))
|
if(user.Id.Equals(default(Guid)))
|
||||||
{
|
{
|
||||||
@ -157,28 +159,36 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _organizationUserRepository.ReplaceAsync(user);
|
await _organizationUserRepository.ReplaceAsync(user);
|
||||||
|
await SaveUserSubvaultsAsync(user, subvaults, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SaveUserSubvaultsAsync(OrganizationUser user, IEnumerable<SubvaultUser> subvaults, bool newUser)
|
||||||
|
{
|
||||||
var orgSubvaults = await _subvaultRepository.GetManyByOrganizationIdAsync(user.OrganizationId);
|
var orgSubvaults = await _subvaultRepository.GetManyByOrganizationIdAsync(user.OrganizationId);
|
||||||
var currentUserSubvaults = await _subvaultUserRepository.GetManyByOrganizationUserIdAsync(user.Id);
|
var currentUserSubvaults = newUser ? null : await _subvaultUserRepository.GetManyByOrganizationUserIdAsync(user.Id);
|
||||||
|
|
||||||
// Let's make sure all these belong to this user and organization.
|
// Let's make sure all these belong to this user and organization.
|
||||||
var filteredSubvaults = subvaults.Where(s =>
|
var filteredSubvaults = subvaults.Where(s => orgSubvaults.Any(os => os.Id == s.SubvaultId));
|
||||||
orgSubvaults.Any(os => os.Id == s.SubvaultId) &&
|
if(!newUser)
|
||||||
(s.Id == default(Guid) || currentUserSubvaults.Any(cs => cs.Id == s.Id)));
|
{
|
||||||
|
filteredSubvaults = filteredSubvaults.Where(s =>
|
||||||
var subvaultsToDelete = currentUserSubvaults.Where(cs => !subvaults.Any(s => s.Id == cs.Id));
|
s.Id == default(Guid) || currentUserSubvaults.Any(cs => cs.Id == s.Id));
|
||||||
|
}
|
||||||
|
|
||||||
foreach(var subvault in filteredSubvaults)
|
foreach(var subvault in filteredSubvaults)
|
||||||
{
|
{
|
||||||
|
subvault.OrganizationUserId = user.Id;
|
||||||
await _subvaultUserRepository.UpsertAsync(subvault);
|
await _subvaultUserRepository.UpsertAsync(subvault);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(var subvault in subvaultsToDelete)
|
if(!newUser)
|
||||||
{
|
{
|
||||||
await _subvaultUserRepository.DeleteAsync(subvault);
|
var subvaultsToDelete = currentUserSubvaults.Where(cs => !subvaults.Any(s => s.Id == cs.Id));
|
||||||
|
foreach(var subvault in subvaultsToDelete)
|
||||||
|
{
|
||||||
|
await _subvaultUserRepository.DeleteAsync(subvault);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,8 @@
|
|||||||
<Build Include="dbo\Stored Procedures\OrganizationUserUserDetails_ReadById.sql" />
|
<Build Include="dbo\Stored Procedures\OrganizationUserUserDetails_ReadById.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\OrganizationUserUserDetails_ReadByOrganizationId.sql" />
|
<Build Include="dbo\Stored Procedures\OrganizationUserUserDetails_ReadByOrganizationId.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\Subvault_ReadByIdAdminUserId.sql" />
|
<Build Include="dbo\Stored Procedures\Subvault_ReadByIdAdminUserId.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\Subvault_ReadByUserId.sql" />
|
<Build Include="dbo\Stored Procedures\SubvaultUserDetails_ReadByUserId.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\Subvault_ReadByOrganizationIdAdminUserId.sql" />
|
<Build Include="dbo\Stored Procedures\Subvault_ReadByOrganizationIdAdminUserId.sql" />
|
||||||
|
<Build Include="dbo\Views\SubvaultUserDetailsView.sql" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -12,11 +12,9 @@ BEGIN
|
|||||||
[Id] = @Id
|
[Id] = @Id
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
S.*
|
*
|
||||||
FROM
|
FROM
|
||||||
[dbo].[SubvaultView] S
|
[dbo].[SubvaultUserDetailsView]
|
||||||
INNER JOIN
|
|
||||||
[dbo].[SubvaultUser] SU ON SU.[SubvaultId] = S.[Id]
|
|
||||||
WHERE
|
WHERE
|
||||||
SU.[OrganizationUserId] = @Id
|
[OrganizationUserId] = @Id
|
||||||
END
|
END
|
@ -1,15 +1,13 @@
|
|||||||
CREATE PROCEDURE [dbo].[Subvault_ReadByUserId]
|
CREATE PROCEDURE [dbo].[SubvaultUserDetails_ReadByUserId]
|
||||||
@UserId UNIQUEIDENTIFIER
|
@UserId UNIQUEIDENTIFIER
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON
|
SET NOCOUNT ON
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
S.*
|
SU.*
|
||||||
FROM
|
FROM
|
||||||
[dbo].[SubvaultView] S
|
[dbo].[SubvaultUserDetailsView] SU
|
||||||
INNER JOIN
|
|
||||||
[SubvaultUser] SU ON SU.[SubvaultId] = S.[Id]
|
|
||||||
INNER JOIN
|
INNER JOIN
|
||||||
[OrganizationUser] OU ON SU.[OrganizationUserId] = OU.[Id]
|
[OrganizationUser] OU ON SU.[OrganizationUserId] = OU.[Id]
|
||||||
WHERE
|
WHERE
|
13
src/Sql/dbo/Views/SubvaultUserDetailsView.sql
Normal file
13
src/Sql/dbo/Views/SubvaultUserDetailsView.sql
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
CREATE VIEW [dbo].[SubvaultUserDetailsView]
|
||||||
|
AS
|
||||||
|
SELECT
|
||||||
|
SU.[Id],
|
||||||
|
SU.[OrganizationUserId],
|
||||||
|
S.[Name],
|
||||||
|
S.[Id] SubvaultId,
|
||||||
|
SU.[ReadOnly],
|
||||||
|
SU.[Admin]
|
||||||
|
FROM
|
||||||
|
[dbo].[SubvaultUser] SU
|
||||||
|
INNER JOIN
|
||||||
|
[dbo].[Subvault] S ON S.[Id] = SU.[SubvaultId]
|
Loading…
Reference in New Issue
Block a user