1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-13 13:47:30 -05:00

Split out repositories to Infrastructure.Dapper / EntityFramework (#1759)

This commit is contained in:
Oscar Hinton
2022-01-11 10:40:51 +01:00
committed by GitHub
parent e2c6fc81f4
commit e4a10aae27
536 changed files with 1629 additions and 1589 deletions

View File

@ -0,0 +1,31 @@
using System;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public abstract class BaseRepository
{
static BaseRepository()
{
SqlMapper.AddTypeHandler(new DateTimeHandler());
}
public BaseRepository(string connectionString, string readOnlyConnectionString)
{
if (string.IsNullOrWhiteSpace(connectionString))
{
throw new ArgumentNullException(nameof(connectionString));
}
if (string.IsNullOrWhiteSpace(readOnlyConnectionString))
{
throw new ArgumentNullException(nameof(readOnlyConnectionString));
}
ConnectionString = connectionString;
ReadOnlyConnectionString = readOnlyConnectionString;
}
protected string ConnectionString { get; private set; }
protected string ReadOnlyConnectionString { get; private set; }
}
}

View File

@ -0,0 +1,954 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Core.Models.Data;
using Dapper;
using Newtonsoft.Json;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
{
public CipherRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public CipherRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<CipherDetails> GetByIdAsync(Guid id, Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<CipherDetails>(
$"[{Schema}].[CipherDetails_ReadByIdUserId]",
new { Id = id, UserId = userId },
commandType: CommandType.StoredProcedure);
return results.FirstOrDefault();
}
}
public async Task<CipherOrganizationDetails> GetOrganizationDetailsByIdAsync(Guid id)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<CipherDetails>(
$"[{Schema}].[CipherOrganizationDetails_ReadById]",
new { Id = id },
commandType: CommandType.StoredProcedure);
return results.FirstOrDefault();
}
}
public async Task<bool> GetCanEditByIdAsync(Guid userId, Guid cipherId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var result = await connection.QueryFirstOrDefaultAsync<bool>(
$"[{Schema}].[Cipher_ReadCanEditByIdUserId]",
new { UserId = userId, Id = cipherId },
commandType: CommandType.StoredProcedure);
return result;
}
}
public async Task<ICollection<CipherDetails>> GetManyByUserIdAsync(Guid userId, bool withOrganizations = true)
{
string sprocName = null;
if (withOrganizations)
{
sprocName = $"[{Schema}].[CipherDetails_ReadByUserId]";
}
else
{
sprocName = $"[{Schema}].[CipherDetails_ReadWithoutOrganizationsByUserId]";
}
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<CipherDetails>(
sprocName,
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results
.GroupBy(c => c.Id)
.Select(g => g.OrderByDescending(og => og.Edit).First())
.ToList();
}
}
public async Task<ICollection<Cipher>> GetManyByOrganizationIdAsync(Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Cipher>(
$"[{Schema}].[Cipher_ReadByOrganizationId]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task CreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
{
cipher.SetNewId();
var objWithCollections = JsonConvert.DeserializeObject<CipherWithCollections>(
JsonConvert.SerializeObject(cipher));
objWithCollections.CollectionIds = collectionIds.ToGuidIdArrayTVP();
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Cipher_CreateWithCollections]",
objWithCollections,
commandType: CommandType.StoredProcedure);
}
}
public async Task CreateAsync(CipherDetails cipher)
{
cipher.SetNewId();
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[CipherDetails_Create]",
cipher,
commandType: CommandType.StoredProcedure);
}
}
public async Task CreateAsync(CipherDetails cipher, IEnumerable<Guid> collectionIds)
{
cipher.SetNewId();
var objWithCollections = JsonConvert.DeserializeObject<CipherDetailsWithCollections>(
JsonConvert.SerializeObject(cipher));
objWithCollections.CollectionIds = collectionIds.ToGuidIdArrayTVP();
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[CipherDetails_CreateWithCollections]",
objWithCollections,
commandType: CommandType.StoredProcedure);
}
}
public async Task ReplaceAsync(CipherDetails obj)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[CipherDetails_Update]",
obj,
commandType: CommandType.StoredProcedure);
}
}
public async Task UpsertAsync(CipherDetails cipher)
{
if (cipher.Id.Equals(default))
{
await CreateAsync(cipher);
}
else
{
await ReplaceAsync(cipher);
}
}
public async Task<bool> ReplaceAsync(Cipher obj, IEnumerable<Guid> collectionIds)
{
var objWithCollections = JsonConvert.DeserializeObject<CipherWithCollections>(
JsonConvert.SerializeObject(obj));
objWithCollections.CollectionIds = collectionIds.ToGuidIdArrayTVP();
using (var connection = new SqlConnection(ConnectionString))
{
var result = await connection.ExecuteScalarAsync<int>(
$"[{Schema}].[Cipher_UpdateWithCollections]",
objWithCollections,
commandType: CommandType.StoredProcedure);
return result >= 0;
}
}
public async Task UpdatePartialAsync(Guid id, Guid userId, Guid? folderId, bool favorite)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Cipher_UpdatePartial]",
new { Id = id, UserId = userId, FolderId = folderId, Favorite = favorite },
commandType: CommandType.StoredProcedure);
}
}
public async Task UpdateAttachmentAsync(CipherAttachment attachment)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Cipher_UpdateAttachment]",
attachment,
commandType: CommandType.StoredProcedure);
}
}
public async Task DeleteAttachmentAsync(Guid cipherId, string attachmentId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Cipher_DeleteAttachment]",
new { Id = cipherId, AttachmentId = attachmentId },
commandType: CommandType.StoredProcedure);
}
}
public async Task DeleteAsync(IEnumerable<Guid> ids, Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Cipher_Delete]",
new { Ids = ids.ToGuidIdArrayTVP(), UserId = userId },
commandType: CommandType.StoredProcedure);
}
}
public async Task DeleteByIdsOrganizationIdAsync(IEnumerable<Guid> ids, Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Cipher_DeleteByIdsOrganizationId]",
new { Ids = ids.ToGuidIdArrayTVP(), OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
}
}
public async Task SoftDeleteByIdsOrganizationIdAsync(IEnumerable<Guid> ids, Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Cipher_SoftDeleteByIdsOrganizationId]",
new { Ids = ids.ToGuidIdArrayTVP(), OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
}
}
public async Task MoveAsync(IEnumerable<Guid> ids, Guid? folderId, Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Cipher_Move]",
new { Ids = ids.ToGuidIdArrayTVP(), FolderId = folderId, UserId = userId },
commandType: CommandType.StoredProcedure);
}
}
public async Task DeleteByUserIdAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Cipher_DeleteByUserId]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
}
}
public async Task DeleteByOrganizationIdAsync(Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Cipher_DeleteByOrganizationId]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
}
}
public Task UpdateUserKeysAndCiphersAsync(User user, IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders, IEnumerable<Send> sends)
{
using (var connection = new SqlConnection(ConnectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
try
{
// 1. Update user.
using (var cmd = new SqlCommand("[dbo].[User_UpdateKeys]", connection, transaction))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Id", SqlDbType.UniqueIdentifier).Value = user.Id;
cmd.Parameters.Add("@SecurityStamp", SqlDbType.NVarChar).Value = user.SecurityStamp;
cmd.Parameters.Add("@Key", SqlDbType.VarChar).Value = user.Key;
if (string.IsNullOrWhiteSpace(user.PrivateKey))
{
cmd.Parameters.Add("@PrivateKey", SqlDbType.VarChar).Value = DBNull.Value;
}
else
{
cmd.Parameters.Add("@PrivateKey", SqlDbType.VarChar).Value = user.PrivateKey;
}
cmd.Parameters.Add("@RevisionDate", SqlDbType.DateTime2).Value = user.RevisionDate;
cmd.ExecuteNonQuery();
}
// 2. Create temp tables to bulk copy into.
var sqlCreateTemp = @"
SELECT TOP 0 *
INTO #TempCipher
FROM [dbo].[Cipher]
SELECT TOP 0 *
INTO #TempFolder
FROM [dbo].[Folder]
SELECT TOP 0 *
INTO #TempSend
FROM [dbo].[Send]";
using (var cmd = new SqlCommand(sqlCreateTemp, connection, transaction))
{
cmd.ExecuteNonQuery();
}
// 3. Bulk copy into temp tables.
if (ciphers.Any())
{
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction))
{
bulkCopy.DestinationTableName = "#TempCipher";
var dataTable = BuildCiphersTable(bulkCopy, ciphers);
bulkCopy.WriteToServer(dataTable);
}
}
if (folders.Any())
{
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction))
{
bulkCopy.DestinationTableName = "#TempFolder";
var dataTable = BuildFoldersTable(bulkCopy, folders);
bulkCopy.WriteToServer(dataTable);
}
}
if (sends.Any())
{
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction))
{
bulkCopy.DestinationTableName = "#TempSend";
var dataTable = BuildSendsTable(bulkCopy, sends);
bulkCopy.WriteToServer(dataTable);
}
}
// 4. Insert into real tables from temp tables and clean up.
var sql = string.Empty;
if (ciphers.Any())
{
sql += @"
UPDATE
[dbo].[Cipher]
SET
[Data] = TC.[Data],
[Attachments] = TC.[Attachments],
[RevisionDate] = TC.[RevisionDate]
FROM
[dbo].[Cipher] C
INNER JOIN
#TempCipher TC ON C.Id = TC.Id
WHERE
C.[UserId] = @UserId";
}
if (folders.Any())
{
sql += @"
UPDATE
[dbo].[Folder]
SET
[Name] = TF.[Name],
[RevisionDate] = TF.[RevisionDate]
FROM
[dbo].[Folder] F
INNER JOIN
#TempFolder TF ON F.Id = TF.Id
WHERE
F.[UserId] = @UserId";
}
if (sends.Any())
{
sql += @"
UPDATE
[dbo].[Send]
SET
[Key] = TS.[Key],
[RevisionDate] = TS.[RevisionDate]
FROM
[dbo].[Send] S
INNER JOIN
#TempSend TS ON S.Id = TS.Id
WHERE
S.[UserId] = @UserId";
}
sql += @"
DROP TABLE #TempCipher
DROP TABLE #TempFolder
DROP TABLE #TempSend";
using (var cmd = new SqlCommand(sql, connection, transaction))
{
cmd.Parameters.Add("@UserId", SqlDbType.UniqueIdentifier).Value = user.Id;
cmd.ExecuteNonQuery();
}
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
return Task.FromResult(0);
}
public async Task UpdateCiphersAsync(Guid userId, IEnumerable<Cipher> ciphers)
{
if (!ciphers.Any())
{
return;
}
using (var connection = new SqlConnection(ConnectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
try
{
// 1. Create temp tables to bulk copy into.
var sqlCreateTemp = @"
SELECT TOP 0 *
INTO #TempCipher
FROM [dbo].[Cipher]";
using (var cmd = new SqlCommand(sqlCreateTemp, connection, transaction))
{
cmd.ExecuteNonQuery();
}
// 2. Bulk copy into temp tables.
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction))
{
bulkCopy.DestinationTableName = "#TempCipher";
var dataTable = BuildCiphersTable(bulkCopy, ciphers);
bulkCopy.WriteToServer(dataTable);
}
// 3. Insert into real tables from temp tables and clean up.
// Intentionally not including Favorites, Folders, and CreationDate
// since those are not meant to be bulk updated at this time
var sql = @"
UPDATE
[dbo].[Cipher]
SET
[UserId] = TC.[UserId],
[OrganizationId] = TC.[OrganizationId],
[Type] = TC.[Type],
[Data] = TC.[Data],
[Attachments] = TC.[Attachments],
[RevisionDate] = TC.[RevisionDate],
[DeletedDate] = TC.[DeletedDate]
FROM
[dbo].[Cipher] C
INNER JOIN
#TempCipher TC ON C.Id = TC.Id
WHERE
C.[UserId] = @UserId
DROP TABLE #TempCipher";
using (var cmd = new SqlCommand(sql, connection, transaction))
{
cmd.Parameters.Add("@UserId", SqlDbType.UniqueIdentifier).Value = userId;
cmd.ExecuteNonQuery();
}
await connection.ExecuteAsync(
$"[{Schema}].[User_BumpAccountRevisionDate]",
new { Id = userId },
commandType: CommandType.StoredProcedure, transaction: transaction);
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
}
public async Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders)
{
if (!ciphers.Any())
{
return;
}
using (var connection = new SqlConnection(ConnectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
try
{
if (folders.Any())
{
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction))
{
bulkCopy.DestinationTableName = "[dbo].[Folder]";
var dataTable = BuildFoldersTable(bulkCopy, folders);
bulkCopy.WriteToServer(dataTable);
}
}
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction))
{
bulkCopy.DestinationTableName = "[dbo].[Cipher]";
var dataTable = BuildCiphersTable(bulkCopy, ciphers);
bulkCopy.WriteToServer(dataTable);
}
await connection.ExecuteAsync(
$"[{Schema}].[User_BumpAccountRevisionDate]",
new { Id = ciphers.First().UserId },
commandType: CommandType.StoredProcedure, transaction: transaction);
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
}
public async Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections,
IEnumerable<CollectionCipher> collectionCiphers)
{
if (!ciphers.Any())
{
return;
}
using (var connection = new SqlConnection(ConnectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
try
{
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction))
{
bulkCopy.DestinationTableName = "[dbo].[Cipher]";
var dataTable = BuildCiphersTable(bulkCopy, ciphers);
bulkCopy.WriteToServer(dataTable);
}
if (collections.Any())
{
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction))
{
bulkCopy.DestinationTableName = "[dbo].[Collection]";
var dataTable = BuildCollectionsTable(bulkCopy, collections);
bulkCopy.WriteToServer(dataTable);
}
if (collectionCiphers.Any())
{
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction))
{
bulkCopy.DestinationTableName = "[dbo].[CollectionCipher]";
var dataTable = BuildCollectionCiphersTable(bulkCopy, collectionCiphers);
bulkCopy.WriteToServer(dataTable);
}
}
}
await connection.ExecuteAsync(
$"[{Schema}].[User_BumpAccountRevisionDateByOrganizationId]",
new { OrganizationId = ciphers.First().OrganizationId },
commandType: CommandType.StoredProcedure, transaction: transaction);
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
}
public async Task SoftDeleteAsync(IEnumerable<Guid> ids, Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Cipher_SoftDelete]",
new { Ids = ids.ToGuidIdArrayTVP(), UserId = userId },
commandType: CommandType.StoredProcedure);
}
}
public async Task<DateTime> RestoreAsync(IEnumerable<Guid> ids, Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteScalarAsync<DateTime>(
$"[{Schema}].[Cipher_Restore]",
new { Ids = ids.ToGuidIdArrayTVP(), UserId = userId },
commandType: CommandType.StoredProcedure);
return results;
}
}
public async Task DeleteDeletedAsync(DateTime deletedDateBefore)
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(
$"[{Schema}].[Cipher_DeleteDeleted]",
new { DeletedDateBefore = deletedDateBefore },
commandType: CommandType.StoredProcedure,
commandTimeout: 43200);
}
}
private DataTable BuildCiphersTable(SqlBulkCopy bulkCopy, IEnumerable<Cipher> ciphers)
{
var c = ciphers.FirstOrDefault();
if (c == null)
{
throw new ApplicationException("Must have some ciphers to bulk import.");
}
var ciphersTable = new DataTable("CipherDataTable");
var idColumn = new DataColumn(nameof(c.Id), c.Id.GetType());
ciphersTable.Columns.Add(idColumn);
var userIdColumn = new DataColumn(nameof(c.UserId), typeof(Guid));
ciphersTable.Columns.Add(userIdColumn);
var organizationId = new DataColumn(nameof(c.OrganizationId), typeof(Guid));
ciphersTable.Columns.Add(organizationId);
var typeColumn = new DataColumn(nameof(c.Type), typeof(short));
ciphersTable.Columns.Add(typeColumn);
var dataColumn = new DataColumn(nameof(c.Data), typeof(string));
ciphersTable.Columns.Add(dataColumn);
var favoritesColumn = new DataColumn(nameof(c.Favorites), typeof(string));
ciphersTable.Columns.Add(favoritesColumn);
var foldersColumn = new DataColumn(nameof(c.Folders), typeof(string));
ciphersTable.Columns.Add(foldersColumn);
var attachmentsColumn = new DataColumn(nameof(c.Attachments), typeof(string));
ciphersTable.Columns.Add(attachmentsColumn);
var creationDateColumn = new DataColumn(nameof(c.CreationDate), c.CreationDate.GetType());
ciphersTable.Columns.Add(creationDateColumn);
var revisionDateColumn = new DataColumn(nameof(c.RevisionDate), c.RevisionDate.GetType());
ciphersTable.Columns.Add(revisionDateColumn);
var deletedDateColumn = new DataColumn(nameof(c.DeletedDate), typeof(DateTime));
ciphersTable.Columns.Add(deletedDateColumn);
var repromptColumn = new DataColumn(nameof(c.Reprompt), typeof(short));
ciphersTable.Columns.Add(repromptColumn);
foreach (DataColumn col in ciphersTable.Columns)
{
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
}
var keys = new DataColumn[1];
keys[0] = idColumn;
ciphersTable.PrimaryKey = keys;
foreach (var cipher in ciphers)
{
var row = ciphersTable.NewRow();
row[idColumn] = cipher.Id;
row[userIdColumn] = cipher.UserId.HasValue ? (object)cipher.UserId.Value : DBNull.Value;
row[organizationId] = cipher.OrganizationId.HasValue ? (object)cipher.OrganizationId.Value : DBNull.Value;
row[typeColumn] = (short)cipher.Type;
row[dataColumn] = cipher.Data;
row[favoritesColumn] = cipher.Favorites;
row[foldersColumn] = cipher.Folders;
row[attachmentsColumn] = cipher.Attachments;
row[creationDateColumn] = cipher.CreationDate;
row[revisionDateColumn] = cipher.RevisionDate;
row[deletedDateColumn] = cipher.DeletedDate.HasValue ? (object)cipher.DeletedDate : DBNull.Value;
row[repromptColumn] = cipher.Reprompt;
ciphersTable.Rows.Add(row);
}
return ciphersTable;
}
private DataTable BuildFoldersTable(SqlBulkCopy bulkCopy, IEnumerable<Folder> folders)
{
var f = folders.FirstOrDefault();
if (f == null)
{
throw new ApplicationException("Must have some folders to bulk import.");
}
var foldersTable = new DataTable("FolderDataTable");
var idColumn = new DataColumn(nameof(f.Id), f.Id.GetType());
foldersTable.Columns.Add(idColumn);
var userIdColumn = new DataColumn(nameof(f.UserId), f.UserId.GetType());
foldersTable.Columns.Add(userIdColumn);
var nameColumn = new DataColumn(nameof(f.Name), typeof(string));
foldersTable.Columns.Add(nameColumn);
var creationDateColumn = new DataColumn(nameof(f.CreationDate), f.CreationDate.GetType());
foldersTable.Columns.Add(creationDateColumn);
var revisionDateColumn = new DataColumn(nameof(f.RevisionDate), f.RevisionDate.GetType());
foldersTable.Columns.Add(revisionDateColumn);
foreach (DataColumn col in foldersTable.Columns)
{
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
}
var keys = new DataColumn[1];
keys[0] = idColumn;
foldersTable.PrimaryKey = keys;
foreach (var folder in folders)
{
var row = foldersTable.NewRow();
row[idColumn] = folder.Id;
row[userIdColumn] = folder.UserId;
row[nameColumn] = folder.Name;
row[creationDateColumn] = folder.CreationDate;
row[revisionDateColumn] = folder.RevisionDate;
foldersTable.Rows.Add(row);
}
return foldersTable;
}
private DataTable BuildCollectionsTable(SqlBulkCopy bulkCopy, IEnumerable<Collection> collections)
{
var c = collections.FirstOrDefault();
if (c == null)
{
throw new ApplicationException("Must have some collections to bulk import.");
}
var collectionsTable = new DataTable("CollectionDataTable");
var idColumn = new DataColumn(nameof(c.Id), c.Id.GetType());
collectionsTable.Columns.Add(idColumn);
var organizationIdColumn = new DataColumn(nameof(c.OrganizationId), c.OrganizationId.GetType());
collectionsTable.Columns.Add(organizationIdColumn);
var nameColumn = new DataColumn(nameof(c.Name), typeof(string));
collectionsTable.Columns.Add(nameColumn);
var creationDateColumn = new DataColumn(nameof(c.CreationDate), c.CreationDate.GetType());
collectionsTable.Columns.Add(creationDateColumn);
var revisionDateColumn = new DataColumn(nameof(c.RevisionDate), c.RevisionDate.GetType());
collectionsTable.Columns.Add(revisionDateColumn);
foreach (DataColumn col in collectionsTable.Columns)
{
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
}
var keys = new DataColumn[1];
keys[0] = idColumn;
collectionsTable.PrimaryKey = keys;
foreach (var collection in collections)
{
var row = collectionsTable.NewRow();
row[idColumn] = collection.Id;
row[organizationIdColumn] = collection.OrganizationId;
row[nameColumn] = collection.Name;
row[creationDateColumn] = collection.CreationDate;
row[revisionDateColumn] = collection.RevisionDate;
collectionsTable.Rows.Add(row);
}
return collectionsTable;
}
private DataTable BuildCollectionCiphersTable(SqlBulkCopy bulkCopy, IEnumerable<CollectionCipher> collectionCiphers)
{
var cc = collectionCiphers.FirstOrDefault();
if (cc == null)
{
throw new ApplicationException("Must have some collectionCiphers to bulk import.");
}
var collectionCiphersTable = new DataTable("CollectionCipherDataTable");
var collectionIdColumn = new DataColumn(nameof(cc.CollectionId), cc.CollectionId.GetType());
collectionCiphersTable.Columns.Add(collectionIdColumn);
var cipherIdColumn = new DataColumn(nameof(cc.CipherId), cc.CipherId.GetType());
collectionCiphersTable.Columns.Add(cipherIdColumn);
foreach (DataColumn col in collectionCiphersTable.Columns)
{
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
}
var keys = new DataColumn[2];
keys[0] = collectionIdColumn;
keys[1] = cipherIdColumn;
collectionCiphersTable.PrimaryKey = keys;
foreach (var collectionCipher in collectionCiphers)
{
var row = collectionCiphersTable.NewRow();
row[collectionIdColumn] = collectionCipher.CollectionId;
row[cipherIdColumn] = collectionCipher.CipherId;
collectionCiphersTable.Rows.Add(row);
}
return collectionCiphersTable;
}
private DataTable BuildSendsTable(SqlBulkCopy bulkCopy, IEnumerable<Send> sends)
{
var s = sends.FirstOrDefault();
if (s == null)
{
throw new ApplicationException("Must have some Sends to bulk import.");
}
var sendsTable = new DataTable("SendsDataTable");
var idColumn = new DataColumn(nameof(s.Id), s.Id.GetType());
sendsTable.Columns.Add(idColumn);
var userIdColumn = new DataColumn(nameof(s.UserId), typeof(Guid));
sendsTable.Columns.Add(userIdColumn);
var organizationIdColumn = new DataColumn(nameof(s.OrganizationId), typeof(Guid));
sendsTable.Columns.Add(organizationIdColumn);
var typeColumn = new DataColumn(nameof(s.Type), s.Type.GetType());
sendsTable.Columns.Add(typeColumn);
var dataColumn = new DataColumn(nameof(s.Data), s.Data.GetType());
sendsTable.Columns.Add(dataColumn);
var keyColumn = new DataColumn(nameof(s.Key), s.Key.GetType());
sendsTable.Columns.Add(keyColumn);
var passwordColumn = new DataColumn(nameof(s.Password), typeof(string));
sendsTable.Columns.Add(passwordColumn);
var maxAccessCountColumn = new DataColumn(nameof(s.MaxAccessCount), typeof(int));
sendsTable.Columns.Add(maxAccessCountColumn);
var accessCountColumn = new DataColumn(nameof(s.AccessCount), s.AccessCount.GetType());
sendsTable.Columns.Add(accessCountColumn);
var creationDateColumn = new DataColumn(nameof(s.CreationDate), s.CreationDate.GetType());
sendsTable.Columns.Add(creationDateColumn);
var revisionDateColumn = new DataColumn(nameof(s.RevisionDate), s.RevisionDate.GetType());
sendsTable.Columns.Add(revisionDateColumn);
var expirationDateColumn = new DataColumn(nameof(s.ExpirationDate), typeof(DateTime));
sendsTable.Columns.Add(expirationDateColumn);
var deletionDateColumn = new DataColumn(nameof(s.DeletionDate), s.DeletionDate.GetType());
sendsTable.Columns.Add(deletionDateColumn);
var disabledColumn = new DataColumn(nameof(s.Disabled), s.Disabled.GetType());
sendsTable.Columns.Add(disabledColumn);
var hideEmailColumn = new DataColumn(nameof(s.HideEmail), typeof(bool));
sendsTable.Columns.Add(hideEmailColumn);
foreach (DataColumn col in sendsTable.Columns)
{
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
}
var keys = new DataColumn[1];
keys[0] = idColumn;
sendsTable.PrimaryKey = keys;
foreach (var send in sends)
{
var row = sendsTable.NewRow();
row[idColumn] = send.Id;
row[userIdColumn] = send.UserId.HasValue ? (object)send.UserId.Value : DBNull.Value;
row[organizationIdColumn] = send.OrganizationId.HasValue ? (object)send.OrganizationId.Value : DBNull.Value;
row[typeColumn] = (short)send.Type;
row[dataColumn] = send.Data;
row[keyColumn] = send.Key;
row[passwordColumn] = send.Password;
row[maxAccessCountColumn] = send.MaxAccessCount.HasValue ? (object)send.MaxAccessCount : DBNull.Value;
row[accessCountColumn] = send.AccessCount;
row[creationDateColumn] = send.CreationDate;
row[revisionDateColumn] = send.RevisionDate;
row[expirationDateColumn] = send.ExpirationDate.HasValue ? (object)send.ExpirationDate : DBNull.Value;
row[deletionDateColumn] = send.DeletionDate;
row[disabledColumn] = send.Disabled;
row[hideEmailColumn] = send.HideEmail.HasValue ? (object)send.HideEmail : DBNull.Value;
sendsTable.Rows.Add(row);
}
return sendsTable;
}
public class CipherDetailsWithCollections : CipherDetails
{
public DataTable CollectionIds { get; set; }
}
public class CipherWithCollections : Cipher
{
public DataTable CollectionIds { get; set; }
}
}
}

View File

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class CollectionCipherRepository : BaseRepository, ICollectionCipherRepository
{
public CollectionCipherRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public CollectionCipherRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<ICollection<CollectionCipher>> GetManyByUserIdAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<CollectionCipher>(
"[dbo].[CollectionCipher_ReadByUserId]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<CollectionCipher>> GetManyByOrganizationIdAsync(Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<CollectionCipher>(
"[dbo].[CollectionCipher_ReadByOrganizationId]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<CollectionCipher>> GetManyByUserIdCipherIdAsync(Guid userId, Guid cipherId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<CollectionCipher>(
"[dbo].[CollectionCipher_ReadByUserIdCipherId]",
new { UserId = userId, CipherId = cipherId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task UpdateCollectionsAsync(Guid cipherId, Guid userId, IEnumerable<Guid> collectionIds)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
"[dbo].[CollectionCipher_UpdateCollections]",
new { CipherId = cipherId, UserId = userId, CollectionIds = collectionIds.ToGuidIdArrayTVP() },
commandType: CommandType.StoredProcedure);
}
}
public async Task UpdateCollectionsForAdminAsync(Guid cipherId, Guid organizationId, IEnumerable<Guid> collectionIds)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
"[dbo].[CollectionCipher_UpdateCollectionsAdmin]",
new { CipherId = cipherId, OrganizationId = organizationId, CollectionIds = collectionIds.ToGuidIdArrayTVP() },
commandType: CommandType.StoredProcedure);
}
}
public async Task UpdateCollectionsForCiphersAsync(IEnumerable<Guid> cipherIds, Guid userId,
Guid organizationId, IEnumerable<Guid> collectionIds)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
"[dbo].[CollectionCipher_UpdateCollectionsForCiphers]",
new
{
CipherIds = cipherIds.ToGuidIdArrayTVP(),
UserId = userId,
OrganizationId = organizationId,
CollectionIds = collectionIds.ToGuidIdArrayTVP()
},
commandType: CommandType.StoredProcedure);
}
}
}
}

View File

@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Dapper;
using Newtonsoft.Json;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class CollectionRepository : Repository<Collection, Guid>, ICollectionRepository
{
public CollectionRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public CollectionRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<int> GetCountByOrganizationIdAsync(Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteScalarAsync<int>(
"[dbo].[Collection_ReadCountByOrganizationId]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results;
}
}
public async Task<Tuple<Collection, ICollection<SelectionReadOnly>>> 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 groups = (await results.ReadAsync<SelectionReadOnly>()).ToList();
return new Tuple<Collection, ICollection<SelectionReadOnly>>(collection, groups);
}
}
public async Task<Tuple<CollectionDetails, ICollection<SelectionReadOnly>>> GetByIdWithGroupsAsync(
Guid id, Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryMultipleAsync(
$"[{Schema}].[Collection_ReadWithGroupsByIdUserId]",
new { Id = id, UserId = userId },
commandType: CommandType.StoredProcedure);
var collection = await results.ReadFirstOrDefaultAsync<CollectionDetails>();
var groups = (await results.ReadAsync<SelectionReadOnly>()).ToList();
return new Tuple<CollectionDetails, ICollection<SelectionReadOnly>>(collection, groups);
}
}
public async Task<ICollection<Collection>> GetManyByOrganizationIdAsync(Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Collection>(
$"[{Schema}].[{Table}_ReadByOrganizationId]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<CollectionDetails> GetByIdAsync(Guid id, Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<CollectionDetails>(
$"[{Schema}].[Collection_ReadByIdUserId]",
new { Id = id, UserId = userId },
commandType: CommandType.StoredProcedure);
return results.FirstOrDefault();
}
}
public async Task<ICollection<CollectionDetails>> GetManyByUserIdAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<CollectionDetails>(
$"[{Schema}].[Collection_ReadByUserId]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task CreateAsync(Collection obj, IEnumerable<SelectionReadOnly> groups)
{
obj.SetNewId();
var objWithGroups = JsonConvert.DeserializeObject<CollectionWithGroups>(JsonConvert.SerializeObject(obj));
objWithGroups.Groups = groups.ToArrayTVP();
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<SelectionReadOnly> groups)
{
var objWithGroups = JsonConvert.DeserializeObject<CollectionWithGroups>(JsonConvert.SerializeObject(obj));
objWithGroups.Groups = groups.ToArrayTVP();
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Collection_UpdateWithGroups]",
objWithGroups,
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<SelectionReadOnly> users)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[CollectionUser_UpdateUsers]",
new { CollectionId = id, Users = users.ToArrayTVP() },
commandType: CommandType.StoredProcedure);
}
}
public async Task<ICollection<SelectionReadOnly>> GetManyUsersByIdAsync(Guid id)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<SelectionReadOnly>(
$"[{Schema}].[CollectionUser_ReadByCollectionId]",
new { CollectionId = id },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public class CollectionWithGroups : Collection
{
public DataTable Groups { get; set; }
}
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Data;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class DateTimeHandler : SqlMapper.TypeHandler<DateTime>
{
public override void SetValue(IDbDataParameter parameter, DateTime value)
{
parameter.Value = value;
}
public override DateTime Parse(object value)
{
return DateTime.SpecifyKind((DateTime)value, DateTimeKind.Utc);
}
}
}

View File

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class DeviceRepository : Repository<Device, Guid>, IDeviceRepository
{
public DeviceRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public DeviceRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<Device> GetByIdAsync(Guid id, Guid userId)
{
var device = await GetByIdAsync(id);
if (device == null || device.UserId != userId)
{
return null;
}
return device;
}
public async Task<Device> GetByIdentifierAsync(string identifier)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Device>(
$"[{Schema}].[{Table}_ReadByIdentifier]",
new
{
Identifier = identifier
},
commandType: CommandType.StoredProcedure);
return results.FirstOrDefault();
}
}
public async Task<Device> GetByIdentifierAsync(string identifier, Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Device>(
$"[{Schema}].[{Table}_ReadByIdentifierUserId]",
new
{
UserId = userId,
Identifier = identifier
},
commandType: CommandType.StoredProcedure);
return results.FirstOrDefault();
}
}
public async Task<ICollection<Device>> GetManyByUserIdAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Device>(
$"[{Schema}].[{Table}_ReadByUserId]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task ClearPushTokenAsync(Guid id)
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(
$"[{Schema}].[{Table}_ClearPushTokenById]",
new { Id = id },
commandType: CommandType.StoredProcedure);
}
}
}
}

View File

@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class EmergencyAccessRepository : Repository<EmergencyAccess, Guid>, IEmergencyAccessRepository
{
public EmergencyAccessRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public EmergencyAccessRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<int> GetCountByGrantorIdEmailAsync(Guid grantorId, string email, bool onlyRegisteredUsers)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteScalarAsync<int>(
"[dbo].[EmergencyAccess_ReadCountByGrantorIdEmail]",
new { GrantorId = grantorId, Email = email, OnlyUsers = onlyRegisteredUsers },
commandType: CommandType.StoredProcedure);
return results;
}
}
public async Task<ICollection<EmergencyAccessDetails>> GetManyDetailsByGrantorIdAsync(Guid grantorId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<EmergencyAccessDetails>(
"[dbo].[EmergencyAccessDetails_ReadByGrantorId]",
new { GrantorId = grantorId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<EmergencyAccessDetails>> GetManyDetailsByGranteeIdAsync(Guid granteeId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<EmergencyAccessDetails>(
"[dbo].[EmergencyAccessDetails_ReadByGranteeId]",
new { GranteeId = granteeId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<EmergencyAccessDetails> GetDetailsByIdGrantorIdAsync(Guid id, Guid grantorId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<EmergencyAccessDetails>(
"[dbo].[EmergencyAccessDetails_ReadByIdGrantorId]",
new { Id = id, GrantorId = grantorId },
commandType: CommandType.StoredProcedure);
return results.FirstOrDefault();
}
}
public async Task<ICollection<EmergencyAccessNotify>> GetManyToNotifyAsync()
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<EmergencyAccessNotify>(
"[dbo].[EmergencyAccess_ReadToNotify]",
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<EmergencyAccessDetails>> GetExpiredRecoveriesAsync()
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<EmergencyAccessDetails>(
"[dbo].[EmergencyAccessDetails_ReadExpiredRecoveries]",
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
}
}

View File

@ -0,0 +1,230 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class EventRepository : Repository<Event, Guid>, IEventRepository
{
public EventRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public EventRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<PagedResult<IEvent>> GetManyByUserAsync(Guid userId, DateTime startDate, DateTime endDate,
PageOptions pageOptions)
{
return await GetManyAsync($"[{Schema}].[Event_ReadPageByUserId]",
new Dictionary<string, object>
{
["@UserId"] = userId
}, startDate, endDate, pageOptions);
}
public async Task<PagedResult<IEvent>> GetManyByOrganizationAsync(Guid organizationId,
DateTime startDate, DateTime endDate, PageOptions pageOptions)
{
return await GetManyAsync($"[{Schema}].[Event_ReadPageByOrganizationId]",
new Dictionary<string, object>
{
["@OrganizationId"] = organizationId
}, startDate, endDate, pageOptions);
}
public async Task<PagedResult<IEvent>> GetManyByOrganizationActingUserAsync(Guid organizationId, Guid actingUserId,
DateTime startDate, DateTime endDate, PageOptions pageOptions)
{
return await GetManyAsync($"[{Schema}].[Event_ReadPageByOrganizationIdActingUserId]",
new Dictionary<string, object>
{
["@OrganizationId"] = organizationId,
["@ActingUserId"] = actingUserId
}, startDate, endDate, pageOptions);
}
public async Task<PagedResult<IEvent>> GetManyByProviderAsync(Guid providerId,
DateTime startDate, DateTime endDate, PageOptions pageOptions)
{
return await GetManyAsync($"[{Schema}].[Event_ReadPageByProviderId]",
new Dictionary<string, object>
{
["@ProviderId"] = providerId
}, startDate, endDate, pageOptions);
}
public async Task<PagedResult<IEvent>> GetManyByProviderActingUserAsync(Guid providerId, Guid actingUserId,
DateTime startDate, DateTime endDate, PageOptions pageOptions)
{
return await GetManyAsync($"[{Schema}].[Event_ReadPageByProviderIdActingUserId]",
new Dictionary<string, object>
{
["@ProviderId"] = providerId,
["@ActingUserId"] = actingUserId
}, startDate, endDate, pageOptions);
}
public async Task<PagedResult<IEvent>> GetManyByCipherAsync(Cipher cipher, DateTime startDate, DateTime endDate,
PageOptions pageOptions)
{
return await GetManyAsync($"[{Schema}].[Event_ReadPageByCipherId]",
new Dictionary<string, object>
{
["@OrganizationId"] = cipher.OrganizationId,
["@UserId"] = cipher.UserId,
["@CipherId"] = cipher.Id
}, startDate, endDate, pageOptions);
}
public async Task CreateAsync(IEvent e)
{
if (!(e is Event ev))
{
ev = new Event(e);
}
await base.CreateAsync(ev);
}
public async Task CreateManyAsync(IEnumerable<IEvent> entities)
{
if (!entities?.Any() ?? true)
{
return;
}
if (!entities.Skip(1).Any())
{
await CreateAsync(entities.First());
return;
}
using (var connection = new SqlConnection(ConnectionString))
{
connection.Open();
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, null))
{
bulkCopy.DestinationTableName = "[dbo].[Event]";
var dataTable = BuildEventsTable(bulkCopy, entities.Select(e => e is Event ? e as Event : new Event(e)));
await bulkCopy.WriteToServerAsync(dataTable);
}
}
}
private async Task<PagedResult<IEvent>> GetManyAsync(string sprocName,
IDictionary<string, object> sprocParams, DateTime startDate, DateTime endDate, PageOptions pageOptions)
{
DateTime? beforeDate = null;
if (!string.IsNullOrWhiteSpace(pageOptions.ContinuationToken) &&
long.TryParse(pageOptions.ContinuationToken, out var binaryDate))
{
beforeDate = DateTime.SpecifyKind(DateTime.FromBinary(binaryDate), DateTimeKind.Utc);
}
var parameters = new DynamicParameters(sprocParams);
parameters.Add("@PageSize", pageOptions.PageSize, DbType.Int32);
// Explicitly use DbType.DateTime2 for proper precision.
// ref: https://github.com/StackExchange/Dapper/issues/229
parameters.Add("@StartDate", startDate.ToUniversalTime(), DbType.DateTime2, null, 7);
parameters.Add("@EndDate", endDate.ToUniversalTime(), DbType.DateTime2, null, 7);
parameters.Add("@BeforeDate", beforeDate, DbType.DateTime2, null, 7);
using (var connection = new SqlConnection(ConnectionString))
{
var events = (await connection.QueryAsync<Event>(sprocName, parameters,
commandType: CommandType.StoredProcedure)).ToList();
var result = new PagedResult<IEvent>();
if (events.Any() && events.Count >= pageOptions.PageSize)
{
result.ContinuationToken = events.Last().Date.ToBinary().ToString();
}
result.Data.AddRange(events);
return result;
}
}
private DataTable BuildEventsTable(SqlBulkCopy bulkCopy, IEnumerable<Event> events)
{
var e = events.FirstOrDefault();
if (e == null)
{
throw new ApplicationException("Must have some events to bulk import.");
}
var eventsTable = new DataTable("EventDataTable");
var idColumn = new DataColumn(nameof(e.Id), e.Id.GetType());
eventsTable.Columns.Add(idColumn);
var typeColumn = new DataColumn(nameof(e.Type), typeof(int));
eventsTable.Columns.Add(typeColumn);
var userIdColumn = new DataColumn(nameof(e.UserId), typeof(Guid));
eventsTable.Columns.Add(userIdColumn);
var organizationIdColumn = new DataColumn(nameof(e.OrganizationId), typeof(Guid));
eventsTable.Columns.Add(organizationIdColumn);
var cipherIdColumn = new DataColumn(nameof(e.CipherId), typeof(Guid));
eventsTable.Columns.Add(cipherIdColumn);
var collectionIdColumn = new DataColumn(nameof(e.CollectionId), typeof(Guid));
eventsTable.Columns.Add(collectionIdColumn);
var policyIdColumn = new DataColumn(nameof(e.PolicyId), typeof(Guid));
eventsTable.Columns.Add(policyIdColumn);
var groupIdColumn = new DataColumn(nameof(e.GroupId), typeof(Guid));
eventsTable.Columns.Add(groupIdColumn);
var organizationUserIdColumn = new DataColumn(nameof(e.OrganizationUserId), typeof(Guid));
eventsTable.Columns.Add(organizationUserIdColumn);
var actingUserIdColumn = new DataColumn(nameof(e.ActingUserId), typeof(Guid));
eventsTable.Columns.Add(actingUserIdColumn);
var deviceTypeColumn = new DataColumn(nameof(e.DeviceType), typeof(int));
eventsTable.Columns.Add(deviceTypeColumn);
var ipAddressColumn = new DataColumn(nameof(e.IpAddress), typeof(string));
eventsTable.Columns.Add(ipAddressColumn);
var dateColumn = new DataColumn(nameof(e.Date), typeof(DateTime));
eventsTable.Columns.Add(dateColumn);
foreach (DataColumn col in eventsTable.Columns)
{
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
}
var keys = new DataColumn[1];
keys[0] = idColumn;
eventsTable.PrimaryKey = keys;
foreach (var ev in events)
{
ev.SetNewId();
var row = eventsTable.NewRow();
row[idColumn] = ev.Id;
row[typeColumn] = (int)ev.Type;
row[userIdColumn] = ev.UserId.HasValue ? (object)ev.UserId.Value : DBNull.Value;
row[organizationIdColumn] = ev.OrganizationId.HasValue ? (object)ev.OrganizationId.Value : DBNull.Value;
row[cipherIdColumn] = ev.CipherId.HasValue ? (object)ev.CipherId.Value : DBNull.Value;
row[collectionIdColumn] = ev.CollectionId.HasValue ? (object)ev.CollectionId.Value : DBNull.Value;
row[policyIdColumn] = ev.PolicyId.HasValue ? (object)ev.PolicyId.Value : DBNull.Value;
row[groupIdColumn] = ev.GroupId.HasValue ? (object)ev.GroupId.Value : DBNull.Value;
row[organizationUserIdColumn] = ev.OrganizationUserId.HasValue ?
(object)ev.OrganizationUserId.Value : DBNull.Value;
row[actingUserIdColumn] = ev.ActingUserId.HasValue ? (object)ev.ActingUserId.Value : DBNull.Value;
row[deviceTypeColumn] = ev.DeviceType.HasValue ? (object)ev.DeviceType.Value : DBNull.Value;
row[ipAddressColumn] = ev.IpAddress != null ? (object)ev.IpAddress : DBNull.Value;
row[dateColumn] = ev.Date;
eventsTable.Rows.Add(row);
}
return eventsTable;
}
}
}

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class FolderRepository : Repository<Folder, Guid>, IFolderRepository
{
public FolderRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public FolderRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<Folder> GetByIdAsync(Guid id, Guid userId)
{
var folder = await GetByIdAsync(id);
if (folder == null || folder.UserId != userId)
{
return null;
}
return folder;
}
public async Task<ICollection<Folder>> GetManyByUserIdAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Folder>(
$"[{Schema}].[Folder_ReadByUserId]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
}
}

View File

@ -0,0 +1,83 @@
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class GrantRepository : BaseRepository, IGrantRepository
{
public GrantRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public GrantRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<Grant> GetByKeyAsync(string key)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Grant>(
"[dbo].[Grant_ReadByKey]",
new { Key = key },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task<ICollection<Grant>> GetManyAsync(string subjectId, string sessionId,
string clientId, string type)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Grant>(
"[dbo].[Grant_Read]",
new { SubjectId = subjectId, SessionId = sessionId, ClientId = clientId, Type = type },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task SaveAsync(Grant obj)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
"[dbo].[Grant_Save]",
obj,
commandType: CommandType.StoredProcedure);
}
}
public async Task DeleteByKeyAsync(string key)
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(
"[dbo].[Grant_DeleteByKey]",
new { Key = key },
commandType: CommandType.StoredProcedure);
}
}
public async Task DeleteManyAsync(string subjectId, string sessionId, string clientId, string type)
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(
"[dbo].[Grant_Delete]",
new { SubjectId = subjectId, SessionId = sessionId, ClientId = clientId, Type = type },
commandType: CommandType.StoredProcedure);
}
}
}
}

View File

@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Dapper;
using Newtonsoft.Json;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class GroupRepository : Repository<Group, Guid>, IGroupRepository
{
public GroupRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public GroupRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<Tuple<Group, ICollection<SelectionReadOnly>>> GetByIdWithCollectionsAsync(Guid id)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryMultipleAsync(
$"[{Schema}].[Group_ReadWithCollectionsById]",
new { Id = id },
commandType: CommandType.StoredProcedure);
var group = await results.ReadFirstOrDefaultAsync<Group>();
var colletions = (await results.ReadAsync<SelectionReadOnly>()).ToList();
return new Tuple<Group, ICollection<SelectionReadOnly>>(group, colletions);
}
}
public async Task<ICollection<Group>> GetManyByOrganizationIdAsync(Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Group>(
$"[{Schema}].[Group_ReadByOrganizationId]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<Guid>> GetManyIdsByUserIdAsync(Guid organizationUserId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Guid>(
$"[{Schema}].[GroupUser_ReadGroupIdsByOrganizationUserId]",
new { OrganizationUserId = organizationUserId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<Guid>> GetManyUserIdsByIdAsync(Guid id)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Guid>(
$"[{Schema}].[GroupUser_ReadOrganizationUserIdsByGroupId]",
new { GroupId = id },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<GroupUser>> GetManyGroupUsersByOrganizationIdAsync(Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<GroupUser>(
$"[{Schema}].[GroupUser_ReadByOrganizationId]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task CreateAsync(Group obj, IEnumerable<SelectionReadOnly> collections)
{
obj.SetNewId();
var objWithCollections = JsonConvert.DeserializeObject<GroupWithCollections>(JsonConvert.SerializeObject(obj));
objWithCollections.Collections = collections.ToArrayTVP();
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Group_CreateWithCollections]",
objWithCollections,
commandType: CommandType.StoredProcedure);
}
}
public async Task ReplaceAsync(Group obj, IEnumerable<SelectionReadOnly> collections)
{
var objWithCollections = JsonConvert.DeserializeObject<GroupWithCollections>(JsonConvert.SerializeObject(obj));
objWithCollections.Collections = collections.ToArrayTVP();
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[Group_UpdateWithCollections]",
objWithCollections,
commandType: CommandType.StoredProcedure);
}
}
public async Task DeleteUserAsync(Guid groupId, Guid organizationUserId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[GroupUser_Delete]",
new { GroupId = groupId, OrganizationUserId = organizationUserId },
commandType: CommandType.StoredProcedure);
}
}
public async Task UpdateUsersAsync(Guid groupId, IEnumerable<Guid> organizationUserIds)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
"[dbo].[GroupUser_UpdateUsers]",
new { GroupId = groupId, OrganizationUserIds = organizationUserIds.ToGuidIdArrayTVP() },
commandType: CommandType.StoredProcedure);
}
}
}
}

View File

@ -0,0 +1,18 @@
using System;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.Settings;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class InstallationRepository : Repository<Installation, Guid>, IInstallationRepository
{
public InstallationRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public InstallationRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
}
}

View File

@ -0,0 +1,66 @@
using System.Data;
using System.Data.SqlClient;
using System.Threading.Tasks;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class MaintenanceRepository : BaseRepository, IMaintenanceRepository
{
public MaintenanceRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public MaintenanceRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task UpdateStatisticsAsync()
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(
"[dbo].[AzureSQLMaintenance]",
new { operation = "statistics", mode = "smart", LogToTable = true },
commandType: CommandType.StoredProcedure,
commandTimeout: 172800);
}
}
public async Task DisableCipherAutoStatsAsync()
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(
"sp_autostats",
new { tblname = "[dbo].[Cipher]", flagc = "OFF" },
commandType: CommandType.StoredProcedure);
}
}
public async Task RebuildIndexesAsync()
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(
"[dbo].[AzureSQLMaintenance]",
new { operation = "index", mode = "smart", LogToTable = true },
commandType: CommandType.StoredProcedure,
commandTimeout: 172800);
}
}
public async Task DeleteExpiredGrantsAsync()
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(
"[dbo].[Grant_DeleteExpired]",
commandType: CommandType.StoredProcedure,
commandTimeout: 172800);
}
}
}
}

View File

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class OrganizationRepository : Repository<Organization, Guid>, IOrganizationRepository
{
public OrganizationRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public OrganizationRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<Organization> GetByIdentifierAsync(string identifier)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Organization>(
"[dbo].[Organization_ReadByIdentifier]",
new { Identifier = identifier },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task<ICollection<Organization>> GetManyByEnabledAsync()
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Organization>(
"[dbo].[Organization_ReadByEnabled]",
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<Organization>> GetManyByUserIdAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Organization>(
"[dbo].[Organization_ReadByUserId]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<Organization>> SearchAsync(string name, string userEmail, bool? paid,
int skip, int take)
{
using (var connection = new SqlConnection(ReadOnlyConnectionString))
{
var results = await connection.QueryAsync<Organization>(
"[dbo].[Organization_Search]",
new { Name = name, UserEmail = userEmail, Paid = paid, Skip = skip, Take = take },
commandType: CommandType.StoredProcedure,
commandTimeout: 120);
return results.ToList();
}
}
public async Task UpdateStorageAsync(Guid id)
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(
"[dbo].[Organization_UpdateStorage]",
new { Id = id },
commandType: CommandType.StoredProcedure,
commandTimeout: 180);
}
}
public async Task<ICollection<OrganizationAbility>> GetManyAbilitiesAsync()
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationAbility>(
"[dbo].[Organization_ReadAbilities]",
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
}
}

View File

@ -0,0 +1,68 @@
using System;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class OrganizationSponsorshipRepository : Repository<OrganizationSponsorship, Guid>, IOrganizationSponsorshipRepository
{
public OrganizationSponsorshipRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public OrganizationSponsorshipRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<OrganizationSponsorship> GetBySponsoringOrganizationUserIdAsync(Guid sponsoringOrganizationUserId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationSponsorship>(
"[dbo].[OrganizationSponsorship_ReadBySponsoringOrganizationUserId]",
new
{
SponsoringOrganizationUserId = sponsoringOrganizationUserId
},
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task<OrganizationSponsorship> GetBySponsoredOrganizationIdAsync(Guid sponsoredOrganizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationSponsorship>(
"[dbo].[OrganizationSponsorship_ReadBySponsoredOrganizationId]",
new { SponsoredOrganizationId = sponsoredOrganizationId },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task<OrganizationSponsorship> GetByOfferedToEmailAsync(string offeredToEmail)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationSponsorship>(
"[dbo].[OrganizationSponsorship_ReadByOfferedToEmail]",
new
{
OfferedToEmail = offeredToEmail
},
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
}
}

View File

@ -0,0 +1,413 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Dapper;
using Newtonsoft.Json;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class OrganizationUserRepository : Repository<OrganizationUser, Guid>, IOrganizationUserRepository
{
/// <summary>
/// For use with methods with TDS stream issues.
/// This has been observed in Linux-hosted SqlServers with large table-valued-parameters
/// https://github.com/dotnet/SqlClient/issues/54
/// </summary>
private string _marsConnectionString;
public OrganizationUserRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{
var builder = new SqlConnectionStringBuilder(ConnectionString)
{
MultipleActiveResultSets = true,
};
_marsConnectionString = builder.ToString();
}
public OrganizationUserRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<int> GetCountByOrganizationIdAsync(Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteScalarAsync<int>(
"[dbo].[OrganizationUser_ReadCountByOrganizationId]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results;
}
}
public async Task<int> GetCountByFreeOrganizationAdminUserAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteScalarAsync<int>(
"[dbo].[OrganizationUser_ReadCountByFreeOrganizationAdminUser]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results;
}
}
public async Task<int> GetCountByOnlyOwnerAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteScalarAsync<int>(
"[dbo].[OrganizationUser_ReadCountByOnlyOwner]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results;
}
}
public async Task<int> GetCountByOrganizationAsync(Guid organizationId, string email, bool onlyRegisteredUsers)
{
using (var connection = new SqlConnection(ConnectionString))
{
var result = await connection.ExecuteScalarAsync<int>(
"[dbo].[OrganizationUser_ReadCountByOrganizationIdEmail]",
new { OrganizationId = organizationId, Email = email, OnlyUsers = onlyRegisteredUsers },
commandType: CommandType.StoredProcedure);
return result;
}
}
public async Task<ICollection<string>> SelectKnownEmailsAsync(Guid organizationId, IEnumerable<string> emails,
bool onlyRegisteredUsers)
{
var emailsTvp = emails.ToArrayTVP("Email");
using (var connection = new SqlConnection(_marsConnectionString))
{
var result = await connection.QueryAsync<string>(
"[dbo].[OrganizationUser_SelectKnownEmails]",
new { OrganizationId = organizationId, Emails = emailsTvp, OnlyUsers = onlyRegisteredUsers },
commandType: CommandType.StoredProcedure);
// Return as a list to avoid timing out the sql connection
return result.ToList();
}
}
public async Task<OrganizationUser> GetByOrganizationAsync(Guid organizationId, Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationUser>(
"[dbo].[OrganizationUser_ReadByOrganizationIdUserId]",
new { OrganizationId = organizationId, UserId = userId },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task<ICollection<OrganizationUser>> GetManyByUserAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationUser>(
"[dbo].[OrganizationUser_ReadByUserId]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<OrganizationUser>> GetManyByOrganizationAsync(Guid organizationId,
OrganizationUserType? type)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationUser>(
"[dbo].[OrganizationUser_ReadByOrganizationId]",
new { OrganizationId = organizationId, Type = type },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<Tuple<OrganizationUser, ICollection<SelectionReadOnly>>> GetByIdWithCollectionsAsync(Guid id)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryMultipleAsync(
"[dbo].[OrganizationUser_ReadWithCollectionsById]",
new { Id = id },
commandType: CommandType.StoredProcedure);
var user = (await results.ReadAsync<OrganizationUser>()).SingleOrDefault();
var collections = (await results.ReadAsync<SelectionReadOnly>()).ToList();
return new Tuple<OrganizationUser, ICollection<SelectionReadOnly>>(user, collections);
}
}
public async Task<OrganizationUserUserDetails> GetDetailsByIdAsync(Guid id)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationUserUserDetails>(
"[dbo].[OrganizationUserUserDetails_ReadById]",
new { Id = id },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task<Tuple<OrganizationUserUserDetails, ICollection<SelectionReadOnly>>>
GetDetailsByIdWithCollectionsAsync(Guid id)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryMultipleAsync(
"[dbo].[OrganizationUserUserDetails_ReadWithCollectionsById]",
new { Id = id },
commandType: CommandType.StoredProcedure);
var user = (await results.ReadAsync<OrganizationUserUserDetails>()).SingleOrDefault();
var collections = (await results.ReadAsync<SelectionReadOnly>()).ToList();
return new Tuple<OrganizationUserUserDetails, ICollection<SelectionReadOnly>>(user, collections);
}
}
public async Task<ICollection<OrganizationUserUserDetails>> GetManyDetailsByOrganizationAsync(Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationUserUserDetails>(
"[dbo].[OrganizationUserUserDetails_ReadByOrganizationId]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<OrganizationUserOrganizationDetails>> GetManyDetailsByUserAsync(Guid userId,
OrganizationUserStatusType? status = null)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationUserOrganizationDetails>(
"[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatus]",
new { UserId = userId, Status = status },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<OrganizationUserOrganizationDetails> GetDetailsByUserAsync(Guid userId,
Guid organizationId, OrganizationUserStatusType? status = null)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationUserOrganizationDetails>(
"[dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId]",
new { UserId = userId, Status = status, OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task UpdateGroupsAsync(Guid orgUserId, IEnumerable<Guid> groupIds)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
"[dbo].[GroupUser_UpdateGroups]",
new { OrganizationUserId = orgUserId, GroupIds = groupIds.ToGuidIdArrayTVP() },
commandType: CommandType.StoredProcedure);
}
}
public async Task<Guid> CreateAsync(OrganizationUser obj, IEnumerable<SelectionReadOnly> collections)
{
obj.SetNewId();
var objWithCollections = JsonConvert.DeserializeObject<OrganizationUserWithCollections>(
JsonConvert.SerializeObject(obj));
objWithCollections.Collections = collections.ToArrayTVP();
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[OrganizationUser_CreateWithCollections]",
objWithCollections,
commandType: CommandType.StoredProcedure);
}
return obj.Id;
}
public async Task ReplaceAsync(OrganizationUser obj, IEnumerable<SelectionReadOnly> collections)
{
var objWithCollections = JsonConvert.DeserializeObject<OrganizationUserWithCollections>(
JsonConvert.SerializeObject(obj));
objWithCollections.Collections = collections.ToArrayTVP();
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[OrganizationUser_UpdateWithCollections]",
objWithCollections,
commandType: CommandType.StoredProcedure);
}
}
public async Task<ICollection<OrganizationUser>> GetManyByManyUsersAsync(IEnumerable<Guid> userIds)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationUser>(
"[dbo].[OrganizationUser_ReadByUserIds]",
new { UserIds = userIds.ToGuidIdArrayTVP() },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<OrganizationUser>> GetManyAsync(IEnumerable<Guid> Ids)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationUser>(
"[dbo].[OrganizationUser_ReadByIds]",
new { Ids = Ids.ToGuidIdArrayTVP() },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<OrganizationUser> GetByOrganizationEmailAsync(Guid organizationId, string email)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationUser>(
"[dbo].[OrganizationUser_ReadByOrganizationIdEmail]",
new { OrganizationId = organizationId, Email = email },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task DeleteManyAsync(IEnumerable<Guid> organizationUserIds)
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync("[dbo].[OrganizationUser_DeleteByIds]",
new { Ids = organizationUserIds.ToGuidIdArrayTVP() }, commandType: CommandType.StoredProcedure);
}
}
public async Task UpsertManyAsync(IEnumerable<OrganizationUser> organizationUsers)
{
var createUsers = new List<OrganizationUser>();
var replaceUsers = new List<OrganizationUser>();
foreach (var organizationUser in organizationUsers)
{
if (organizationUser.Id.Equals(default))
{
createUsers.Add(organizationUser);
}
else
{
replaceUsers.Add(organizationUser);
}
}
await CreateManyAsync(createUsers);
await ReplaceManyAsync(replaceUsers);
}
public async Task<ICollection<Guid>> CreateManyAsync(IEnumerable<OrganizationUser> organizationUsers)
{
if (!organizationUsers.Any())
{
return default;
}
foreach (var organizationUser in organizationUsers)
{
organizationUser.SetNewId();
}
var orgUsersTVP = organizationUsers.ToTvp();
using (var connection = new SqlConnection(_marsConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[{Table}_CreateMany]",
new { OrganizationUsersInput = orgUsersTVP },
commandType: CommandType.StoredProcedure);
}
return organizationUsers.Select(u => u.Id).ToList();
}
public async Task ReplaceManyAsync(IEnumerable<OrganizationUser> organizationUsers)
{
if (!organizationUsers.Any())
{
return;
}
var orgUsersTVP = organizationUsers.ToTvp();
using (var connection = new SqlConnection(_marsConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[{Table}_UpdateMany]",
new { OrganizationUsersInput = orgUsersTVP },
commandType: CommandType.StoredProcedure);
}
}
public async Task<IEnumerable<OrganizationUserPublicKey>> GetManyPublicKeysByOrganizationUserAsync(
Guid organizationId, IEnumerable<Guid> Ids)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationUserPublicKey>(
"[dbo].[User_ReadPublicKeysByOrganizationUserIds]",
new { OrganizationId = organizationId, OrganizationUserIds = Ids.ToGuidIdArrayTVP() },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<IEnumerable<OrganizationUserUserDetails>> GetManyByMinimumRoleAsync(Guid organizationId, OrganizationUserType minRole)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<OrganizationUserUserDetails>(
"[dbo].[OrganizationUser_ReadByMinimumRole]",
new { OrganizationId = organizationId, MinRole = minRole },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
}
}

View File

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class PolicyRepository : Repository<Policy, Guid>, IPolicyRepository
{
public PolicyRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public PolicyRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<Policy> GetByOrganizationIdTypeAsync(Guid organizationId, PolicyType type)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Policy>(
$"[{Schema}].[{Table}_ReadByOrganizationIdType]",
new { OrganizationId = organizationId, Type = (byte)type },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task<ICollection<Policy>> GetManyByOrganizationIdAsync(Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Policy>(
$"[{Schema}].[{Table}_ReadByOrganizationId]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<Policy>> GetManyByUserIdAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Policy>(
$"[{Schema}].[{Table}_ReadByUserId]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<Policy>> GetManyByTypeApplicableToUserIdAsync(Guid userId, PolicyType policyType,
OrganizationUserStatusType minStatus)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Policy>(
$"[{Schema}].[{Table}_ReadByTypeApplicableToUser]",
new { UserId = userId, PolicyType = policyType, MinimumStatus = minStatus },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<int> GetCountByTypeApplicableToUserIdAsync(Guid userId, PolicyType policyType,
OrganizationUserStatusType minStatus)
{
using (var connection = new SqlConnection(ConnectionString))
{
var result = await connection.ExecuteScalarAsync<int>(
$"[{Schema}].[{Table}_CountByTypeApplicableToUser]",
new { UserId = userId, PolicyType = policyType, MinimumStatus = minStatus },
commandType: CommandType.StoredProcedure);
return result;
}
}
}
}

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities.Provider;
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 ProviderOrganizationRepository : Repository<ProviderOrganization, Guid>, IProviderOrganizationRepository
{
public ProviderOrganizationRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public ProviderOrganizationRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<ICollection<ProviderOrganizationOrganizationDetails>> GetManyDetailsByProviderAsync(Guid providerId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<ProviderOrganizationOrganizationDetails>(
"[dbo].[ProviderOrganizationOrganizationDetails_ReadByProviderId]",
new { ProviderId = providerId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ProviderOrganization> GetByOrganizationId(Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<ProviderOrganization>(
"[dbo].[ProviderOrganization_ReadByOrganizationId]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
}
}

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities.Provider;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class ProviderRepository : Repository<Provider, Guid>, IProviderRepository
{
public ProviderRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public ProviderRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<ICollection<Provider>> SearchAsync(string name, string userEmail, int skip, int take)
{
using (var connection = new SqlConnection(ReadOnlyConnectionString))
{
var results = await connection.QueryAsync<Provider>(
"[dbo].[Provider_Search]",
new { Name = name, UserEmail = userEmail, Skip = skip, Take = take },
commandType: CommandType.StoredProcedure,
commandTimeout: 120);
return results.ToList();
}
}
public async Task<ICollection<ProviderAbility>> GetManyAbilitiesAsync()
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<ProviderAbility>(
"[dbo].[Provider_ReadAbilities]",
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
}
}

View File

@ -0,0 +1,169 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities.Provider;
using Bit.Core.Enums.Provider;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Dapper;
using Microsoft.Data.SqlClient;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class ProviderUserRepository : Repository<ProviderUser, Guid>, IProviderUserRepository
{
public ProviderUserRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public ProviderUserRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<int> GetCountByProviderAsync(Guid providerId, string email, bool onlyRegisteredUsers)
{
using (var connection = new SqlConnection(ConnectionString))
{
var result = await connection.ExecuteScalarAsync<int>(
"[dbo].[ProviderUser_ReadCountByProviderIdEmail]",
new { ProviderId = providerId, Email = email, OnlyUsers = onlyRegisteredUsers },
commandType: CommandType.StoredProcedure);
return result;
}
}
public async Task<ICollection<ProviderUser>> GetManyAsync(IEnumerable<Guid> ids)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<ProviderUser>(
"[dbo].[ProviderUser_ReadByIds]",
new { Ids = ids.ToGuidIdArrayTVP() },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<ProviderUser>> GetManyByUserAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<ProviderUser>(
"[dbo].[ProviderUser_ReadByUserId]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ProviderUser> GetByProviderUserAsync(Guid providerId, Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<ProviderUser>(
"[dbo].[ProviderUser_ReadByProviderIdUserId]",
new { ProviderId = providerId, UserId = userId },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task<ICollection<ProviderUser>> GetManyByProviderAsync(Guid providerId, ProviderUserType? type)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<ProviderUser>(
"[dbo].[ProviderUser_ReadByProviderId]",
new { ProviderId = providerId, Type = type },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<ProviderUserUserDetails>> GetManyDetailsByProviderAsync(Guid providerId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<ProviderUserUserDetails>(
"[dbo].[ProviderUserUserDetails_ReadByProviderId]",
new { ProviderId = providerId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<ProviderUserProviderDetails>> GetManyDetailsByUserAsync(Guid userId,
ProviderUserStatusType? status = null)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<ProviderUserProviderDetails>(
"[dbo].[ProviderUserProviderDetails_ReadByUserIdStatus]",
new { UserId = userId, Status = status },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<IEnumerable<ProviderUserOrganizationDetails>> GetManyOrganizationDetailsByUserAsync(Guid userId,
ProviderUserStatusType? status = null)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<ProviderUserOrganizationDetails>(
"[dbo].[ProviderUserProviderOrganizationDetails_ReadByUserIdStatus]",
new { UserId = userId, Status = status },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task DeleteManyAsync(IEnumerable<Guid> providerUserIds)
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync("[dbo].[ProviderUser_DeleteByIds]",
new { Ids = providerUserIds.ToGuidIdArrayTVP() }, commandType: CommandType.StoredProcedure);
}
}
public async Task<IEnumerable<ProviderUserPublicKey>> GetManyPublicKeysByProviderUserAsync(
Guid providerId, IEnumerable<Guid> Ids)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<ProviderUserPublicKey>(
"[dbo].[User_ReadPublicKeysByProviderUserIds]",
new { ProviderId = providerId, ProviderUserIds = Ids.ToGuidIdArrayTVP() },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<int> GetCountByOnlyOwnerAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteScalarAsync<int>(
"[dbo].[ProviderUser_ReadCountByOnlyOwner]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results;
}
}
}
}

View File

@ -0,0 +1,98 @@
using System;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public abstract class Repository<T, TId> : BaseRepository, IRepository<T, TId>
where TId : IEquatable<TId>
where T : class, ITableObject<TId>
{
public Repository(string connectionString, string readOnlyConnectionString,
string schema = null, string table = null)
: base(connectionString, readOnlyConnectionString)
{
if (!string.IsNullOrWhiteSpace(table))
{
Table = table;
}
if (!string.IsNullOrWhiteSpace(schema))
{
Schema = schema;
}
}
protected string Schema { get; private set; } = "dbo";
protected string Table { get; private set; } = typeof(T).Name;
public virtual async Task<T> GetByIdAsync(TId id)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<T>(
$"[{Schema}].[{Table}_ReadById]",
new { Id = id },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public virtual async Task<T> CreateAsync(T obj)
{
obj.SetNewId();
using (var connection = new SqlConnection(ConnectionString))
{
var parameters = new DynamicParameters();
parameters.AddDynamicParams(obj);
parameters.Add("Id", obj.Id, direction: ParameterDirection.InputOutput);
var results = await connection.ExecuteAsync(
$"[{Schema}].[{Table}_Create]",
parameters,
commandType: CommandType.StoredProcedure);
obj.Id = parameters.Get<TId>(nameof(obj.Id));
}
return obj;
}
public virtual async Task ReplaceAsync(T obj)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[{Table}_Update]",
obj,
commandType: CommandType.StoredProcedure);
}
}
public virtual async Task UpsertAsync(T obj)
{
if (obj.Id.Equals(default(TId)))
{
await CreateAsync(obj);
}
else
{
await ReplaceAsync(obj);
}
}
public virtual async Task DeleteAsync(T obj)
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(
$"[{Schema}].[{Table}_DeleteById]",
new { Id = obj.Id },
commandType: CommandType.StoredProcedure);
}
}
}
}

View File

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class SendRepository : Repository<Send, Guid>, ISendRepository
{
public SendRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public SendRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<ICollection<Send>> GetManyByUserIdAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Send>(
$"[{Schema}].[Send_ReadByUserId]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<Send>> GetManyByDeletionDateAsync(DateTime deletionDateBefore)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Send>(
$"[{Schema}].[Send_ReadByDeletionDateBefore]",
new { DeletionDate = deletionDateBefore },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
}
}

View File

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class SsoConfigRepository : Repository<SsoConfig, long>, ISsoConfigRepository
{
public SsoConfigRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public SsoConfigRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<SsoConfig> GetByOrganizationIdAsync(Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<SsoConfig>(
$"[{Schema}].[{Table}_ReadByOrganizationId]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task<SsoConfig> GetByIdentifierAsync(string identifier)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<SsoConfig>(
$"[{Schema}].[{Table}_ReadByIdentifier]",
new { Identifier = identifier },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task<ICollection<SsoConfig>> GetManyByRevisionNotBeforeDate(DateTime? notBefore)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<SsoConfig>(
$"[{Schema}].[{Table}_ReadManyByNotBeforeRevisionDate]",
new { NotBefore = notBefore },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class SsoUserRepository : Repository<SsoUser, long>, ISsoUserRepository
{
public SsoUserRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public SsoUserRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task DeleteAsync(Guid userId, Guid? organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[SsoUser_Delete]",
new { UserId = userId, OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
}
}
public async Task<SsoUser> GetByUserIdOrganizationIdAsync(Guid organizationId, Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<SsoUser>(
$"[{Schema}].[SsoUser_ReadByUserIdOrganizationId]",
new { UserId = userId, OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
}
}

View File

@ -0,0 +1,72 @@
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class TaxRateRepository : Repository<TaxRate, string>, ITaxRateRepository
{
public TaxRateRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public TaxRateRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<ICollection<TaxRate>> SearchAsync(int skip, int count)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<TaxRate>(
$"[{Schema}].[TaxRate_Search]",
new { Skip = skip, Count = count },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<TaxRate>> GetAllActiveAsync()
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<TaxRate>(
$"[{Schema}].[TaxRate_ReadAllActive]",
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task ArchiveAsync(TaxRate model)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[TaxRate_Archive]",
new { Id = model.Id },
commandType: CommandType.StoredProcedure);
}
}
public async Task<ICollection<TaxRate>> GetByLocationAsync(TaxRate model)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<TaxRate>(
$"[{Schema}].[TaxRate_ReadByLocation]",
new { Country = model.Country, PostalCode = model.PostalCode },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
}
}

View File

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class TransactionRepository : Repository<Transaction, Guid>, ITransactionRepository
{
public TransactionRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public TransactionRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<ICollection<Transaction>> GetManyByUserIdAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Transaction>(
$"[{Schema}].[Transaction_ReadByUserId]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<ICollection<Transaction>> GetManyByOrganizationIdAsync(Guid organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Transaction>(
$"[{Schema}].[Transaction_ReadByOrganizationId]",
new { OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<Transaction> GetByGatewayIdAsync(GatewayType gatewayType, string gatewayId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<Transaction>(
$"[{Schema}].[Transaction_ReadByGatewayId]",
new { Gateway = gatewayType, GatewayId = gatewayId },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
}
}

View File

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class U2fRepository : Repository<U2f, int>, IU2fRepository
{
public U2fRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public U2fRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<ICollection<U2f>> GetManyByUserIdAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<U2f>(
$"[{Schema}].[U2f_ReadByUserId]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task DeleteManyByUserIdAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(
$"[{Schema}].[U2f_DeleteByUserId]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
}
}
public override Task ReplaceAsync(U2f obj)
{
throw new NotSupportedException();
}
public override Task UpsertAsync(U2f obj)
{
throw new NotSupportedException();
}
public override Task DeleteAsync(U2f obj)
{
throw new NotSupportedException();
}
}
}

View File

@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Entities;
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories
{
public class UserRepository : Repository<User, Guid>, IUserRepository
{
public UserRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public UserRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public override async Task<User> GetByIdAsync(Guid id)
{
return await base.GetByIdAsync(id);
}
public async Task<User> GetByEmailAsync(string email)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<User>(
$"[{Schema}].[{Table}_ReadByEmail]",
new { Email = email },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task<User> GetBySsoUserAsync(string externalId, Guid? organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<User>(
$"[{Schema}].[{Table}_ReadBySsoUserOrganizationIdExternalId]",
new { OrganizationId = organizationId, ExternalId = externalId },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task<UserKdfInformation> GetKdfInformationByEmailAsync(string email)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<UserKdfInformation>(
$"[{Schema}].[{Table}_ReadKdfByEmail]",
new { Email = email },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task<ICollection<User>> SearchAsync(string email, int skip, int take)
{
using (var connection = new SqlConnection(ReadOnlyConnectionString))
{
var results = await connection.QueryAsync<User>(
$"[{Schema}].[{Table}_Search]",
new { Email = email, Skip = skip, Take = take },
commandType: CommandType.StoredProcedure,
commandTimeout: 120);
return results.ToList();
}
}
public async Task<ICollection<User>> GetManyByPremiumAsync(bool premium)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<User>(
"[dbo].[User_ReadByPremium]",
new { Premium = premium },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task<string> GetPublicKeyAsync(Guid id)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<string>(
$"[{Schema}].[{Table}_ReadPublicKeyById]",
new { Id = id },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public async Task<DateTime> GetAccountRevisionDateAsync(Guid id)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<DateTime>(
$"[{Schema}].[{Table}_ReadAccountRevisionDateById]",
new { Id = id },
commandType: CommandType.StoredProcedure);
return results.SingleOrDefault();
}
}
public override async Task ReplaceAsync(User user)
{
await base.ReplaceAsync(user);
}
public override async Task DeleteAsync(User user)
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(
$"[{Schema}].[{Table}_DeleteById]",
new { Id = user.Id },
commandType: CommandType.StoredProcedure,
commandTimeout: 180);
}
}
public async Task UpdateStorageAsync(Guid id)
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(
$"[{Schema}].[{Table}_UpdateStorage]",
new { Id = id },
commandType: CommandType.StoredProcedure,
commandTimeout: 180);
}
}
public async Task UpdateRenewalReminderDateAsync(Guid id, DateTime renewalReminderDate)
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(
$"[{Schema}].[User_UpdateRenewalReminderDate]",
new { Id = id, RenewalReminderDate = renewalReminderDate },
commandType: CommandType.StoredProcedure);
}
}
public async Task<IEnumerable<User>> GetManyAsync(IEnumerable<Guid> ids)
{
using (var connection = new SqlConnection(ReadOnlyConnectionString))
{
var results = await connection.QueryAsync<User>(
$"[{Schema}].[{Table}_ReadByIds]",
new { Ids = ids.ToGuidIdArrayTVP() },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
}
}