From 13f85bf2f1b6ca29ebd74ac9e0381609a71c3b34 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Sat, 6 Feb 2016 01:18:25 -0500 Subject: [PATCH] Added Sql project with database schema. Added SqlServer repositories for Site, Folder, and User (Cipher still TODO). Switched DI in Startup to SqlServer repos. --- bitwarden-core.sln | 11 +- src/Api/Startup.cs | 12 +- src/Api/settings.json | 3 + src/Core/GlobalSettings.cs | 10 +- .../SqlServer/CipherRepository.cs | 27 +++++ .../SqlServer/FolderRepository.cs | 47 ++++++++ .../SqlServer/Models/FolderTableModel.cs | 37 ++++++ .../SqlServer/Models/ITableModel.cs | 7 ++ .../SqlServer/Models/SiteTableModel.cs | 52 ++++++++ .../SqlServer/Models/UserTableModel.cs | 56 +++++++++ src/Core/Repositories/SqlServer/Repository.cs | 111 ++++++++++++++++++ .../Repositories/SqlServer/SiteRepository.cs | 47 ++++++++ .../Repositories/SqlServer/UserRepository.cs | 36 ++++++ src/Core/project.json | 3 +- src/Sql/Sql.sqlproj | 91 ++++++++++++++ .../FolderAndSite_ReadByUserId.sql | 18 +++ .../dbo/Stored Procedures/Folder_Create.sql | 25 ++++ .../Stored Procedures/Folder_DeleteById.sql | 21 ++++ .../dbo/Stored Procedures/Folder_ReadById.sql | 11 ++ .../Stored Procedures/Folder_ReadByUserId.sql | 11 ++ .../dbo/Stored Procedures/Folder_Update.sql | 17 +++ src/Sql/dbo/Stored Procedures/Site_Create.sql | 40 +++++++ .../dbo/Stored Procedures/Site_DeleteById.sql | 10 ++ .../dbo/Stored Procedures/Site_ReadById.sql | 11 ++ .../Stored Procedures/Site_ReadByUserId.sql | 11 ++ src/Sql/dbo/Stored Procedures/Site_Update.sql | 27 +++++ src/Sql/dbo/Stored Procedures/User_Create.sql | 43 +++++++ .../dbo/Stored Procedures/User_DeleteById.sql | 26 ++++ .../Stored Procedures/User_ReadByEmail.sql | 11 ++ .../dbo/Stored Procedures/User_ReadById.sql | 11 ++ src/Sql/dbo/Stored Procedures/User_Update.sql | 29 +++++ src/Sql/dbo/Tables/Folder.sql | 15 +++ src/Sql/dbo/Tables/Site.sql | 21 ++++ src/Sql/dbo/Tables/User.sql | 20 ++++ src/Sql/dbo/Views/FolderView.sql | 6 + src/Sql/dbo/Views/SiteView.sql | 6 + src/Sql/dbo/Views/UserView.sql | 6 + 37 files changed, 935 insertions(+), 11 deletions(-) create mode 100644 src/Core/Repositories/SqlServer/CipherRepository.cs create mode 100644 src/Core/Repositories/SqlServer/FolderRepository.cs create mode 100644 src/Core/Repositories/SqlServer/Models/FolderTableModel.cs create mode 100644 src/Core/Repositories/SqlServer/Models/ITableModel.cs create mode 100644 src/Core/Repositories/SqlServer/Models/SiteTableModel.cs create mode 100644 src/Core/Repositories/SqlServer/Models/UserTableModel.cs create mode 100644 src/Core/Repositories/SqlServer/Repository.cs create mode 100644 src/Core/Repositories/SqlServer/SiteRepository.cs create mode 100644 src/Core/Repositories/SqlServer/UserRepository.cs create mode 100644 src/Sql/Sql.sqlproj create mode 100644 src/Sql/dbo/Stored Procedures/FolderAndSite_ReadByUserId.sql create mode 100644 src/Sql/dbo/Stored Procedures/Folder_Create.sql create mode 100644 src/Sql/dbo/Stored Procedures/Folder_DeleteById.sql create mode 100644 src/Sql/dbo/Stored Procedures/Folder_ReadById.sql create mode 100644 src/Sql/dbo/Stored Procedures/Folder_ReadByUserId.sql create mode 100644 src/Sql/dbo/Stored Procedures/Folder_Update.sql create mode 100644 src/Sql/dbo/Stored Procedures/Site_Create.sql create mode 100644 src/Sql/dbo/Stored Procedures/Site_DeleteById.sql create mode 100644 src/Sql/dbo/Stored Procedures/Site_ReadById.sql create mode 100644 src/Sql/dbo/Stored Procedures/Site_ReadByUserId.sql create mode 100644 src/Sql/dbo/Stored Procedures/Site_Update.sql create mode 100644 src/Sql/dbo/Stored Procedures/User_Create.sql create mode 100644 src/Sql/dbo/Stored Procedures/User_DeleteById.sql create mode 100644 src/Sql/dbo/Stored Procedures/User_ReadByEmail.sql create mode 100644 src/Sql/dbo/Stored Procedures/User_ReadById.sql create mode 100644 src/Sql/dbo/Stored Procedures/User_Update.sql create mode 100644 src/Sql/dbo/Tables/Folder.sql create mode 100644 src/Sql/dbo/Tables/Site.sql create mode 100644 src/Sql/dbo/Tables/User.sql create mode 100644 src/Sql/dbo/Views/FolderView.sql create mode 100644 src/Sql/dbo/Views/SiteView.sql create mode 100644 src/Sql/dbo/Views/UserView.sql diff --git a/bitwarden-core.sln b/bitwarden-core.sln index 9da5cfe97..5118e544a 100644 --- a/bitwarden-core.sln +++ b/bitwarden-core.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.23107.0 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}" EndProject @@ -17,6 +17,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Core", "src\Core\Core.xproj EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Api", "src\Api\Api.xproj", "{E8548AD6-7FB0-439A-8EB5-549A10336D2D}" EndProject +Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "Sql", "src\Sql\Sql.sqlproj", "{58554E52-FDEC-4832-AFF9-302B01E08DCA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -31,6 +33,12 @@ Global {E8548AD6-7FB0-439A-8EB5-549A10336D2D}.Debug|Any CPU.Build.0 = Debug|Any CPU {E8548AD6-7FB0-439A-8EB5-549A10336D2D}.Release|Any CPU.ActiveCfg = Release|Any CPU {E8548AD6-7FB0-439A-8EB5-549A10336D2D}.Release|Any CPU.Build.0 = Release|Any CPU + {58554E52-FDEC-4832-AFF9-302B01E08DCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58554E52-FDEC-4832-AFF9-302B01E08DCA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58554E52-FDEC-4832-AFF9-302B01E08DCA}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {58554E52-FDEC-4832-AFF9-302B01E08DCA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58554E52-FDEC-4832-AFF9-302B01E08DCA}.Release|Any CPU.Build.0 = Release|Any CPU + {58554E52-FDEC-4832-AFF9-302B01E08DCA}.Release|Any CPU.Deploy.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -38,5 +46,6 @@ Global GlobalSection(NestedProjects) = preSolution {3973D21B-A692-4B60-9B70-3631C057423A} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D} {E8548AD6-7FB0-439A-8EB5-549A10336D2D} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D} + {58554E52-FDEC-4832-AFF9-302B01E08DCA} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D} EndGlobalSection EndGlobal diff --git a/src/Api/Startup.cs b/src/Api/Startup.cs index e7420eaa9..00dc03646 100644 --- a/src/Api/Startup.cs +++ b/src/Api/Startup.cs @@ -14,9 +14,8 @@ using Bit.Core; using Bit.Core.Domains; using Bit.Core.Identity; using Bit.Core.Repositories; -using Bit.Core.Repositories.DocumentDB.Utilities; using Bit.Core.Services; -using Repos = Bit.Core.Repositories.DocumentDB; +using Repos = Bit.Core.Repositories.SqlServer; namespace Bit.Api { @@ -53,11 +52,10 @@ namespace Bit.Api services.AddSingleton(s => globalSettings); // Repositories - var documentDBClient = DocumentDBHelpers.InitClient(globalSettings.DocumentDB); - services.AddSingleton(s => new Repos.UserRepository(documentDBClient, globalSettings.DocumentDB.DatabaseId)); - services.AddSingleton(s => new Repos.SiteRepository(documentDBClient, globalSettings.DocumentDB.DatabaseId)); - services.AddSingleton(s => new Repos.FolderRepository(documentDBClient, globalSettings.DocumentDB.DatabaseId)); - services.AddSingleton(s => new Repos.CipherRepository(documentDBClient, globalSettings.DocumentDB.DatabaseId)); + services.AddSingleton(s => new Repos.UserRepository(globalSettings.SqlServer.ConnectionString)); + services.AddSingleton(s => new Repos.SiteRepository(globalSettings.SqlServer.ConnectionString)); + services.AddSingleton(s => new Repos.FolderRepository(globalSettings.SqlServer.ConnectionString)); + services.AddSingleton(s => new Repos.CipherRepository(globalSettings.SqlServer.ConnectionString)); // Context services.AddScoped(); diff --git a/src/Api/settings.json b/src/Api/settings.json index f5e067e59..b2bcda01f 100644 --- a/src/Api/settings.json +++ b/src/Api/settings.json @@ -9,6 +9,9 @@ "collectionIdPrefix": "SECRET", "numberOfCollections": 1 }, + "sqlServer": { + "connectionString": "SECRET" + }, "mail": { "apiKey": "SECRET", "replyToEmail": "do-not-reply@bitwarden.com" diff --git a/src/Core/GlobalSettings.cs b/src/Core/GlobalSettings.cs index b62bdc270..12e093d7a 100644 --- a/src/Core/GlobalSettings.cs +++ b/src/Core/GlobalSettings.cs @@ -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; } diff --git a/src/Core/Repositories/SqlServer/CipherRepository.cs b/src/Core/Repositories/SqlServer/CipherRepository.cs new file mode 100644 index 000000000..516106feb --- /dev/null +++ b/src/Core/Repositories/SqlServer/CipherRepository.cs @@ -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 ciphers) + { + throw new NotImplementedException(); + } + + public Task CreateAsync(IEnumerable ciphers) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Core/Repositories/SqlServer/FolderRepository.cs b/src/Core/Repositories/SqlServer/FolderRepository.cs new file mode 100644 index 000000000..61e6cf2ae --- /dev/null +++ b/src/Core/Repositories/SqlServer/FolderRepository.cs @@ -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, IFolderRepository + { + public FolderRepository(string connectionString) + : base(connectionString) + { } + + public async Task GetByIdAsync(string id, string userId) + { + var folder = await GetByIdAsync(id); + if(folder == null || folder.UserId != userId) + { + return null; + } + + return folder; + } + + public async Task> GetManyByUserIdAsync(string userId) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + $"[{Schema}].[{Table}_ReadByUserId]", + new { UserId = userId }, + commandType: CommandType.StoredProcedure); + + return results.Select(f => f.ToDomain()).ToList(); + } + } + + public async Task> GetManyByUserIdAsync(string userId, bool dirty) + { + return await GetManyByUserIdAsync(userId); + } + } +} diff --git a/src/Core/Repositories/SqlServer/Models/FolderTableModel.cs b/src/Core/Repositories/SqlServer/Models/FolderTableModel.cs new file mode 100644 index 000000000..f19618aa1 --- /dev/null +++ b/src/Core/Repositories/SqlServer/Models/FolderTableModel.cs @@ -0,0 +1,37 @@ +using System; +using Bit.Core.Domains; + +namespace Bit.Core.Repositories.SqlServer.Models +{ + public class FolderTableModel : ITableModel + { + 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 + }; + } + } +} diff --git a/src/Core/Repositories/SqlServer/Models/ITableModel.cs b/src/Core/Repositories/SqlServer/Models/ITableModel.cs new file mode 100644 index 000000000..ac581e78c --- /dev/null +++ b/src/Core/Repositories/SqlServer/Models/ITableModel.cs @@ -0,0 +1,7 @@ +namespace Bit.Core.Repositories.SqlServer.Models +{ + public interface ITableModel + { + T ToDomain(); + } +} diff --git a/src/Core/Repositories/SqlServer/Models/SiteTableModel.cs b/src/Core/Repositories/SqlServer/Models/SiteTableModel.cs new file mode 100644 index 000000000..7ecb968ea --- /dev/null +++ b/src/Core/Repositories/SqlServer/Models/SiteTableModel.cs @@ -0,0 +1,52 @@ +using System; +using Bit.Core.Domains; + +namespace Bit.Core.Repositories.SqlServer.Models +{ + public class SiteTableModel : ITableModel + { + 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 + }; + } + } +} diff --git a/src/Core/Repositories/SqlServer/Models/UserTableModel.cs b/src/Core/Repositories/SqlServer/Models/UserTableModel.cs new file mode 100644 index 000000000..371ced193 --- /dev/null +++ b/src/Core/Repositories/SqlServer/Models/UserTableModel.cs @@ -0,0 +1,56 @@ +using System; +using Bit.Core.Domains; +using Bit.Core.Enums; + +namespace Bit.Core.Repositories.SqlServer.Models +{ + public class UserTableModel : ITableModel + { + 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 + }; + } + } +} diff --git a/src/Core/Repositories/SqlServer/Repository.cs b/src/Core/Repositories/SqlServer/Repository.cs new file mode 100644 index 000000000..6e1ef5e9c --- /dev/null +++ b/src/Core/Repositories/SqlServer/Repository.cs @@ -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 : IRepository where T : IDataObject where TModel : ITableModel + { + 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 GetByIdAsync(string id) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + $"[{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); + } + } + } +} diff --git a/src/Core/Repositories/SqlServer/SiteRepository.cs b/src/Core/Repositories/SqlServer/SiteRepository.cs new file mode 100644 index 000000000..19d616065 --- /dev/null +++ b/src/Core/Repositories/SqlServer/SiteRepository.cs @@ -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, ISiteRepository + { + public SiteRepository(string connectionString) + : base(connectionString) + { } + + public async Task GetByIdAsync(string id, string userId) + { + var site = await GetByIdAsync(id); + if(site == null || site.UserId != userId) + { + return null; + } + + return site; + } + + public async Task> GetManyByUserIdAsync(string userId) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + $"[{Schema}].[{Table}_ReadByUserId]", + new { UserId = userId }, + commandType: CommandType.StoredProcedure); + + return results.Select(s => s.ToDomain()).ToList(); + } + } + + public async Task> GetManyByUserIdAsync(string userId, bool dirty) + { + return await GetManyByUserIdAsync(userId); + } + } +} diff --git a/src/Core/Repositories/SqlServer/UserRepository.cs b/src/Core/Repositories/SqlServer/UserRepository.cs new file mode 100644 index 000000000..dbfe04499 --- /dev/null +++ b/src/Core/Repositories/SqlServer/UserRepository.cs @@ -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, IUserRepository + { + public UserRepository(string connectionString) + : base(connectionString) + { } + + public async Task GetByEmailAsync(string email) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + $"[{Schema}].[{Table}_ReadByEmail]", + new { Email = email }, + commandType: CommandType.StoredProcedure); + + var model = results.FirstOrDefault(); + if(model == null) + { + return null; + } + + return model.ToDomain(); + } + } + } +} diff --git a/src/Core/project.json b/src/Core/project.json index b6964efe1..76ffe0d6d 100644 --- a/src/Core/project.json +++ b/src/Core/project.json @@ -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": { diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj new file mode 100644 index 000000000..17578ddc0 --- /dev/null +++ b/src/Sql/Sql.sqlproj @@ -0,0 +1,91 @@ + + + + + Debug + AnyCPU + Sql + 2.0 + 4.1 + {58554e52-fdec-4832-aff9-302b01e08dca} + Microsoft.Data.Tools.Schema.Sql.Sql120DatabaseSchemaProvider + Database + + + Sql + Sql + 1033,CI + BySchemaAndSchemaType + True + v4.6 + CS + Properties + False + True + True + SQL_Latin1_General_CP1_CI_AS + PRIMARY + + + bin\Release\ + $(MSBuildProjectName).sql + False + pdbonly + true + false + true + prompt + 4 + + + bin\Debug\ + $(MSBuildProjectName).sql + false + true + full + false + true + true + prompt + 4 + + + 11.0 + + True + 11.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/FolderAndSite_ReadByUserId.sql b/src/Sql/dbo/Stored Procedures/FolderAndSite_ReadByUserId.sql new file mode 100644 index 000000000..c201ee1de --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/FolderAndSite_ReadByUserId.sql @@ -0,0 +1,18 @@ +CREATE PROCEDURE [dbo].[FolderAndSite_ReadByUserId] + @UserId UNIQUEIDENTIFIER +AS +BEGIN + SELECT + * + FROM + [dbo].[FolderView] + WHERE + [UserId] = @UserId + + SELECT + * + FROM + [dbo].[SiteView] + WHERE + [UserId] = @UserId +END diff --git a/src/Sql/dbo/Stored Procedures/Folder_Create.sql b/src/Sql/dbo/Stored Procedures/Folder_Create.sql new file mode 100644 index 000000000..a3b05d19c --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Folder_Create.sql @@ -0,0 +1,25 @@ +CREATE PROCEDURE [dbo].[Folder_Create] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @Name NVARCHAR(MAX), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7) +AS +BEGIN + INSERT INTO [dbo].[Folder] + ( + [Id], + [UserId], + [Name], + [CreationDate], + [RevisionDate] + ) + VALUES + ( + @Id, + @UserId, + @Name, + @CreationDate, + @RevisionDate + ) +END diff --git a/src/Sql/dbo/Stored Procedures/Folder_DeleteById.sql b/src/Sql/dbo/Stored Procedures/Folder_DeleteById.sql new file mode 100644 index 000000000..7fcc5eedd --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Folder_DeleteById.sql @@ -0,0 +1,21 @@ +CREATE PROCEDURE [dbo].[Folder_DeleteById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + BEGIN TRANSACTION Folder_DeleteById + + UPDATE + [dbo].[Site] + SET + [FolderId] = NULL + WHERE + [FolderId] = @Id + + DELETE + FROM + [dbo].[Folder] + WHERE + [Id] = @Id + + COMMIT TRANSACTION Folder_DeleteById +END diff --git a/src/Sql/dbo/Stored Procedures/Folder_ReadById.sql b/src/Sql/dbo/Stored Procedures/Folder_ReadById.sql new file mode 100644 index 000000000..3c322f660 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Folder_ReadById.sql @@ -0,0 +1,11 @@ +CREATE PROCEDURE [dbo].[Folder_ReadById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SELECT + * + FROM + [dbo].[FolderView] + WHERE + [Id] = @Id +END diff --git a/src/Sql/dbo/Stored Procedures/Folder_ReadByUserId.sql b/src/Sql/dbo/Stored Procedures/Folder_ReadByUserId.sql new file mode 100644 index 000000000..6cda77314 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Folder_ReadByUserId.sql @@ -0,0 +1,11 @@ +CREATE PROCEDURE [dbo].[Folder_ReadByUserId] + @UserId UNIQUEIDENTIFIER +AS +BEGIN + SELECT + * + FROM + [dbo].[FolderView] + WHERE + [UserId] = @UserId +END diff --git a/src/Sql/dbo/Stored Procedures/Folder_Update.sql b/src/Sql/dbo/Stored Procedures/Folder_Update.sql new file mode 100644 index 000000000..aadcc31f1 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Folder_Update.sql @@ -0,0 +1,17 @@ +CREATE PROCEDURE [dbo].[Folder_Update] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @Name NVARCHAR(MAX), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7) +AS +BEGIN + UPDATE [dbo].[Folder] + SET + [UserId] = @UserId, + [Name] = @Name, + [CreationDate] = @CreationDate, + [RevisionDate] = @RevisionDate + WHERE + [Id] = @Id +END diff --git a/src/Sql/dbo/Stored Procedures/Site_Create.sql b/src/Sql/dbo/Stored Procedures/Site_Create.sql new file mode 100644 index 000000000..82eb157ff --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Site_Create.sql @@ -0,0 +1,40 @@ +CREATE PROCEDURE [dbo].[Site_Create] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @FolderId UNIQUEIDENTIFIER, + @Name NVARCHAR(MAX), + @Uri NVARCHAR(MAX), + @Username NVARCHAR(MAX), + @Password NVARCHAR(MAX), + @Notes NVARCHAR(MAX), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7) +AS +BEGIN + INSERT INTO [dbo].[Site] + ( + [Id], + [UserId], + [FolderId], + [Name], + [Uri], + [Username], + [Password], + [Notes], + [CreationDate], + [RevisionDate] + ) + VALUES + ( + @Id, + @UserId, + @FolderId, + @Name, + @Uri, + @Username, + @Password, + @Notes, + @CreationDate, + @RevisionDate + ) +END diff --git a/src/Sql/dbo/Stored Procedures/Site_DeleteById.sql b/src/Sql/dbo/Stored Procedures/Site_DeleteById.sql new file mode 100644 index 000000000..097600fb1 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Site_DeleteById.sql @@ -0,0 +1,10 @@ +CREATE PROCEDURE [dbo].[Site_DeleteById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + DELETE + FROM + [dbo].[Site] + WHERE + [Id] = @Id +END diff --git a/src/Sql/dbo/Stored Procedures/Site_ReadById.sql b/src/Sql/dbo/Stored Procedures/Site_ReadById.sql new file mode 100644 index 000000000..c585f08d4 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Site_ReadById.sql @@ -0,0 +1,11 @@ +CREATE PROCEDURE [dbo].[Site_ReadById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SELECT + * + FROM + [dbo].[SiteView] + WHERE + [Id] = @Id +END diff --git a/src/Sql/dbo/Stored Procedures/Site_ReadByUserId.sql b/src/Sql/dbo/Stored Procedures/Site_ReadByUserId.sql new file mode 100644 index 000000000..7b1503092 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Site_ReadByUserId.sql @@ -0,0 +1,11 @@ +CREATE PROCEDURE [dbo].[Site_ReadByUserId] + @UserId UNIQUEIDENTIFIER +AS +BEGIN + SELECT + * + FROM + [dbo].[SiteView] + WHERE + [UserId] = @UserId +END diff --git a/src/Sql/dbo/Stored Procedures/Site_Update.sql b/src/Sql/dbo/Stored Procedures/Site_Update.sql new file mode 100644 index 000000000..e6ab31c17 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Site_Update.sql @@ -0,0 +1,27 @@ +CREATE PROCEDURE [dbo].[Site_Update] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @FolderId UNIQUEIDENTIFIER, + @Name NVARCHAR(MAX), + @Uri NVARCHAR(MAX), + @Username NVARCHAR(MAX), + @Password NVARCHAR(MAX), + @Notes NVARCHAR(MAX), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7) +AS +BEGIN + UPDATE [dbo].[Site] + SET + [UserId] = @UserId, + [FolderId] = @FolderId, + [Name] = @Name, + [Uri] = @Uri, + [Username] = @Username, + [Password] = @Password, + [Notes] = @Notes, + [CreationDate] = @CreationDate, + [RevisionDate] = @RevisionDate + WHERE + [Id] = @Id +END diff --git a/src/Sql/dbo/Stored Procedures/User_Create.sql b/src/Sql/dbo/Stored Procedures/User_Create.sql new file mode 100644 index 000000000..480726cac --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/User_Create.sql @@ -0,0 +1,43 @@ +CREATE PROCEDURE [dbo].[User_Create] + @Id UNIQUEIDENTIFIER, + @Name NVARCHAR(50), + @Email NVARCHAR(50), + @MasterPassword NVARCHAR(300), + @MasterPasswordHint NVARCHAR(50), + @Culture NVARCHAR(10), + @SecurityStamp NVARCHAR(50), + @TwoFactorEnabled BIT, + @TwoFactorProvider TINYINT, + @AuthenticatorKey NVARCHAR(50), + @CreationDate DATETIME2(7) +AS +BEGIN + INSERT INTO [dbo].[User] + ( + [Id], + [Name], + [Email], + [MasterPassword], + [MasterPasswordHint], + [Culture], + [SecurityStamp], + [TwoFactorEnabled], + [TwoFactorProvider], + [AuthenticatorKey], + [CreationDate] + ) + VALUES + ( + @Id, + @Name, + @Email, + @MasterPassword, + @MasterPasswordHint, + @Culture, + @SecurityStamp, + @TwoFactorEnabled, + @TwoFactorProvider, + @AuthenticatorKey, + @CreationDate + ) +END diff --git a/src/Sql/dbo/Stored Procedures/User_DeleteById.sql b/src/Sql/dbo/Stored Procedures/User_DeleteById.sql new file mode 100644 index 000000000..4971ef4f2 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/User_DeleteById.sql @@ -0,0 +1,26 @@ +CREATE PROCEDURE [dbo].[User_DeleteById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + BEGIN TRANSACTION User_DeleteById + + DELETE + FROM + [dbo].[Site] + WHERE + [UserId] = @Id + + DELETE + FROM + [dbo].[Folder] + WHERE + [UserId] = @Id + + DELETE + FROM + [dbo].[User] + WHERE + [Id] = @Id + + COMMIT TRANSACTION User_DeleteById +END diff --git a/src/Sql/dbo/Stored Procedures/User_ReadByEmail.sql b/src/Sql/dbo/Stored Procedures/User_ReadByEmail.sql new file mode 100644 index 000000000..d84fad0bd --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/User_ReadByEmail.sql @@ -0,0 +1,11 @@ +CREATE PROCEDURE [dbo].[User_ReadByEmail] + @Email NVARCHAR(50) +AS +BEGIN + SELECT + * + FROM + [dbo].[UserView] + WHERE + [Email] = @Email +END diff --git a/src/Sql/dbo/Stored Procedures/User_ReadById.sql b/src/Sql/dbo/Stored Procedures/User_ReadById.sql new file mode 100644 index 000000000..0ae4d05c3 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/User_ReadById.sql @@ -0,0 +1,11 @@ +CREATE PROCEDURE [dbo].[User_ReadById] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SELECT + * + FROM + [dbo].[UserView] + WHERE + [Id] = @Id +END diff --git a/src/Sql/dbo/Stored Procedures/User_Update.sql b/src/Sql/dbo/Stored Procedures/User_Update.sql new file mode 100644 index 000000000..fee898e74 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/User_Update.sql @@ -0,0 +1,29 @@ +CREATE PROCEDURE [dbo].[User_Update] + @Id UNIQUEIDENTIFIER, + @Name NVARCHAR(50), + @Email NVARCHAR(50), + @MasterPassword NVARCHAR(300), + @MasterPasswordHint NVARCHAR(50), + @Culture NVARCHAR(10), + @SecurityStamp NVARCHAR(50), + @TwoFactorEnabled BIT, + @TwoFactorProvider TINYINT, + @AuthenticatorKey NVARCHAR(50), + @CreationDate DATETIME2(7) +AS +BEGIN + UPDATE [dbo].[User] + SET + [Name] = @Name, + [Email] = @Email, + [MasterPassword] = @MasterPassword, + [MasterPasswordHint] = @MasterPasswordHint, + [Culture] = @Culture, + [SecurityStamp] = @SecurityStamp, + [TwoFactorEnabled] = @TwoFactorEnabled, + [TwoFactorProvider] = TwoFactorProvider, + [AuthenticatorKey] = @AuthenticatorKey, + [CreationDate] = @CreationDate + WHERE + [Id] = @Id +END diff --git a/src/Sql/dbo/Tables/Folder.sql b/src/Sql/dbo/Tables/Folder.sql new file mode 100644 index 000000000..5b1d63d50 --- /dev/null +++ b/src/Sql/dbo/Tables/Folder.sql @@ -0,0 +1,15 @@ +CREATE TABLE [dbo].[Folder] ( + [Id] UNIQUEIDENTIFIER NOT NULL, + [UserId] UNIQUEIDENTIFIER NOT NULL, + [Name] NVARCHAR (MAX) NOT NULL, + [CreationDate] DATETIME2 (7) NOT NULL, + [RevisionDate] DATETIME2 (7) NOT NULL, + CONSTRAINT [PK_Folder] PRIMARY KEY CLUSTERED ([Id] ASC), + CONSTRAINT [FK_Folder_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) +); + + +GO +CREATE NONCLUSTERED INDEX [IX_Folder_UserId] + ON [dbo].[Folder]([UserId] ASC); + diff --git a/src/Sql/dbo/Tables/Site.sql b/src/Sql/dbo/Tables/Site.sql new file mode 100644 index 000000000..7dbbf7e4b --- /dev/null +++ b/src/Sql/dbo/Tables/Site.sql @@ -0,0 +1,21 @@ +CREATE TABLE [dbo].[Site] ( + [Id] UNIQUEIDENTIFIER NOT NULL, + [UserId] UNIQUEIDENTIFIER NOT NULL, + [FolderId] UNIQUEIDENTIFIER NULL, + [Name] NVARCHAR (MAX) NOT NULL, + [Uri] NVARCHAR (MAX) NULL, + [Username] NVARCHAR (MAX) NULL, + [Password] NVARCHAR (MAX) NULL, + [Notes] NVARCHAR (MAX) NULL, + [CreationDate] DATETIME2 (7) NOT NULL, + [RevisionDate] DATETIME2 (7) NOT NULL, + CONSTRAINT [PK_Site] PRIMARY KEY CLUSTERED ([Id] ASC), + CONSTRAINT [FK_Site_Folder] FOREIGN KEY ([FolderId]) REFERENCES [dbo].[Folder] ([Id]), + CONSTRAINT [FK_Site_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) +); + + +GO +CREATE NONCLUSTERED INDEX [IX_Site_UserId] + ON [dbo].[Site]([UserId] ASC); + diff --git a/src/Sql/dbo/Tables/User.sql b/src/Sql/dbo/Tables/User.sql new file mode 100644 index 000000000..bb882e0d5 --- /dev/null +++ b/src/Sql/dbo/Tables/User.sql @@ -0,0 +1,20 @@ +CREATE TABLE [dbo].[User] ( + [Id] UNIQUEIDENTIFIER NOT NULL, + [Name] NVARCHAR (50) NULL, + [Email] NVARCHAR (50) NOT NULL, + [MasterPassword] NVARCHAR (300) NOT NULL, + [MasterPasswordHint] NVARCHAR (50) NULL, + [Culture] NVARCHAR (10) NOT NULL, + [SecurityStamp] NVARCHAR (50) NOT NULL, + [TwoFactorEnabled] BIT NOT NULL, + [TwoFactorProvider] TINYINT NULL, + [AuthenticatorKey] NVARCHAR (50) NULL, + [CreationDate] DATETIME2 (7) NOT NULL, + CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC) +); + + +GO +CREATE NONCLUSTERED INDEX [IX_User_Email] + ON [dbo].[User]([Email] ASC); + diff --git a/src/Sql/dbo/Views/FolderView.sql b/src/Sql/dbo/Views/FolderView.sql new file mode 100644 index 000000000..4a6fc9158 --- /dev/null +++ b/src/Sql/dbo/Views/FolderView.sql @@ -0,0 +1,6 @@ +CREATE VIEW [dbo].[FolderView] +AS +SELECT + * +FROM + [dbo].[Folder] diff --git a/src/Sql/dbo/Views/SiteView.sql b/src/Sql/dbo/Views/SiteView.sql new file mode 100644 index 000000000..26e779468 --- /dev/null +++ b/src/Sql/dbo/Views/SiteView.sql @@ -0,0 +1,6 @@ +CREATE VIEW [dbo].[SiteView] +AS +SELECT + * +FROM + [dbo].[Site] \ No newline at end of file diff --git a/src/Sql/dbo/Views/UserView.sql b/src/Sql/dbo/Views/UserView.sql new file mode 100644 index 000000000..82fa8a2c6 --- /dev/null +++ b/src/Sql/dbo/Views/UserView.sql @@ -0,0 +1,6 @@ +CREATE VIEW [dbo].[UserView] +AS +SELECT + * +FROM + [dbo].[User]