1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-28 23:04:50 -05:00

create/get/update collection with groups

This commit is contained in:
Kyle Spearrin 2017-05-09 12:41:36 -04:00
parent d166f9cca3
commit 6c923102e9
11 changed files with 196 additions and 6 deletions

View File

@ -47,6 +47,18 @@ namespace Bit.Api.Controllers
return new CollectionResponseModel(collection); return new CollectionResponseModel(collection);
} }
[HttpGet("{id}/details")]
public async Task<CollectionDetailsResponseModel> GetDetails(string orgId, string id)
{
var collectionDetails = await _collectionRepository.GetByIdWithGroupsAsync(new Guid(id));
if(collectionDetails?.Item1 == null || !_currentContext.OrganizationAdmin(collectionDetails.Item1.OrganizationId))
{
throw new NotFoundException();
}
return new CollectionDetailsResponseModel(collectionDetails.Item1, collectionDetails.Item2);
}
[HttpGet("")] [HttpGet("")]
public async Task<ListResponseModel<CollectionResponseModel>> Get(string orgId) public async Task<ListResponseModel<CollectionResponseModel>> Get(string orgId)
{ {
@ -80,7 +92,7 @@ namespace Bit.Api.Controllers
} }
var collection = model.ToCollection(orgIdGuid); var collection = model.ToCollection(orgIdGuid);
await _collectionService.SaveAsync(collection); await _collectionService.SaveAsync(collection, model?.GroupIds.Select(g => new Guid(g)));
return new CollectionResponseModel(collection); return new CollectionResponseModel(collection);
} }
@ -94,7 +106,7 @@ namespace Bit.Api.Controllers
throw new NotFoundException(); throw new NotFoundException();
} }
await _collectionService.SaveAsync(model.ToCollection(collection)); await _collectionService.SaveAsync(model.ToCollection(collection), model?.GroupIds.Select(g => new Guid(g)));
return new CollectionResponseModel(collection); return new CollectionResponseModel(collection);
} }

View File

@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Bit.Core.Models.Table; using Bit.Core.Models.Table;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Collections.Generic;
namespace Bit.Core.Models.Api namespace Bit.Core.Models.Api
{ {
@ -12,6 +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 Collection ToCollection(Guid orgId) public Collection ToCollection(Guid orgId)
{ {

View File

@ -1,6 +1,7 @@
using System; using System;
using Bit.Core.Models.Table; using Bit.Core.Models.Table;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
using System.Collections.Generic;
namespace Bit.Core.Models.Api namespace Bit.Core.Models.Api
{ {
@ -37,6 +38,17 @@ namespace Bit.Core.Models.Api
public string Name { get; set; } public string Name { get; set; }
} }
public class CollectionDetailsResponseModel : CollectionResponseModel
{
public CollectionDetailsResponseModel(Collection collection, IEnumerable<Guid> collectionIds)
: base(collection, "collectionDetails")
{
CollectionIds = collectionIds;
}
public IEnumerable<Guid> CollectionIds { get; set; }
}
public class CollectionUserDetailsResponseModel : CollectionResponseModel public class CollectionUserDetailsResponseModel : CollectionResponseModel
{ {
public CollectionUserDetailsResponseModel(CollectionUserCollectionDetails collection) public CollectionUserDetailsResponseModel(CollectionUserCollectionDetails collection)

View File

@ -8,8 +8,11 @@ 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<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 ReplaceAsync(Collection obj, IEnumerable<Guid> groupIds);
} }
} }

View File

@ -6,6 +6,8 @@ using System.Data.SqlClient;
using System.Data; using System.Data;
using Dapper; using Dapper;
using System.Linq; using System.Linq;
using Newtonsoft.Json;
using Bit.Core.Utilities;
namespace Bit.Core.Repositories.SqlServer namespace Bit.Core.Repositories.SqlServer
{ {
@ -32,6 +34,22 @@ namespace Bit.Core.Repositories.SqlServer
} }
} }
public async Task<Tuple<Collection, ICollection<Guid>>> GetByIdWithGroupsAsync(Guid id)
{
using(var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryMultipleAsync(
$"[{Schema}].[Collection_ReadWithGroupsById]",
new { Id = id },
commandType: CommandType.StoredProcedure);
var collection = await results.ReadFirstOrDefaultAsync<Collection>();
var groupIds = (await results.ReadAsync<Guid>()).ToList();
return new Tuple<Collection, ICollection<Guid>>(collection, groupIds);
}
}
public async Task<ICollection<Collection>> GetManyByOrganizationIdAsync(Guid organizationId) public async Task<ICollection<Collection>> GetManyByOrganizationIdAsync(Guid organizationId)
{ {
using(var connection = new SqlConnection(ConnectionString)) using(var connection = new SqlConnection(ConnectionString))
@ -57,5 +75,39 @@ namespace Bit.Core.Repositories.SqlServer
return results.ToList(); return results.ToList();
} }
} }
public async Task CreateAsync(Collection obj, IEnumerable<Guid> groupIds)
{
obj.SetNewId();
var objWithGroups = JsonConvert.DeserializeObject<CollectionWithGroups>(JsonConvert.SerializeObject(obj));
objWithGroups.GroupIds = groupIds.ToGuidIdArrayTVP();
using(var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Collection_CreateWithGroups]",
objWithGroups,
commandType: CommandType.StoredProcedure);
}
}
public async Task ReplaceAsync(Collection obj, IEnumerable<Guid> groupIds)
{
var objWithGroups = JsonConvert.DeserializeObject<CollectionWithGroups>(JsonConvert.SerializeObject(obj));
objWithGroups.GroupIds = groupIds.ToGuidIdArrayTVP();
using(var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Collection_UpdateWithGroups]",
objWithGroups,
commandType: CommandType.StoredProcedure);
}
}
public class CollectionWithGroups : Collection
{
public DataTable GroupIds { get; set; }
}
} }
} }

View File

@ -1,10 +1,12 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.Core.Models.Table; using Bit.Core.Models.Table;
using System.Collections.Generic;
using System;
namespace Bit.Core.Services namespace Bit.Core.Services
{ {
public interface ICollectionService public interface ICollectionService
{ {
Task SaveAsync(Collection collection); Task SaveAsync(Collection collection, IEnumerable<Guid> groupIds = null);
} }
} }

View File

@ -3,6 +3,7 @@ using System.Threading.Tasks;
using Bit.Core.Exceptions; 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;
namespace Bit.Core.Services namespace Bit.Core.Services
{ {
@ -31,7 +32,7 @@ namespace Bit.Core.Services
_mailService = mailService; _mailService = mailService;
} }
public async Task SaveAsync(Collection collection) public async Task SaveAsync(Collection collection, IEnumerable<Guid> groupIds = null)
{ {
if(collection.Id == default(Guid)) if(collection.Id == default(Guid))
{ {
@ -51,11 +52,25 @@ namespace Bit.Core.Services
} }
} }
await _collectionRepository.CreateAsync(collection); if(groupIds == null)
{
await _collectionRepository.CreateAsync(collection);
}
else
{
await _collectionRepository.CreateAsync(collection, groupIds);
}
} }
else else
{ {
await _collectionRepository.ReplaceAsync(collection); if(groupIds == null)
{
await _collectionRepository.ReplaceAsync(collection);
}
else
{
await _collectionRepository.ReplaceAsync(collection, groupIds);
}
} }
} }
} }

View File

@ -187,5 +187,8 @@
<Build Include="dbo\Stored Procedures\Group_CreateWithCollections.sql" /> <Build Include="dbo\Stored Procedures\Group_CreateWithCollections.sql" />
<Build Include="dbo\Stored Procedures\Group_UpdateWithCollections.sql" /> <Build Include="dbo\Stored Procedures\Group_UpdateWithCollections.sql" />
<Build Include="dbo\Stored Procedures\Group_ReadWithCollectionsById.sql" /> <Build Include="dbo\Stored Procedures\Group_ReadWithCollectionsById.sql" />
<Build Include="dbo\Stored Procedures\Collection_UpdateWithGroups.sql" />
<Build Include="dbo\Stored Procedures\Collection_CreateWithGroups.sql" />
<Build Include="dbo\Stored Procedures\Collection_ReadWithGroupsById.sql" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,34 @@
CREATE PROCEDURE [dbo].[Collection_CreateWithGroups]
@Id UNIQUEIDENTIFIER,
@OrganizationId UNIQUEIDENTIFIER,
@Name VARCHAR(MAX),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@GroupIds AS [dbo].[GuidIdArray] READONLY
AS
BEGIN
SET NOCOUNT ON
EXEC [dbo].[Collection_Create] @Id, @OrganizationId, @Name, @CreationDate, @RevisionDate
;WITH [AvailableGroupsCTE] AS(
SELECT
[Id]
FROM
[dbo].[Group]
WHERE
[OrganizationId] = @OrganizationId
)
INSERT INTO [dbo].[CollectionGroup]
(
[CollectionId],
[GroupId]
)
SELECT
@Id,
[Id]
FROM
@GroupIds
WHERE
[Id] IN (SELECT [Id] FROM [AvailableGroupsCTE])
END

View File

@ -0,0 +1,15 @@
CREATE PROCEDURE [dbo].[Collection_ReadWithGroupsById]
@Id UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
EXEC [dbo].[Collection_ReadById] @Id
SELECT
[GroupId]
FROM
[dbo].[CollectionGroup]
WHERE
[CollectionId] = @Id
END

View File

@ -0,0 +1,40 @@
CREATE PROCEDURE [dbo].[Collection_UpdateWithGroups]
@Id UNIQUEIDENTIFIER,
@OrganizationId UNIQUEIDENTIFIER,
@Name VARCHAR(MAX),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@GroupIds AS [dbo].[GuidIdArray] READONLY
AS
BEGIN
SET NOCOUNT ON
EXEC [dbo].[Collection_Update] @Id, @OrganizationId, @Name, @CreationDate, @RevisionDate
;WITH [AvailableGroupsCTE] AS(
SELECT
Id
FROM
[dbo].[Group]
WHERE
OrganizationId = @OrganizationId
)
MERGE
[dbo].[CollectionGroup] AS [Target]
USING
@GroupIds AS [Source]
ON
[Target].[CollectionId] = @Id
AND [Target].[GroupId] = [Source].[Id]
WHEN NOT MATCHED BY TARGET
AND [Source].[Id] IN (SELECT [Id] FROM [AvailableGroupsCTE]) THEN
INSERT VALUES
(
@Id,
[Source].[Id]
)
WHEN NOT MATCHED BY SOURCE
AND [Target].[CollectionId] = @Id THEN
DELETE
;
END