mirror of
https://github.com/bitwarden/server.git
synced 2025-07-03 00:52:49 -05:00
Added Sql project with database schema. Added SqlServer repositories for Site, Folder, and User (Cipher still TODO). Switched DI in Startup to SqlServer repos.
This commit is contained in:
@ -5,9 +5,10 @@ namespace Bit.Core
|
||||
{
|
||||
public class GlobalSettings
|
||||
{
|
||||
public string SiteName { get; set; }
|
||||
public string BaseVaultUri { get; set; }
|
||||
public virtual string SiteName { get; set; }
|
||||
public virtual string BaseVaultUri { get; set; }
|
||||
public virtual DocumentDBSettings DocumentDB { get; set; } = new DocumentDBSettings();
|
||||
public virtual SqlServerSettings SqlServer { get; set; } = new SqlServerSettings();
|
||||
public virtual MailSettings Mail { get; set; } = new MailSettings();
|
||||
|
||||
public class DocumentDBSettings
|
||||
@ -19,6 +20,11 @@ namespace Bit.Core
|
||||
public int NumberOfCollections { get; set; }
|
||||
}
|
||||
|
||||
public class SqlServerSettings
|
||||
{
|
||||
public string ConnectionString { get; set; }
|
||||
}
|
||||
|
||||
public class MailSettings
|
||||
{
|
||||
public string APIKey { get; set; }
|
||||
|
27
src/Core/Repositories/SqlServer/CipherRepository.cs
Normal file
27
src/Core/Repositories/SqlServer/CipherRepository.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bit.Core.Repositories.SqlServer
|
||||
{
|
||||
public class CipherRepository : ICipherRepository
|
||||
{
|
||||
public CipherRepository(string connectionString)
|
||||
{ }
|
||||
|
||||
public Task DirtyCiphersAsync(string userId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task UpdateDirtyCiphersAsync(IEnumerable<dynamic> ciphers)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task CreateAsync(IEnumerable<dynamic> ciphers)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
47
src/Core/Repositories/SqlServer/FolderRepository.cs
Normal file
47
src/Core/Repositories/SqlServer/FolderRepository.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Domains;
|
||||
using Bit.Core.Repositories.SqlServer.Models;
|
||||
using Dapper;
|
||||
|
||||
namespace Bit.Core.Repositories.SqlServer
|
||||
{
|
||||
public class FolderRepository : Repository<Folder, FolderTableModel>, IFolderRepository
|
||||
{
|
||||
public FolderRepository(string connectionString)
|
||||
: base(connectionString)
|
||||
{ }
|
||||
|
||||
public async Task<Folder> GetByIdAsync(string id, string userId)
|
||||
{
|
||||
var folder = await GetByIdAsync(id);
|
||||
if(folder == null || folder.UserId != userId)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
||||
public async Task<ICollection<Folder>> GetManyByUserIdAsync(string userId)
|
||||
{
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<FolderTableModel>(
|
||||
$"[{Schema}].[{Table}_ReadByUserId]",
|
||||
new { UserId = userId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results.Select(f => f.ToDomain()).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<Folder>> GetManyByUserIdAsync(string userId, bool dirty)
|
||||
{
|
||||
return await GetManyByUserIdAsync(userId);
|
||||
}
|
||||
}
|
||||
}
|
37
src/Core/Repositories/SqlServer/Models/FolderTableModel.cs
Normal file
37
src/Core/Repositories/SqlServer/Models/FolderTableModel.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using Bit.Core.Domains;
|
||||
|
||||
namespace Bit.Core.Repositories.SqlServer.Models
|
||||
{
|
||||
public class FolderTableModel : ITableModel<Folder>
|
||||
{
|
||||
public FolderTableModel() { }
|
||||
|
||||
public FolderTableModel(Folder folder)
|
||||
{
|
||||
Id = new Guid(folder.Id);
|
||||
UserId = new Guid(folder.UserId);
|
||||
Name = folder.Name;
|
||||
CreationDate = folder.CreationDate;
|
||||
RevisionDate = folder.RevisionDate;
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public DateTime CreationDate { get; set; }
|
||||
public DateTime RevisionDate { get; set; }
|
||||
|
||||
public Folder ToDomain()
|
||||
{
|
||||
return new Folder
|
||||
{
|
||||
Id = Id.ToString(),
|
||||
UserId = UserId.ToString(),
|
||||
Name = Name,
|
||||
CreationDate = CreationDate,
|
||||
RevisionDate = RevisionDate
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
7
src/Core/Repositories/SqlServer/Models/ITableModel.cs
Normal file
7
src/Core/Repositories/SqlServer/Models/ITableModel.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Bit.Core.Repositories.SqlServer.Models
|
||||
{
|
||||
public interface ITableModel<T>
|
||||
{
|
||||
T ToDomain();
|
||||
}
|
||||
}
|
52
src/Core/Repositories/SqlServer/Models/SiteTableModel.cs
Normal file
52
src/Core/Repositories/SqlServer/Models/SiteTableModel.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using Bit.Core.Domains;
|
||||
|
||||
namespace Bit.Core.Repositories.SqlServer.Models
|
||||
{
|
||||
public class SiteTableModel : ITableModel<Site>
|
||||
{
|
||||
public SiteTableModel() { }
|
||||
|
||||
public SiteTableModel(Site site)
|
||||
{
|
||||
Id = new Guid(site.Id);
|
||||
UserId = new Guid(site.UserId);
|
||||
FolderId = string.IsNullOrWhiteSpace(site.FolderId) ? (Guid?)null : new Guid(site.FolderId);
|
||||
Name = site.Name;
|
||||
Uri = site.Uri;
|
||||
Username = site.Username;
|
||||
Password = site.Password;
|
||||
Notes = site.Notes;
|
||||
CreationDate = site.CreationDate;
|
||||
RevisionDate = site.RevisionDate;
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public Guid? FolderId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Uri { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public DateTime CreationDate { get; set; }
|
||||
public DateTime RevisionDate { get; set; }
|
||||
|
||||
public Site ToDomain()
|
||||
{
|
||||
return new Site
|
||||
{
|
||||
Id = Id.ToString(),
|
||||
UserId = UserId.ToString(),
|
||||
FolderId = FolderId.ToString(),
|
||||
Name = Name,
|
||||
Uri = Uri,
|
||||
Username = Username,
|
||||
Password = Password,
|
||||
Notes = Notes,
|
||||
CreationDate = CreationDate,
|
||||
RevisionDate = RevisionDate
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
56
src/Core/Repositories/SqlServer/Models/UserTableModel.cs
Normal file
56
src/Core/Repositories/SqlServer/Models/UserTableModel.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using Bit.Core.Domains;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Repositories.SqlServer.Models
|
||||
{
|
||||
public class UserTableModel : ITableModel<User>
|
||||
{
|
||||
public UserTableModel() { }
|
||||
|
||||
public UserTableModel(User user)
|
||||
{
|
||||
Id = new Guid(user.Id);
|
||||
Name = user.Name;
|
||||
Email = user.Email;
|
||||
MasterPassword = user.MasterPassword;
|
||||
MasterPasswordHint = user.MasterPasswordHint;
|
||||
Culture = user.Culture;
|
||||
SecurityStamp = user.SecurityStamp;
|
||||
TwoFactorEnabled = user.TwoFactorEnabled;
|
||||
TwoFactorProvider = user.TwoFactorProvider;
|
||||
AuthenticatorKey = user.AuthenticatorKey;
|
||||
CreationDate = user.CreationDate;
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string MasterPassword { get; set; }
|
||||
public string MasterPasswordHint { get; set; }
|
||||
public string Culture { get; set; }
|
||||
public string SecurityStamp { get; set; }
|
||||
public bool TwoFactorEnabled { get; set; }
|
||||
public TwoFactorProvider? TwoFactorProvider { get; set; }
|
||||
public string AuthenticatorKey { get; set; }
|
||||
public DateTime CreationDate { get; set; }
|
||||
|
||||
public User ToDomain()
|
||||
{
|
||||
return new User
|
||||
{
|
||||
Id = Id.ToString(),
|
||||
Name = Name,
|
||||
Email = Email,
|
||||
MasterPassword = MasterPassword,
|
||||
MasterPasswordHint = MasterPasswordHint,
|
||||
Culture = Culture,
|
||||
SecurityStamp = SecurityStamp,
|
||||
TwoFactorEnabled = TwoFactorEnabled,
|
||||
TwoFactorProvider = TwoFactorProvider,
|
||||
AuthenticatorKey = AuthenticatorKey,
|
||||
CreationDate = CreationDate
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
111
src/Core/Repositories/SqlServer/Repository.cs
Normal file
111
src/Core/Repositories/SqlServer/Repository.cs
Normal file
@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Repositories.SqlServer.Models;
|
||||
using Dapper;
|
||||
|
||||
namespace Bit.Core.Repositories.SqlServer
|
||||
{
|
||||
public abstract class Repository<T, TModel> : IRepository<T> where T : IDataObject where TModel : ITableModel<T>
|
||||
{
|
||||
public Repository(string connectionString, string schema = null, string table = null)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(connectionString))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(connectionString));
|
||||
}
|
||||
|
||||
ConnectionString = connectionString;
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(table))
|
||||
{
|
||||
Table = table;
|
||||
}
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(schema))
|
||||
{
|
||||
Schema = schema;
|
||||
}
|
||||
}
|
||||
|
||||
protected string ConnectionString { get; private set; }
|
||||
protected string Schema { get; private set; } = "dbo";
|
||||
protected string Table { get; private set; } = typeof(T).Name;
|
||||
|
||||
public virtual async Task<T> GetByIdAsync(string id)
|
||||
{
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<TModel>(
|
||||
$"[{Schema}].[{Table}_ReadById]",
|
||||
new { Id = id },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
var model = results.FirstOrDefault();
|
||||
if(model == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
return model.ToDomain();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual async Task CreateAsync(T obj)
|
||||
{
|
||||
obj.Id = Guid.NewGuid().ToString();
|
||||
var tableModel = (TModel)Activator.CreateInstance(typeof(TModel), obj);
|
||||
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.ExecuteAsync(
|
||||
$"[{Schema}].[{Table}_Create]",
|
||||
tableModel,
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual async Task ReplaceAsync(T obj)
|
||||
{
|
||||
var tableModel = (TModel)Activator.CreateInstance(typeof(TModel), obj);
|
||||
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.ExecuteAsync(
|
||||
$"[{Schema}].[{Table}_Update]",
|
||||
tableModel,
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual async Task UpsertAsync(T obj)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(obj.Id))
|
||||
{
|
||||
await CreateAsync(obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplaceAsync(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual async Task DeleteAsync(T obj)
|
||||
{
|
||||
await DeleteByIdAsync(obj.Id);
|
||||
}
|
||||
|
||||
public virtual async Task DeleteByIdAsync(string id)
|
||||
{
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
await connection.ExecuteAsync(
|
||||
$"[{Schema}].[{Table}_DeleteById]",
|
||||
new { Id = id },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
src/Core/Repositories/SqlServer/SiteRepository.cs
Normal file
47
src/Core/Repositories/SqlServer/SiteRepository.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Domains;
|
||||
using Bit.Core.Repositories.SqlServer.Models;
|
||||
using Dapper;
|
||||
|
||||
namespace Bit.Core.Repositories.SqlServer
|
||||
{
|
||||
public class SiteRepository : Repository<Site, SiteTableModel>, ISiteRepository
|
||||
{
|
||||
public SiteRepository(string connectionString)
|
||||
: base(connectionString)
|
||||
{ }
|
||||
|
||||
public async Task<Site> GetByIdAsync(string id, string userId)
|
||||
{
|
||||
var site = await GetByIdAsync(id);
|
||||
if(site == null || site.UserId != userId)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return site;
|
||||
}
|
||||
|
||||
public async Task<ICollection<Site>> GetManyByUserIdAsync(string userId)
|
||||
{
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<SiteTableModel>(
|
||||
$"[{Schema}].[{Table}_ReadByUserId]",
|
||||
new { UserId = userId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results.Select(s => s.ToDomain()).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ICollection<Site>> GetManyByUserIdAsync(string userId, bool dirty)
|
||||
{
|
||||
return await GetManyByUserIdAsync(userId);
|
||||
}
|
||||
}
|
||||
}
|
36
src/Core/Repositories/SqlServer/UserRepository.cs
Normal file
36
src/Core/Repositories/SqlServer/UserRepository.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Domains;
|
||||
using Bit.Core.Repositories.SqlServer.Models;
|
||||
using Dapper;
|
||||
|
||||
namespace Bit.Core.Repositories.SqlServer
|
||||
{
|
||||
public class UserRepository : Repository<User, UserTableModel>, IUserRepository
|
||||
{
|
||||
public UserRepository(string connectionString)
|
||||
: base(connectionString)
|
||||
{ }
|
||||
|
||||
public async Task<User> GetByEmailAsync(string email)
|
||||
{
|
||||
using(var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<UserTableModel>(
|
||||
$"[{Schema}].[{Table}_ReadByEmail]",
|
||||
new { Email = email },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
var model = results.FirstOrDefault();
|
||||
if(model == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return model.ToDomain();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,8 @@
|
||||
"Sendgrid": "6.3.4",
|
||||
"Microsoft.AspNet.DataProtection.Extensions": "1.0.0-rc1-final",
|
||||
"Microsoft.Azure.DocumentDB": "1.5.2",
|
||||
"Newtonsoft.Json": "8.0.1"
|
||||
"Newtonsoft.Json": "8.0.1",
|
||||
"Dapper": "1.42.0"
|
||||
},
|
||||
|
||||
"frameworks": {
|
||||
|
Reference in New Issue
Block a user