using System.Data; using System.Text.Json; using Bit.Core.Entities; using Bit.Core.Models.Data; using Bit.Core.Repositories; using Bit.Core.Settings; using Dapper; using Microsoft.Data.SqlClient; namespace Bit.Infrastructure.Dapper.Repositories; public class CollectionRepository : Repository, ICollectionRepository { public CollectionRepository(GlobalSettings globalSettings) : this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString) { } public CollectionRepository(string connectionString, string readOnlyConnectionString) : base(connectionString, readOnlyConnectionString) { } public async Task GetCountByOrganizationIdAsync(Guid organizationId) { using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.ExecuteScalarAsync( "[dbo].[Collection_ReadCountByOrganizationId]", new { OrganizationId = organizationId }, commandType: CommandType.StoredProcedure); return results; } } public async Task> GetByIdWithAccessAsync(Guid id) { using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.QueryMultipleAsync( $"[{Schema}].[Collection_ReadWithGroupsAndUsersById]", new { Id = id }, commandType: CommandType.StoredProcedure); var collection = await results.ReadFirstOrDefaultAsync(); var groups = (await results.ReadAsync()).ToList(); var users = (await results.ReadAsync()).ToList(); var access = new CollectionAccessDetails { Groups = groups, Users = users }; return new Tuple(collection, access); } } public async Task> GetByIdWithAccessAsync( Guid id, Guid userId) { using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.QueryMultipleAsync( $"[{Schema}].[Collection_ReadWithGroupsAndUsersByIdUserId]", new { Id = id, UserId = userId }, commandType: CommandType.StoredProcedure); var collection = await results.ReadFirstOrDefaultAsync(); var groups = (await results.ReadAsync()).ToList(); var users = (await results.ReadAsync()).ToList(); var access = new CollectionAccessDetails { Groups = groups, Users = users }; return new Tuple(collection, access); } } public async Task> GetManyByManyIdsAsync(IEnumerable collectionIds) { using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.QueryAsync( $"[{Schema}].[Collection_ReadByIds]", new { Ids = collectionIds.ToGuidIdArrayTVP() }, commandType: CommandType.StoredProcedure); return results.ToList(); } } public async Task> GetManyByOrganizationIdAsync(Guid organizationId) { using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.QueryAsync( $"[{Schema}].[{Table}_ReadByOrganizationId]", new { OrganizationId = organizationId }, commandType: CommandType.StoredProcedure); return results.ToList(); } } public async Task>> GetManyByOrganizationIdWithAccessAsync(Guid organizationId) { using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.QueryMultipleAsync( $"[{Schema}].[Collection_ReadWithGroupsAndUsersByOrganizationId]", new { OrganizationId = organizationId }, commandType: CommandType.StoredProcedure); var collections = (await results.ReadAsync()); var groups = (await results.ReadAsync()) .GroupBy(g => g.CollectionId); var users = (await results.ReadAsync()) .GroupBy(u => u.CollectionId); return collections.Select(collection => new Tuple( collection, new CollectionAccessDetails { Groups = groups .FirstOrDefault(g => g.Key == collection.Id)? .Select(g => new CollectionAccessSelection { Id = g.GroupId, HidePasswords = g.HidePasswords, ReadOnly = g.ReadOnly, Manage = g.Manage }).ToList() ?? new List(), Users = users .FirstOrDefault(u => u.Key == collection.Id)? .Select(c => new CollectionAccessSelection { Id = c.OrganizationUserId, HidePasswords = c.HidePasswords, ReadOnly = c.ReadOnly, Manage = c.Manage }).ToList() ?? new List() } ) ).ToList(); } } public async Task>> GetManyByUserIdWithAccessAsync(Guid userId, Guid organizationId) { using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.QueryMultipleAsync( $"[{Schema}].[Collection_ReadWithGroupsAndUsersByUserId]", new { UserId = userId }, commandType: CommandType.StoredProcedure); var collections = (await results.ReadAsync()).Where(c => c.OrganizationId == organizationId); var groups = (await results.ReadAsync()) .GroupBy(g => g.CollectionId); var users = (await results.ReadAsync()) .GroupBy(u => u.CollectionId); return collections.Select(collection => new Tuple( collection, new CollectionAccessDetails { Groups = groups .FirstOrDefault(g => g.Key == collection.Id)? .Select(g => new CollectionAccessSelection { Id = g.GroupId, HidePasswords = g.HidePasswords, ReadOnly = g.ReadOnly, Manage = g.Manage }).ToList() ?? new List(), Users = users .FirstOrDefault(u => u.Key == collection.Id)? .Select(c => new CollectionAccessSelection { Id = c.OrganizationUserId, HidePasswords = c.HidePasswords, ReadOnly = c.ReadOnly, Manage = c.Manage }).ToList() ?? new List() } ) ).ToList(); } } public async Task GetByIdAsync(Guid id, Guid userId) { using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.QueryAsync( $"[{Schema}].[Collection_ReadByIdUserId]", new { Id = id, UserId = userId }, commandType: CommandType.StoredProcedure); return results.FirstOrDefault(); } } public async Task> GetManyByUserIdAsync(Guid userId) { using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.QueryAsync( $"[{Schema}].[Collection_ReadByUserId]", new { UserId = userId }, commandType: CommandType.StoredProcedure); return results.ToList(); } } public async Task CreateAsync(Collection obj, IEnumerable groups, IEnumerable users) { obj.SetNewId(); var objWithGroupsAndUsers = JsonSerializer.Deserialize(JsonSerializer.Serialize(obj)); objWithGroupsAndUsers.Groups = groups != null ? groups.ToArrayTVP() : Enumerable.Empty().ToArrayTVP(); objWithGroupsAndUsers.Users = users != null ? users.ToArrayTVP() : Enumerable.Empty().ToArrayTVP(); using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.ExecuteAsync( $"[{Schema}].[Collection_CreateWithGroupsAndUsers_V2]", objWithGroupsAndUsers, commandType: CommandType.StoredProcedure); } } public async Task ReplaceAsync(Collection obj, IEnumerable groups, IEnumerable users) { var objWithGroupsAndUsers = JsonSerializer.Deserialize(JsonSerializer.Serialize(obj)); objWithGroupsAndUsers.Groups = groups != null ? groups.ToArrayTVP() : Enumerable.Empty().ToArrayTVP(); objWithGroupsAndUsers.Users = users != null ? users.ToArrayTVP() : Enumerable.Empty().ToArrayTVP(); using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.ExecuteAsync( $"[{Schema}].[Collection_UpdateWithGroupsAndUsers_V2]", objWithGroupsAndUsers, commandType: CommandType.StoredProcedure); } } public async Task DeleteManyAsync(IEnumerable collectionIds) { using (var connection = new SqlConnection(ConnectionString)) { await connection.ExecuteAsync("[dbo].[Collection_DeleteByIds]", new { Ids = collectionIds.ToGuidIdArrayTVP() }, commandType: CommandType.StoredProcedure); } } public async Task CreateOrUpdateAccessForManyAsync(Guid organizationId, IEnumerable collectionIds, IEnumerable users, IEnumerable groups) { using (var connection = new SqlConnection(ConnectionString)) { var usersArray = users != null ? users.ToArrayTVP() : Enumerable.Empty().ToArrayTVP(); var groupsArray = groups != null ? groups.ToArrayTVP() : Enumerable.Empty().ToArrayTVP(); var results = await connection.ExecuteAsync( $"[{Schema}].[Collection_CreateOrUpdateAccessForMany]", new { OrganizationId = organizationId, CollectionIds = collectionIds.ToGuidIdArrayTVP(), Users = usersArray, Groups = groupsArray }, commandType: CommandType.StoredProcedure); } } public async Task CreateUserAsync(Guid collectionId, Guid organizationUserId) { using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.ExecuteAsync( $"[{Schema}].[CollectionUser_Create]", new { CollectionId = collectionId, OrganizationUserId = organizationUserId }, commandType: CommandType.StoredProcedure); } } public async Task DeleteUserAsync(Guid collectionId, Guid organizationUserId) { using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.ExecuteAsync( $"[{Schema}].[CollectionUser_Delete]", new { CollectionId = collectionId, OrganizationUserId = organizationUserId }, commandType: CommandType.StoredProcedure); } } public async Task UpdateUsersAsync(Guid id, IEnumerable users) { using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.ExecuteAsync( $"[{Schema}].[CollectionUser_UpdateUsers_V2]", new { CollectionId = id, Users = users.ToArrayTVP() }, commandType: CommandType.StoredProcedure); } } public async Task> GetManyUsersByIdAsync(Guid id) { using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.QueryAsync( $"[{Schema}].[CollectionUser_ReadByCollectionId]", new { CollectionId = id }, commandType: CommandType.StoredProcedure); return results.ToList(); } } public class CollectionWithGroupsAndUsers : Collection { public DataTable Groups { get; set; } public DataTable Users { get; set; } } }