mirror of
https://github.com/bitwarden/server.git
synced 2025-05-07 12:42:24 -05:00
collection groups management
This commit is contained in:
parent
2b8db4d1ed
commit
d7f9977382
@ -8,6 +8,7 @@ using Bit.Core.Models.Api;
|
|||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
|
||||||
namespace Bit.Api.Controllers
|
namespace Bit.Api.Controllers
|
||||||
{
|
{
|
||||||
@ -91,7 +92,7 @@ namespace Bit.Api.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
var collection = model.ToCollection(orgIdGuid);
|
var collection = model.ToCollection(orgIdGuid);
|
||||||
await _collectionService.SaveAsync(collection, model.GroupIds?.Select(g => new Guid(g)));
|
await _collectionService.SaveAsync(collection, model.Groups?.Select(g => g.ToSelectionReadOnly()));
|
||||||
return new CollectionResponseModel(collection);
|
return new CollectionResponseModel(collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +106,8 @@ namespace Bit.Api.Controllers
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
await _collectionService.SaveAsync(model.ToCollection(collection), model.GroupIds?.Select(g => new Guid(g)));
|
await _collectionService.SaveAsync(model.ToCollection(collection),
|
||||||
|
model.Groups?.Select(g => g.ToSelectionReadOnly()));
|
||||||
return new CollectionResponseModel(collection);
|
return new CollectionResponseModel(collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ namespace Bit.Core.Models.Api
|
|||||||
[EncryptedString]
|
[EncryptedString]
|
||||||
[StringLength(300)]
|
[StringLength(300)]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public IEnumerable<string> GroupIds { get; set; }
|
public IEnumerable<SelectionReadOnlyRequestModel> Groups { get; set; }
|
||||||
|
|
||||||
public Collection ToCollection(Guid orgId)
|
public Collection ToCollection(Guid orgId)
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Bit.Core.Models.Api
|
namespace Bit.Core.Models.Api
|
||||||
{
|
{
|
||||||
@ -26,12 +28,12 @@ namespace Bit.Core.Models.Api
|
|||||||
|
|
||||||
public class CollectionDetailsResponseModel : CollectionResponseModel
|
public class CollectionDetailsResponseModel : CollectionResponseModel
|
||||||
{
|
{
|
||||||
public CollectionDetailsResponseModel(Collection collection, IEnumerable<Guid> groupIds)
|
public CollectionDetailsResponseModel(Collection collection, IEnumerable<SelectionReadOnly> groups)
|
||||||
: base(collection, "collectionDetails")
|
: base(collection, "collectionDetails")
|
||||||
{
|
{
|
||||||
GroupIds = groupIds;
|
Groups = groups.Select(g => new SelectionReadOnlyResponseModel(g));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Guid> GroupIds { get; set; }
|
public IEnumerable<SelectionReadOnlyResponseModel> Groups { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,18 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
|
||||||
namespace Bit.Core.Repositories
|
namespace Bit.Core.Repositories
|
||||||
{
|
{
|
||||||
public interface ICollectionRepository : IRepository<Collection, Guid>
|
public interface ICollectionRepository : IRepository<Collection, Guid>
|
||||||
{
|
{
|
||||||
Task<int> GetCountByOrganizationIdAsync(Guid organizationId);
|
Task<int> GetCountByOrganizationIdAsync(Guid organizationId);
|
||||||
Task<Tuple<Collection, ICollection<Guid>>> GetByIdWithGroupsAsync(Guid id);
|
Task<Tuple<Collection, ICollection<SelectionReadOnly>>> GetByIdWithGroupsAsync(Guid id);
|
||||||
Task<ICollection<Collection>> GetManyByOrganizationIdAsync(Guid organizationId);
|
Task<ICollection<Collection>> GetManyByOrganizationIdAsync(Guid organizationId);
|
||||||
Task<ICollection<Collection>> GetManyByUserIdAsync(Guid userId);
|
Task<ICollection<Collection>> GetManyByUserIdAsync(Guid userId);
|
||||||
Task CreateAsync(Collection obj, IEnumerable<Guid> groupIds);
|
Task CreateAsync(Collection obj, IEnumerable<SelectionReadOnly> groups);
|
||||||
Task ReplaceAsync(Collection obj, IEnumerable<Guid> groupIds);
|
Task ReplaceAsync(Collection obj, IEnumerable<SelectionReadOnly> groups);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using Dapper;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
|
||||||
namespace Bit.Core.Repositories.SqlServer
|
namespace Bit.Core.Repositories.SqlServer
|
||||||
{
|
{
|
||||||
@ -34,7 +35,7 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Tuple<Collection, ICollection<Guid>>> GetByIdWithGroupsAsync(Guid id)
|
public async Task<Tuple<Collection, ICollection<SelectionReadOnly>>> GetByIdWithGroupsAsync(Guid id)
|
||||||
{
|
{
|
||||||
using(var connection = new SqlConnection(ConnectionString))
|
using(var connection = new SqlConnection(ConnectionString))
|
||||||
{
|
{
|
||||||
@ -44,9 +45,9 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
var collection = await results.ReadFirstOrDefaultAsync<Collection>();
|
var collection = await results.ReadFirstOrDefaultAsync<Collection>();
|
||||||
var groupIds = (await results.ReadAsync<Guid>()).ToList();
|
var groups = (await results.ReadAsync<SelectionReadOnly>()).ToList();
|
||||||
|
|
||||||
return new Tuple<Collection, ICollection<Guid>>(collection, groupIds);
|
return new Tuple<Collection, ICollection<SelectionReadOnly>>(collection, groups);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,11 +81,11 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateAsync(Collection obj, IEnumerable<Guid> groupIds)
|
public async Task CreateAsync(Collection obj, IEnumerable<SelectionReadOnly> groups)
|
||||||
{
|
{
|
||||||
obj.SetNewId();
|
obj.SetNewId();
|
||||||
var objWithGroups = JsonConvert.DeserializeObject<CollectionWithGroups>(JsonConvert.SerializeObject(obj));
|
var objWithGroups = JsonConvert.DeserializeObject<CollectionWithGroups>(JsonConvert.SerializeObject(obj));
|
||||||
objWithGroups.GroupIds = groupIds.ToGuidIdArrayTVP();
|
objWithGroups.Groups = groups.ToArrayTVP();
|
||||||
|
|
||||||
using(var connection = new SqlConnection(ConnectionString))
|
using(var connection = new SqlConnection(ConnectionString))
|
||||||
{
|
{
|
||||||
@ -95,10 +96,10 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ReplaceAsync(Collection obj, IEnumerable<Guid> groupIds)
|
public async Task ReplaceAsync(Collection obj, IEnumerable<SelectionReadOnly> groups)
|
||||||
{
|
{
|
||||||
var objWithGroups = JsonConvert.DeserializeObject<CollectionWithGroups>(JsonConvert.SerializeObject(obj));
|
var objWithGroups = JsonConvert.DeserializeObject<CollectionWithGroups>(JsonConvert.SerializeObject(obj));
|
||||||
objWithGroups.GroupIds = groupIds.ToGuidIdArrayTVP();
|
objWithGroups.Groups = groups.ToArrayTVP();
|
||||||
|
|
||||||
using(var connection = new SqlConnection(ConnectionString))
|
using(var connection = new SqlConnection(ConnectionString))
|
||||||
{
|
{
|
||||||
@ -111,7 +112,7 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
|
|
||||||
public class CollectionWithGroups : Collection
|
public class CollectionWithGroups : Collection
|
||||||
{
|
{
|
||||||
public DataTable GroupIds { get; set; }
|
public DataTable Groups { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,12 @@
|
|||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System;
|
using System;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
public interface ICollectionService
|
public interface ICollectionService
|
||||||
{
|
{
|
||||||
Task SaveAsync(Collection collection, IEnumerable<Guid> groupIds = null);
|
Task SaveAsync(Collection collection, IEnumerable<SelectionReadOnly> groups = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ using Bit.Core.Exceptions;
|
|||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
@ -32,7 +33,7 @@ namespace Bit.Core.Services
|
|||||||
_mailService = mailService;
|
_mailService = mailService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SaveAsync(Collection collection, IEnumerable<Guid> groupIds = null)
|
public async Task SaveAsync(Collection collection, IEnumerable<SelectionReadOnly> groups = null)
|
||||||
{
|
{
|
||||||
var org = await _organizationRepository.GetByIdAsync(collection.OrganizationId);
|
var org = await _organizationRepository.GetByIdAsync(collection.OrganizationId);
|
||||||
if(org == null)
|
if(org == null)
|
||||||
@ -52,24 +53,24 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(groupIds == null || !org.UseGroups)
|
if(groups == null || !org.UseGroups)
|
||||||
{
|
{
|
||||||
await _collectionRepository.CreateAsync(collection);
|
await _collectionRepository.CreateAsync(collection);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _collectionRepository.CreateAsync(collection, groupIds);
|
await _collectionRepository.CreateAsync(collection, groups);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(groupIds == null || !org.UseGroups)
|
if(groups == null || !org.UseGroups)
|
||||||
{
|
{
|
||||||
await _collectionRepository.ReplaceAsync(collection);
|
await _collectionRepository.ReplaceAsync(collection);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _collectionRepository.ReplaceAsync(collection, groupIds);
|
await _collectionRepository.ReplaceAsync(collection, groups);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,8 @@ BEGIN
|
|||||||
EXEC [dbo].[Collection_ReadById] @Id
|
EXEC [dbo].[Collection_ReadById] @Id
|
||||||
|
|
||||||
SELECT
|
SELECT
|
||||||
[GroupId]
|
[GroupId] [Id],
|
||||||
|
[ReadOnly]
|
||||||
FROM
|
FROM
|
||||||
[dbo].[CollectionGroup]
|
[dbo].[CollectionGroup]
|
||||||
WHERE
|
WHERE
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
@Name VARCHAR(MAX),
|
@Name VARCHAR(MAX),
|
||||||
@CreationDate DATETIME2(7),
|
@CreationDate DATETIME2(7),
|
||||||
@RevisionDate DATETIME2(7),
|
@RevisionDate DATETIME2(7),
|
||||||
@GroupIds AS [dbo].[GuidIdArray] READONLY
|
@Groups AS [dbo].[SelectionReadOnlyArray] READONLY
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON
|
SET NOCOUNT ON
|
||||||
@ -22,7 +22,7 @@ BEGIN
|
|||||||
MERGE
|
MERGE
|
||||||
[dbo].[CollectionGroup] AS [Target]
|
[dbo].[CollectionGroup] AS [Target]
|
||||||
USING
|
USING
|
||||||
@GroupIds AS [Source]
|
@Groups AS [Source]
|
||||||
ON
|
ON
|
||||||
[Target].[CollectionId] = @Id
|
[Target].[CollectionId] = @Id
|
||||||
AND [Target].[GroupId] = [Source].[Id]
|
AND [Target].[GroupId] = [Source].[Id]
|
||||||
@ -32,8 +32,10 @@ BEGIN
|
|||||||
(
|
(
|
||||||
@Id,
|
@Id,
|
||||||
[Source].[Id],
|
[Source].[Id],
|
||||||
0
|
[Source].[ReadOnly]
|
||||||
)
|
)
|
||||||
|
WHEN MATCHED AND [Target].[ReadOnly] != [Source].[ReadOnly] THEN
|
||||||
|
UPDATE SET [Target].[ReadOnly] = [Source].[ReadOnly]
|
||||||
WHEN NOT MATCHED BY SOURCE
|
WHEN NOT MATCHED BY SOURCE
|
||||||
AND [Target].[CollectionId] = @Id THEN
|
AND [Target].[CollectionId] = @Id THEN
|
||||||
DELETE
|
DELETE
|
||||||
|
Loading…
x
Reference in New Issue
Block a user