1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-28 13:15:12 +01:00

Postgres & MySql Support For Self-Hosted Installations (#1386)

* EF Database Support Init (#1221)

* scaffolding for ef support

* deleted old postgres repos

* added tables to oncreate

* updated all the things to .NET 5

* Addition to #1221: Migrated DockerFiles from dotnet/3.1 to  5.0 (#1223)

* Migrated DockerFiles from dotnet/3.1 to  5.0

* Migrated SSO/Dockerfile from dotnet 3.1 to 5.0

Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>

* EFDatabaseSupport: Updated links and description in README.md and SETUP.md (#1232)

* Updated requirements in README.md

* Updated link to documentation of app-secrets

* upgraded dotnet version to 5.0

* Ef database support implementation examples (#1265)

* mostly finished testing the user repo

* finished testing user repo

* finished org, user, ssoconfig, and ssouser ef implementations

* removed unused prop

* fixed a sql file

* fixed a spacing issue

* fixed a spacing issue

* removed extra database creation

* refactoring

* MsSql => SqlServer

* refactoring

* code review fixes

* build fix

* code review

* continued attempts to fix the the build

* skipped another test

* finished all create test

* initial pass at several repos

* continued building out repos

* initial pass at several repos

* initial pass at device repo

* initial pass at collection repo

* initial run of all Entity Framework implementations

* signup, signin, create/edit ciphers works

* sync working

* all web vault pages seem to load with 100% 200s

* bulkcopy, folders, and favorites

* group and collection management

* sso, groups, emergency access, send

* get basic creates matching on all repos

* got everything building again post merge

* removed some IDE config files

* cleanup

* no more notimplemented methods in the cipher repo

* no more not implementeds everywhere

* cleaned up schema/navigation properties and fixed tests

* removed a sql comment that was written in c# style

* fixed build issues from merge

* removed unsupported db providers

* formatting

* code review refactors

* naming cleanup for queries

* added provider methods

* cipher repo cleanup

* implemented several missing procedures from the EF implementation surround account revision dates, keys, and storage

* fixed the build

* added a null check

* consolidated some cipher repo methods

* formatting fix

* cleaned up indentation of queries

* removed .idea file

* generated postgres migrations

* added mysql migrations

* formatting

* Bug Fixes & Formatting

* Formatting

* fixed a bug with bulk import when using MySql

* code review fixes

* fixed the build

* implemented new methods

* formatting

* fixed the build

* cleaned up select statements in ef queries

* formatting

* formatting

* formatting

Co-authored-by: Daniel James Smith <djsmith85@users.noreply.github.com>
This commit is contained in:
Addison Beck 2021-07-08 12:35:48 -04:00 committed by GitHub
parent be13eb153a
commit b13dda2799
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
251 changed files with 21099 additions and 608 deletions

View File

@ -1,8 +1,8 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<Version>1.41.5</Version> <Version>1.41.5</Version>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace> <RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace>
</PropertyGroup> </PropertyGroup>

View File

@ -25,7 +25,7 @@ Please read the [Setup guide](https://github.com/bitwarden/server/blob/master/SE
### Requirements ### Requirements
- [.NET Core 3.1 SDK](https://www.microsoft.com/net/download/core) - [.NET Core 5.0 SDK](https://www.microsoft.com/net/download/core)
- [SQL Server 2017](https://docs.microsoft.com/en-us/sql/index) - [SQL Server 2017](https://docs.microsoft.com/en-us/sql/index)
*These dependencies are free to use.* *These dependencies are free to use.*

View File

@ -91,7 +91,7 @@ User secrets are a method for managing application settings on a per-developer b
User secrets override the settings in `appsettings.json` of each project. Your user secrets file should match the structure of the `appsettings.json` file for the settings you intend to override. User secrets override the settings in `appsettings.json` of each project. Your user secrets file should match the structure of the `appsettings.json` file for the settings you intend to override.
For more information, see: [Safe storage of app secrets in development in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-3.1). For more information, see: [Safe storage of app secrets in development in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-5.0).
Open the server solution file (`bitwarden-server.sln`) in Visual Studio before proceeding. Open the server solution file (`bitwarden-server.sln`) in Visual Studio before proceeding.

View File

@ -72,6 +72,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommCore.Test", "bitwarden_
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test - Bitwarden License", "test - Bitwarden License", "{287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test - Bitwarden License", "test - Bitwarden License", "{287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MySqlMigrations", "util\MySqlMigrations\MySqlMigrations.csproj", "{BDC1D592-5947-47ED-9903-7CDBB12A50C8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PostgresMigrations", "util\PostgresMigrations\PostgresMigrations.csproj", "{F72E0229-2EF7-49B3-9004-FF4C0043816E}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -152,6 +156,15 @@ Global
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}.Debug|Any CPU.Build.0 = Debug|Any CPU {C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}.Release|Any CPU.ActiveCfg = Release|Any CPU {C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}.Release|Any CPU.Build.0 = Release|Any CPU {C7BA2255-C1B1-4789-8BB9-C27540DA6FB8}.Release|Any CPU.Build.0 = Release|Any CPU
{BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BDC1D592-5947-47ED-9903-7CDBB12A50C8}.Release|Any CPU.Build.0 = Release|Any CPU
{F72E0229-2EF7-49B3-9004-FF4C0043816E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F72E0229-2EF7-49B3-9004-FF4C0043816E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F72E0229-2EF7-49B3-9004-FF4C0043816E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F72E0229-2EF7-49B3-9004-FF4C0043816E}.Release|Any CPU.Build.0 = Release|Any CPU
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Debug|Any CPU.Build.0 = Debug|Any CPU {EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Release|Any CPU.ActiveCfg = Release|Any CPU {EDC0D688-D58C-4CE1-AA07-3606AC6874B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -183,6 +196,8 @@ Global
{BA852F18-852F-4154-973B-77D577B8CA04} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A} {BA852F18-852F-4154-973B-77D577B8CA04} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}
{4866AF64-6640-4C65-A662-A31E02FF9064} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A} {4866AF64-6640-4C65-A662-A31E02FF9064} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}
{C7BA2255-C1B1-4789-8BB9-C27540DA6FB8} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F} {C7BA2255-C1B1-4789-8BB9-C27540DA6FB8} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
{BDC1D592-5947-47ED-9903-7CDBB12A50C8} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
{F72E0229-2EF7-49B3-9004-FF4C0043816E} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
{EDC0D688-D58C-4CE1-AA07-3606AC6874B8} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A} {EDC0D688-D58C-4CE1-AA07-3606AC6874B8} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}
{0E99A21B-684B-4C59-9831-90F775CAB6F7} = {287CFF34-BBDB-4BC4-AF88-1E19A5A4679B} {0E99A21B-684B-4C59-9831-90F775CAB6F7} = {287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}
EndGlobalSection EndGlobalSection

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 FROM mcr.microsoft.com/dotnet/aspnet:5.0
LABEL com.bitwarden.product="bitwarden" LABEL com.bitwarden.product="bitwarden"

View File

@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 FROM mcr.microsoft.com/dotnet/aspnet:5.0
LABEL com.bitwarden.product="bitwarden" LABEL com.bitwarden.product="bitwarden"

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>

View File

@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 FROM mcr.microsoft.com/dotnet/aspnet:5.0
LABEL com.bitwarden.product="bitwarden" LABEL com.bitwarden.product="bitwarden"

View File

@ -28,7 +28,7 @@
</Choose> </Choose>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.6" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.2" />
<PackageReference Include="NewRelic.Agent" Version="8.30.0" /> <PackageReference Include="NewRelic.Agent" Version="8.30.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.1" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.1" />
<PackageReference Include="Microsoft.Azure.EventGrid" Version="3.2.0" /> <PackageReference Include="Microsoft.Azure.EventGrid" Version="3.2.0" />

View File

@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 FROM mcr.microsoft.com/dotnet/aspnet:5.0
LABEL com.bitwarden.product="bitwarden" LABEL com.bitwarden.product="bitwarden"

View File

@ -27,18 +27,20 @@
<PackageReference Include="Fido2.AspNet" Version="1.1.0" /> <PackageReference Include="Fido2.AspNet" Version="1.1.0" />
<PackageReference Include="Handlebars.Net" Version="1.10.1" /> <PackageReference Include="Handlebars.Net" Version="1.10.1" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" /> <PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="linq2db.EntityFrameworkCore" Version="5.2.1" />
<PackageReference Include="MailKit" Version="2.8.0" /> <PackageReference Include="MailKit" Version="2.8.0" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.AzureStorage" Version="3.1.6" /> <PackageReference Include="Microsoft.AspNetCore.DataProtection.AzureStorage" Version="3.1.16" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.6" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.2" />
<PackageReference Include="Microsoft.Azure.Cosmos.Table" Version="1.0.7" /> <PackageReference Include="Microsoft.Azure.Cosmos.Table" Version="1.0.7" />
<PackageReference Include="Microsoft.Azure.NotificationHubs" Version="3.3.0" /> <PackageReference Include="Microsoft.Azure.NotificationHubs" Version="3.3.0" />
<PackageReference Include="Microsoft.Azure.ServiceBus" Version="4.1.3" /> <PackageReference Include="Microsoft.Azure.ServiceBus" Version="4.1.3" />
<PackageReference Include="Microsoft.Azure.Storage.Blob" Version="11.1.7" /> <PackageReference Include="Microsoft.Azure.Storage.Blob" Version="11.1.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="3.1.6" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.6" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="5.0.0" />
<PackageReference Include="Npgsql" Version="4.1.4" /> <PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="5.0.2" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.4" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.2" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="5.0.0" />
<PackageReference Include="Quartz" Version="3.1.0" /> <PackageReference Include="Quartz" Version="3.1.0" />
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" /> <PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" /> <PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />

View File

@ -0,0 +1,9 @@
namespace Bit.Core.Enums
{
public enum CipherStateAction
{
Restore,
SoftDelete,
HardDelete,
}
}

View File

@ -0,0 +1,10 @@
namespace Bit.Core.Enums
{
public enum SupportedDatabaseProviders
{
SqlServer,
MySql,
Postgres,
}
}

View File

@ -52,8 +52,8 @@ namespace Bit.Core.Models.Api
public bool UseBusinessPortal => UsePolicies || UseSso; // TODO add events if needed public bool UseBusinessPortal => UsePolicies || UseSso; // TODO add events if needed
public bool UsersGetPremium { get; set; } public bool UsersGetPremium { get; set; }
public bool SelfHost { get; set; } public bool SelfHost { get; set; }
public int Seats { get; set; } public int? Seats { get; set; }
public int MaxCollections { get; set; } public short? MaxCollections { get; set; }
public short? MaxStorageGb { get; set; } public short? MaxStorageGb { get; set; }
public string Key { get; set; } public string Key { get; set; }
public OrganizationUserStatusType Status { get; set; } public OrganizationUserStatusType Status { get; set; }

View File

@ -0,0 +1,10 @@
using System.Data;
using Bit.Core.Models.Table;
namespace Bit.Core.Models.Data
{
public class GroupWithCollections : Group
{
public DataTable Collections { get; set; }
}
}

View File

@ -19,8 +19,8 @@ namespace Bit.Core.Models.Data
public bool UseBusinessPortal => UsePolicies || UseSso; public bool UseBusinessPortal => UsePolicies || UseSso;
public bool SelfHost { get; set; } public bool SelfHost { get; set; }
public bool UsersGetPremium { get; set; } public bool UsersGetPremium { get; set; }
public int Seats { get; set; } public int? Seats { get; set; }
public int MaxCollections { get; set; } public short? MaxCollections { get; set; }
public short? MaxStorageGb { get; set; } public short? MaxStorageGb { get; set; }
public string Key { get; set; } public string Key { get; set; }
public Enums.OrganizationUserStatusType Status { get; set; } public Enums.OrganizationUserStatusType Status { get; set; }

View File

@ -0,0 +1,10 @@
using System.Data;
using Bit.Core.Models.Table;
namespace Bit.Core.Models.Data
{
public class OrganizationUserWithCollections : OrganizationUser
{
public DataTable Collections { get; set; }
}
}

View File

@ -1,57 +1,14 @@
using System.Text.Json; using System.Collections.Generic;
using System.Text.Json;
using AutoMapper; using AutoMapper;
namespace Bit.Core.Models.EntityFramework namespace Bit.Core.Models.EntityFramework
{ {
public class Cipher : Table.Cipher public class Cipher : Table.Cipher
{ {
private JsonDocument _dataJson; public virtual User User { get; set; }
private JsonDocument _attachmentsJson; public virtual Organization Organization { get; set; }
private JsonDocument _favoritesJson; public virtual ICollection<CollectionCipher> CollectionCiphers { get; set; }
private JsonDocument _foldersJson;
public User User { get; set; }
public Organization Organization { get; set; }
[IgnoreMap]
public JsonDocument DataJson
{
get => _dataJson;
set
{
Data = value?.ToString();
_dataJson = value;
}
}
[IgnoreMap]
public JsonDocument AttachmentsJson
{
get => _attachmentsJson;
set
{
Attachments = value?.ToString();
_attachmentsJson = value;
}
}
[IgnoreMap]
public JsonDocument FavoritesJson
{
get => _favoritesJson;
set
{
Favorites = value?.ToString();
_favoritesJson = value;
}
}
[IgnoreMap]
public JsonDocument FoldersJson
{
get => _foldersJson;
set
{
Folders = value?.ToString();
_foldersJson = value;
}
}
} }
public class CipherMapperProfile : Profile public class CipherMapperProfile : Profile

View File

@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class Collection : Table.Collection
{
public virtual Organization Organization { get; set; }
public virtual ICollection<CollectionUser> CollectionUsers { get; set; }
public virtual ICollection<CollectionCipher> CollectionCiphers { get; set; }
public virtual ICollection<CollectionGroup> CollectionGroups { get; set; }
}
public class CollectionMapperProfile : Profile
{
public CollectionMapperProfile()
{
CreateMap<Table.Collection, Collection>().ReverseMap();
}
}
}

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class CollectionCipher : Table.CollectionCipher
{
public virtual Cipher Cipher { get; set; }
public virtual Collection Collection { get; set; }
}
public class CollectionCipherMapperProfile : Profile
{
public CollectionCipherMapperProfile()
{
CreateMap<Table.CollectionCipher, CollectionCipher>().ReverseMap();
}
}
}

View File

@ -0,0 +1,18 @@
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class CollectionGroup : Table.CollectionGroup
{
public virtual Collection Collection { get; set; }
public virtual Group Group { get; set; }
}
public class CollectionGroupMapperProfile : Profile
{
public CollectionGroupMapperProfile()
{
CreateMap<Table.CollectionGroup, CollectionGroup>().ReverseMap();
}
}
}

View File

@ -0,0 +1,18 @@
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class CollectionUser : Table.CollectionUser
{
public virtual Collection Collection { get; set; }
public virtual OrganizationUser OrganizationUser { get; set; }
}
public class CollectionUserMapperProfile : Profile
{
public CollectionUserMapperProfile()
{
CreateMap<Table.CollectionUser, CollectionUser>().ReverseMap();
}
}
}

View File

@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class Device : Table.Device
{
public virtual User User { get; set; }
}
public class DeviceMapperProfile : Profile
{
public DeviceMapperProfile()
{
CreateMap<Table.Device, Device>().ReverseMap();
}
}
}

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class EmergencyAccess : Table.EmergencyAccess
{
public virtual User Grantee { get; set; }
public virtual User Grantor { get; set; }
}
public class EmergencyAccessMapperProfile : Profile
{
public EmergencyAccessMapperProfile()
{
CreateMap<Table.EmergencyAccess, EmergencyAccess>().ReverseMap();
}
}
}

View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class Event : Table.Event
{
}
public class EventMapperProfile : Profile
{
public EventMapperProfile()
{
CreateMap<Table.Event, Event>().ReverseMap();
}
}
}

View File

@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class Folder : Table.Folder
{
public virtual User User { get; set; }
}
public class FolderMapperProfile : Profile
{
public FolderMapperProfile()
{
CreateMap<Table.Folder, Folder>().ReverseMap();
}
}
}

View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class Grant : Table.Grant
{
}
public class GrantMapperProfile : Profile
{
public GrantMapperProfile()
{
CreateMap<Table.Grant, Grant>().ReverseMap();
}
}
}

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class Group : Table.Group
{
public virtual Organization Organization { get; set; }
public virtual ICollection<GroupUser> GroupUsers { get; set; }
}
public class GroupMapperProfile : Profile
{
public GroupMapperProfile()
{
CreateMap<Table.Group, Group>().ReverseMap();
}
}
}

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class GroupUser : Table.GroupUser
{
public virtual Group Group { get; set; }
public virtual OrganizationUser OrganizationUser { get; set; }
}
public class GroupUserMapperProfile : Profile
{
public GroupUserMapperProfile()
{
CreateMap<Table.GroupUser, GroupUser>().ReverseMap();
}
}
}

View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class Installation : Table.Installation
{
}
public class InstallationMapperProfile : Profile
{
public InstallationMapperProfile()
{
CreateMap<Table.Installation, Installation>().ReverseMap();
}
}
}

View File

@ -1,25 +1,17 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json;
using AutoMapper; using AutoMapper;
namespace Bit.Core.Models.EntityFramework namespace Bit.Core.Models.EntityFramework
{ {
public class Organization : Table.Organization public class Organization : Table.Organization
{ {
private JsonDocument _twoFactorProvidersJson; public virtual ICollection<Cipher> Ciphers { get; set; }
public virtual ICollection<OrganizationUser> OrganizationUsers { get; set; }
public ICollection<Cipher> Ciphers { get; set; } public virtual ICollection<Group> Groups { get; set; }
public virtual ICollection<Policy> Policies { get; set; }
[IgnoreMap] public virtual ICollection<SsoConfig> SsoConfigs { get; set; }
public JsonDocument TwoFactorProvidersJson public virtual ICollection<SsoUser> SsoUsers { get; set; }
{ public virtual ICollection<Transaction> Transactions { get; set; }
get => _twoFactorProvidersJson;
set
{
TwoFactorProviders = value?.ToString();
_twoFactorProvidersJson = value;
}
}
} }
public class OrganizationMapperProfile : Profile public class OrganizationMapperProfile : Profile

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class OrganizationUser : Table.OrganizationUser
{
public virtual Organization Organization { get; set; }
public virtual User User { get; set; }
public virtual ICollection<CollectionUser> CollectionUsers { get; set; }
}
public class OrganizationUserMapperProfile : Profile
{
public OrganizationUserMapperProfile()
{
CreateMap<Table.OrganizationUser, OrganizationUser>().ReverseMap();
}
}
}

View File

@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class Policy : Table.Policy
{
public virtual Organization Organization { get; set; }
}
public class PolicyMapperProfile : Profile
{
public PolicyMapperProfile()
{
CreateMap<Table.Policy, Policy>().ReverseMap();
}
}
}

View File

@ -0,0 +1,16 @@
using AutoMapper;
namespace Bit.Core.Models.EntityFramework.Provider
{
public class Provider : Table.Provider.Provider
{
}
public class ProviderMapperProfile : Profile
{
public ProviderMapperProfile()
{
CreateMap<Table.Provider.Provider, Provider>().ReverseMap();
}
}
}

View File

@ -0,0 +1,18 @@
using AutoMapper;
namespace Bit.Core.Models.EntityFramework.Provider
{
public class ProviderOrganization : Table.Provider.ProviderOrganization
{
public virtual Provider Provider { get; set; }
public virtual Organization Organization { get; set; }
}
public class ProviderOrganizationMapperProfile : Profile
{
public ProviderOrganizationMapperProfile()
{
CreateMap<Table.Provider.ProviderOrganization, ProviderOrganization>().ReverseMap();
}
}
}

View File

@ -0,0 +1,18 @@
using AutoMapper;
namespace Bit.Core.Models.EntityFramework.Provider
{
public class ProviderOrganizationProviderUser : Table.Provider.ProviderOrganizationProviderUser
{
public virtual ProviderOrganization ProviderOrganization { get; set; }
public virtual ProviderUser ProviderUser { get; set; }
}
public class ProviderOrganizationProviderUserMapperProfile : Profile
{
public ProviderOrganizationProviderUserMapperProfile()
{
CreateMap<Table.Provider.ProviderOrganizationProviderUser, ProviderOrganizationProviderUser>().ReverseMap();
}
}
}

View File

@ -0,0 +1,18 @@
using AutoMapper;
namespace Bit.Core.Models.EntityFramework.Provider
{
public class ProviderUser : Table.Provider.ProviderUser
{
public virtual User User { get; set; }
public virtual Provider Provider { get; set; }
}
public class ProviderUserMapperProfile : Profile
{
public ProviderUserMapperProfile()
{
CreateMap<Table.Provider.ProviderUser, ProviderUser>().ReverseMap();
}
}
}

View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class Role : Table.Role
{
}
public class RoleMapperProfile : Profile
{
public RoleMapperProfile()
{
CreateMap<Table.Role, Role>().ReverseMap();
}
}
}

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class Send : Table.Send
{
public virtual Organization Organization { get; set; }
public virtual User User { get; set; }
}
public class SendMapperProfile : Profile
{
public SendMapperProfile()
{
CreateMap<Table.Send, Send>().ReverseMap();
}
}
}

View File

@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class SsoConfig : Table.SsoConfig
{
public virtual Organization Organization { get; set; }
}
public class SsoConfigMapperProfile : Profile
{
public SsoConfigMapperProfile()
{
CreateMap<Table.SsoConfig, SsoConfig>().ReverseMap();
}
}
}

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class SsoUser : Table.SsoUser
{
public virtual Organization Organization { get; set; }
public virtual User User { get; set; }
}
public class SsoUserMapperProfile : Profile
{
public SsoUserMapperProfile()
{
CreateMap<Table.SsoUser, SsoUser>().ReverseMap();
}
}
}

View File

@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class TaxRate : Table.TaxRate
{
}
public class TaxRateMapperProfile : Profile
{
public TaxRateMapperProfile()
{
CreateMap<Table.TaxRate, TaxRate>().ReverseMap();
}
}
}

View File

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class Transaction : Table.Transaction
{
public virtual Organization Organization { get; set; }
public virtual User User { get; set; }
}
public class TransactionMapperProfile : Profile
{
public TransactionMapperProfile()
{
CreateMap<Table.Transaction, Transaction>().ReverseMap();
}
}
}

View File

@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Text.Json;
using AutoMapper;
namespace Bit.Core.Models.EntityFramework
{
public class U2f : Table.U2f
{
public virtual User User { get; set; }
}
public class U2fMapperProfile : Profile
{
public U2fMapperProfile()
{
CreateMap<Table.U2f, U2f>().ReverseMap();
}
}
}

View File

@ -6,20 +6,14 @@ namespace Bit.Core.Models.EntityFramework
{ {
public class User : Table.User public class User : Table.User
{ {
private JsonDocument _twoFactorProvidersJson; public virtual ICollection<Cipher> Ciphers { get; set; }
public virtual ICollection<Folder> Folders { get; set; }
public ICollection<Cipher> Ciphers { get; set; } public virtual ICollection<CollectionUser> CollectionUsers { get; set; }
public virtual ICollection<GroupUser> GroupUsers { get; set; }
[IgnoreMap] public virtual ICollection<OrganizationUser> OrganizationUsers { get; set; }
public JsonDocument TwoFactorProvidersJson public virtual ICollection<SsoUser> SsoUsers { get; set; }
{ public virtual ICollection<Transaction> Transactions { get; set; }
get => _twoFactorProvidersJson; public virtual ICollection<U2f> U2fs { get; set; }
set
{
TwoFactorProviders = value?.ToString();
_twoFactorProvidersJson = value;
}
}
} }
public class UserMapperProfile : Profile public class UserMapperProfile : Profile

View File

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Utilities; using Bit.Core.Utilities;
namespace Bit.Core.Models.Table namespace Bit.Core.Models.Table
@ -8,6 +9,7 @@ namespace Bit.Core.Models.Table
public Guid Id { get; set; } public Guid Id { get; set; }
public Guid OrganizationId { get; set; } public Guid OrganizationId { get; set; }
public string Name { get; set; } public string Name { get; set; }
[MaxLength(300)]
public string ExternalId { get; set; } public string ExternalId { get; set; }
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow; public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow; public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;

View File

@ -0,0 +1,13 @@
using System;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Table
{
public class CollectionGroup
{
public Guid CollectionId { get; set; }
public Guid GroupId { get; set; }
public bool ReadOnly { get; set; }
public bool HidePasswords { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Table
{
public class CollectionUser
{
public Guid CollectionId { get; set; }
public Guid OrganizationUserId { get; set; }
public bool ReadOnly { get; set; }
public bool HidePasswords { get; set; }
}
}

View File

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Utilities; using Bit.Core.Utilities;
namespace Bit.Core.Models.Table namespace Bit.Core.Models.Table
@ -7,9 +8,12 @@ namespace Bit.Core.Models.Table
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public Guid UserId { get; set; } public Guid UserId { get; set; }
[MaxLength(50)]
public string Name { get; set; } public string Name { get; set; }
public Enums.DeviceType Type { get; set; } public Enums.DeviceType Type { get; set; }
[MaxLength(50)]
public string Identifier { get; set; } public string Identifier { get; set; }
[MaxLength(255)]
public string PushToken { get; set; } public string PushToken { get; set; }
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow; public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow; public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;

View File

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Utilities; using Bit.Core.Utilities;
@ -9,6 +10,7 @@ namespace Bit.Core.Models.Table
public Guid Id { get; set; } public Guid Id { get; set; }
public Guid GrantorId { get; set; } public Guid GrantorId { get; set; }
public Guid? GranteeId { get; set; } public Guid? GranteeId { get; set; }
[MaxLength(256)]
public string Email { get; set; } public string Email { get; set; }
public string KeyEncrypted { get; set; } public string KeyEncrypted { get; set; }
public EmergencyAccessType Type { get; set; } public EmergencyAccessType Type { get; set; }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
using Bit.Core.Utilities; using Bit.Core.Utilities;
@ -36,6 +37,7 @@ namespace Bit.Core.Models.Table
public Guid? GroupId { get; set; } public Guid? GroupId { get; set; }
public Guid? OrganizationUserId { get; set; } public Guid? OrganizationUserId { get; set; }
public DeviceType? DeviceType { get; set; } public DeviceType? DeviceType { get; set; }
[MaxLength(50)]
public string IpAddress { get; set; } public string IpAddress { get; set; }
public Guid? ActingUserId { get; set; } public Guid? ActingUserId { get; set; }

View File

@ -1,14 +1,21 @@
using System; using System;
using System.ComponentModel.DataAnnotations;
namespace Bit.Core.Models.Table namespace Bit.Core.Models.Table
{ {
public class Grant public class Grant
{ {
[MaxLength(200)]
public string Key { get; set; } public string Key { get; set; }
[MaxLength(50)]
public string Type { get; set; } public string Type { get; set; }
[MaxLength(200)]
public string SubjectId { get; set; } public string SubjectId { get; set; }
[MaxLength(100)]
public string SessionId { get; set; } public string SessionId { get; set; }
[MaxLength(200)]
public string ClientId { get; set; } public string ClientId { get; set; }
[MaxLength(200)]
public string Description { get; set; } public string Description { get; set; }
public DateTime CreationDate { get; set; } public DateTime CreationDate { get; set; }
public DateTime? ExpirationDate { get; set; } public DateTime? ExpirationDate { get; set; }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Utilities; using Bit.Core.Utilities;
namespace Bit.Core.Models.Table namespace Bit.Core.Models.Table
@ -7,8 +8,10 @@ namespace Bit.Core.Models.Table
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public Guid OrganizationId { get; set; } public Guid OrganizationId { get; set; }
[MaxLength(100)]
public string Name { get; set; } public string Name { get; set; }
public bool AccessAll { get; set; } public bool AccessAll { get; set; }
[MaxLength(300)]
public string ExternalId { get; set; } public string ExternalId { get; set; }
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow; public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow; public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;

View File

@ -1,12 +1,15 @@
using Bit.Core.Utilities; using Bit.Core.Utilities;
using System; using System;
using System.ComponentModel.DataAnnotations;
namespace Bit.Core.Models.Table namespace Bit.Core.Models.Table
{ {
public class Installation : ITableObject<Guid> public class Installation : ITableObject<Guid>
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
[MaxLength(256)]
public string Email { get; set; } public string Email { get; set; }
[MaxLength(150)]
public string Key { get; set; } public string Key { get; set; }
public bool Enabled { get; set; } public bool Enabled { get; set; }
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow; public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;

View File

@ -4,6 +4,7 @@ using Bit.Core.Enums;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Linq; using System.Linq;
using System.ComponentModel.DataAnnotations;
namespace Bit.Core.Models.Table namespace Bit.Core.Models.Table
{ {
@ -12,15 +13,25 @@ namespace Bit.Core.Models.Table
private Dictionary<TwoFactorProviderType, TwoFactorProvider> _twoFactorProviders; private Dictionary<TwoFactorProviderType, TwoFactorProvider> _twoFactorProviders;
public Guid Id { get; set; } public Guid Id { get; set; }
[MaxLength(50)]
public string Identifier { get; set; } public string Identifier { get; set; }
[MaxLength(50)]
public string Name { get; set; } public string Name { get; set; }
[MaxLength(50)]
public string BusinessName { get; set; } public string BusinessName { get; set; }
[MaxLength(50)]
public string BusinessAddress1 { get; set; } public string BusinessAddress1 { get; set; }
[MaxLength(50)]
public string BusinessAddress2 { get; set; } public string BusinessAddress2 { get; set; }
[MaxLength(50)]
public string BusinessAddress3 { get; set; } public string BusinessAddress3 { get; set; }
[MaxLength(2)]
public string BusinessCountry { get; set; } public string BusinessCountry { get; set; }
[MaxLength(30)]
public string BusinessTaxNumber { get; set; } public string BusinessTaxNumber { get; set; }
[MaxLength(256)]
public string BillingEmail { get; set; } public string BillingEmail { get; set; }
[MaxLength(50)]
public string Plan { get; set; } public string Plan { get; set; }
public PlanType PlanType { get; set; } public PlanType PlanType { get; set; }
public int? Seats { get; set; } public int? Seats { get; set; }
@ -39,11 +50,15 @@ namespace Bit.Core.Models.Table
public long? Storage { get; set; } public long? Storage { get; set; }
public short? MaxStorageGb { get; set; } public short? MaxStorageGb { get; set; }
public GatewayType? Gateway { get; set; } public GatewayType? Gateway { get; set; }
[MaxLength(50)]
public string GatewayCustomerId { get; set; } public string GatewayCustomerId { get; set; }
[MaxLength(50)]
public string GatewaySubscriptionId { get; set; } public string GatewaySubscriptionId { get; set; }
public string ReferenceData { get; set; } public string ReferenceData { get; set; }
public bool Enabled { get; set; } = true; public bool Enabled { get; set; } = true;
[MaxLength(100)]
public string LicenseKey { get; set; } public string LicenseKey { get; set; }
[MaxLength(30)]
public string ApiKey { get; set; } public string ApiKey { get; set; }
public string PublicKey { get; set; } public string PublicKey { get; set; }
public string PrivateKey { get; set; } public string PrivateKey { get; set; }

View File

@ -1,6 +1,7 @@
using System; using System;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Bit.Core.Enums; using Bit.Core.Enums;
using System.ComponentModel.DataAnnotations;
namespace Bit.Core.Models.Table namespace Bit.Core.Models.Table
{ {
@ -9,12 +10,14 @@ namespace Bit.Core.Models.Table
public Guid Id { get; set; } public Guid Id { get; set; }
public Guid OrganizationId { get; set; } public Guid OrganizationId { get; set; }
public Guid? UserId { get; set; } public Guid? UserId { get; set; }
[MaxLength(256)]
public string Email { get; set; } public string Email { get; set; }
public string Key { get; set; } public string Key { get; set; }
public string ResetPasswordKey { get; set; } public string ResetPasswordKey { get; set; }
public OrganizationUserStatusType Status { get; set; } public OrganizationUserStatusType Status { get; set; }
public OrganizationUserType Type { get; set; } public OrganizationUserType Type { get; set; }
public bool AccessAll { get; set; } public bool AccessAll { get; set; }
[MaxLength(300)]
public string ExternalId { get; set; } public string ExternalId { get; set; }
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow; public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow; public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;

View File

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Utilities; using Bit.Core.Utilities;
@ -12,6 +13,7 @@ namespace Bit.Core.Models.Table
public SendType Type { get; set; } public SendType Type { get; set; }
public string Data { get; set; } public string Data { get; set; }
public string Key { get; set; } public string Key { get; set; }
[MaxLength(300)]
public string Password { get; set; } public string Password { get; set; }
public int? MaxAccessCount { get; set; } public int? MaxAccessCount { get; set; }
public int AccessCount { get; set; } public int AccessCount { get; set; }

View File

@ -13,7 +13,8 @@ namespace Bit.Core.Models.Table
public void SetNewId() public void SetNewId()
{ {
// nothing - int will be auto-populated // int will be auto-populated
Id = 0;
} }
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel.DataAnnotations;
namespace Bit.Core.Models.Table namespace Bit.Core.Models.Table
{ {
@ -7,12 +8,14 @@ namespace Bit.Core.Models.Table
public long Id { get; set; } public long Id { get; set; }
public Guid UserId { get; set; } public Guid UserId { get; set; }
public Guid? OrganizationId { get; set; } public Guid? OrganizationId { get; set; }
[MaxLength(50)]
public string ExternalId { get; set; } public string ExternalId { get; set; }
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow; public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
public void SetNewId() public void SetNewId()
{ {
// nothing - int will be auto-populated // int will be auto-populated
Id = 0;
} }
} }
} }

View File

@ -1,10 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace Bit.Core.Models.Table namespace Bit.Core.Models.Table
{ {
public class TaxRate: ITableObject<string> public class TaxRate: ITableObject<string>
{ {
[MaxLength(40)]
public string Id { get; set; } public string Id { get; set; }
[MaxLength(50)]
public string Country { get; set; } public string Country { get; set; }
[MaxLength(2)]
public string State { get; set; } public string State { get; set; }
[MaxLength(10)]
public string PostalCode { get; set; } public string PostalCode { get; set; }
public decimal Rate { get; set; } public decimal Rate { get; set; }
public bool Active { get; set; } public bool Active { get; set; }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Utilities; using Bit.Core.Utilities;
@ -13,9 +14,11 @@ namespace Bit.Core.Models.Table
public decimal Amount { get; set; } public decimal Amount { get; set; }
public bool? Refunded { get; set; } public bool? Refunded { get; set; }
public decimal? RefundedAmount { get; set; } public decimal? RefundedAmount { get; set; }
[MaxLength(100)]
public string Details { get; set; } public string Details { get; set; }
public PaymentMethodType? PaymentMethodType { get; set; } public PaymentMethodType? PaymentMethodType { get; set; }
public GatewayType? Gateway { get; set; } public GatewayType? Gateway { get; set; }
[MaxLength(50)]
public string GatewayId { get; set; } public string GatewayId { get; set; }
public DateTime CreationDate { get; set; } = DateTime.UtcNow; public DateTime CreationDate { get; set; } = DateTime.UtcNow;

View File

@ -1,4 +1,5 @@
using System; using System;
using System.ComponentModel.DataAnnotations;
namespace Bit.Core.Models.Table namespace Bit.Core.Models.Table
{ {
@ -6,15 +7,20 @@ namespace Bit.Core.Models.Table
{ {
public int Id { get; set; } public int Id { get; set; }
public Guid UserId { get; set; } public Guid UserId { get; set; }
[MaxLength(200)]
public string KeyHandle { get; set; } public string KeyHandle { get; set; }
[MaxLength(200)]
public string Challenge { get; set; } public string Challenge { get; set; }
[MaxLength(50)]
public string AppId { get; set; } public string AppId { get; set; }
[MaxLength(20)]
public string Version { get; set; } public string Version { get; set; }
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow; public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
public void SetNewId() public void SetNewId()
{ {
// do nothing since it is an identity // int will be auto-populated
Id = 0;
} }
} }
} }

View File

@ -4,6 +4,7 @@ using Bit.Core.Utilities;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json; using Newtonsoft.Json;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations;
namespace Bit.Core.Models.Table namespace Bit.Core.Models.Table
{ {
@ -12,14 +13,23 @@ namespace Bit.Core.Models.Table
private Dictionary<TwoFactorProviderType, TwoFactorProvider> _twoFactorProviders; private Dictionary<TwoFactorProviderType, TwoFactorProvider> _twoFactorProviders;
public Guid Id { get; set; } public Guid Id { get; set; }
[MaxLength(50)]
public string Name { get; set; } public string Name { get; set; }
[Required]
[MaxLength(256)]
public string Email { get; set; } public string Email { get; set; }
public bool EmailVerified { get; set; } public bool EmailVerified { get; set; }
[MaxLength(300)]
public string MasterPassword { get; set; } public string MasterPassword { get; set; }
[MaxLength(50)]
public string MasterPasswordHint { get; set; } public string MasterPasswordHint { get; set; }
[MaxLength(10)]
public string Culture { get; set; } = "en-US"; public string Culture { get; set; } = "en-US";
[Required]
[MaxLength(50)]
public string SecurityStamp { get; set; } public string SecurityStamp { get; set; }
public string TwoFactorProviders { get; set; } public string TwoFactorProviders { get; set; }
[MaxLength(32)]
public string TwoFactorRecoveryCode { get; set; } public string TwoFactorRecoveryCode { get; set; }
public string EquivalentDomains { get; set; } public string EquivalentDomains { get; set; }
public string ExcludedGlobalEquivalentDomains { get; set; } public string ExcludedGlobalEquivalentDomains { get; set; }
@ -33,10 +43,15 @@ namespace Bit.Core.Models.Table
public long? Storage { get; set; } public long? Storage { get; set; }
public short? MaxStorageGb { get; set; } public short? MaxStorageGb { get; set; }
public GatewayType? Gateway { get; set; } public GatewayType? Gateway { get; set; }
[MaxLength(50)]
public string GatewayCustomerId { get; set; } public string GatewayCustomerId { get; set; }
[MaxLength(50)]
public string GatewaySubscriptionId { get; set; } public string GatewaySubscriptionId { get; set; }
public string ReferenceData { get; set; } public string ReferenceData { get; set; }
[MaxLength(100)]
public string LicenseKey { get; set; } public string LicenseKey { get; set; }
[Required]
[MaxLength(30)]
public string ApiKey { get; set; } public string ApiKey { get; set; }
public KdfType Kdf { get; set; } = KdfType.PBKDF2_SHA256; public KdfType Kdf { get; set; } = KdfType.PBKDF2_SHA256;
public int KdfIterations { get; set; } = 5000; public int KdfIterations { get; set; } = 5000;

View File

@ -1,10 +1,28 @@
using AutoMapper; using AutoMapper;
using Bit.Core.Models.Table;
using Bit.Core.Repositories.EntityFramework.Queries;
using EfModel = Bit.Core.Models.EntityFramework;
using LinqToDB.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using System;
using Bit.Core.Enums;
using Bit.Core.Enums.Provider;
namespace Bit.Core.Repositories.EntityFramework namespace Bit.Core.Repositories.EntityFramework
{ {
public abstract class BaseEntityFrameworkRepository public abstract class BaseEntityFrameworkRepository
{ {
protected BulkCopyOptions DefaultBulkCopyOptions { get; set; } = new BulkCopyOptions
{
KeepIdentity = true,
BulkCopyType = BulkCopyType.MultipleRows,
};
public BaseEntityFrameworkRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) public BaseEntityFrameworkRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
{ {
ServiceScopeFactory = serviceScopeFactory; ServiceScopeFactory = serviceScopeFactory;
@ -18,5 +36,233 @@ namespace Bit.Core.Repositories.EntityFramework
{ {
return serviceScope.ServiceProvider.GetRequiredService<DatabaseContext>(); return serviceScope.ServiceProvider.GetRequiredService<DatabaseContext>();
} }
public void ClearChangeTracking()
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
dbContext.ChangeTracker.Clear();
}
}
public async Task<int> GetCountFromQuery<T>(IQuery<T> query)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
return await query.Run(GetDatabaseContext(scope)).CountAsync();
}
}
protected async Task UserBumpAccountRevisionDateByCipherId(Cipher cipher)
{
var list = new List<Cipher> { cipher };
await UserBumpAccountRevisionDateByCipherId(list);
}
protected async Task UserBumpAccountRevisionDateByCipherId(IEnumerable<Cipher> ciphers)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
foreach (var cipher in ciphers)
{
var dbContext = GetDatabaseContext(scope);
var query = new UserBumpAccountRevisionDateByCipherIdQuery(cipher);
var users = query.Run(dbContext);
await users.ForEachAsync(e =>
{
dbContext.Attach(e);
e.RevisionDate = DateTime.UtcNow;
});
await dbContext.SaveChangesAsync();
}
}
}
protected async Task UserBumpAccountRevisionDateByOrganizationId(Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new UserBumpAccountRevisionDateByOrganizationIdQuery(organizationId);
var users = query.Run(dbContext);
await users.ForEachAsync(e =>
{
dbContext.Attach(e);
e.RevisionDate = DateTime.UtcNow;
});
await dbContext.SaveChangesAsync();
}
}
protected async Task UserBumpAccountRevisionDate(Guid userId)
{
await UserBumpManyAccountRevisionDates(new[] { userId });
}
protected async Task UserBumpManyAccountRevisionDates(ICollection<Guid> userIds)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var users = dbContext.Users.Where(u => userIds.Contains(u.Id));
await users.ForEachAsync(u =>
{
dbContext.Attach(u);
u.RevisionDate = DateTime.UtcNow;
});
await dbContext.SaveChangesAsync();
}
}
protected async Task OrganizationUpdateStorage(Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var attachments = await dbContext.Ciphers
.Where(e => e.UserId == null &&
e.OrganizationId == organizationId &&
!string.IsNullOrWhiteSpace(e.Attachments))
.Select(e => e.Attachments)
.ToListAsync();
var storage = attachments.Sum(e => JsonDocument.Parse(e)?.RootElement.EnumerateArray()
.Sum(p => p.GetProperty("Size").GetInt64()) ?? 0);
var organization = new EfModel.Organization
{
Id = organizationId,
RevisionDate = DateTime.UtcNow,
Storage = storage,
};
dbContext.Organizations.Attach(organization);
var entry = dbContext.Entry(organization);
entry.Property(e => e.RevisionDate).IsModified = true;
entry.Property(e => e.Storage).IsModified = true;
await dbContext.SaveChangesAsync();
}
}
protected async Task UserUpdateStorage(Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var attachments = await dbContext.Ciphers
.Where(e => e.UserId.HasValue &&
e.UserId.Value == userId &&
e.OrganizationId == null &&
!string.IsNullOrWhiteSpace(e.Attachments))
.Select(e => e.Attachments)
.ToListAsync();
var storage = attachments.Sum(e => JsonDocument.Parse(e)?.RootElement.EnumerateArray()
.Sum(p => p.GetProperty("Size").GetInt64()) ?? 0);
var user = new EfModel.User
{
Id = userId,
RevisionDate = DateTime.UtcNow,
Storage = storage,
};
dbContext.Users.Attach(user);
var entry = dbContext.Entry(user);
entry.Property(e => e.RevisionDate).IsModified = true;
entry.Property(e => e.Storage).IsModified = true;
await dbContext.SaveChangesAsync();
}
}
protected async Task UserUpdateKeys(User user)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var entity = await dbContext.Users.FindAsync(user.Id);
if (entity == null)
{
return;
}
entity.SecurityStamp = user.SecurityStamp;
entity.Key = user.Key;
entity.PrivateKey = user.PrivateKey;
entity.RevisionDate = DateTime.UtcNow;
await dbContext.SaveChangesAsync();
}
}
protected async Task UserBumpAccountRevisionDateByCollectionId(Guid collectionId, Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from u in dbContext.Users
join ou in dbContext.OrganizationUsers
on u.Id equals ou.UserId
join cu in dbContext.CollectionUsers
on ou.Id equals cu.OrganizationUserId into cu_g
from cu in cu_g.DefaultIfEmpty()
where !ou.AccessAll && cu.CollectionId.Equals(collectionId)
join gu in dbContext.GroupUsers
on ou.Id equals gu.OrganizationUserId into gu_g
from gu in gu_g.DefaultIfEmpty()
where cu.CollectionId == default(Guid) && !ou.AccessAll
join g in dbContext.Groups
on gu.GroupId equals g.Id into g_g
from g in g_g.DefaultIfEmpty()
join cg in dbContext.CollectionGroups
on gu.GroupId equals cg.GroupId into cg_g
from cg in cg_g.DefaultIfEmpty()
where !g.AccessAll && cg.CollectionId == collectionId &&
(ou.OrganizationId == organizationId && ou.Status == OrganizationUserStatusType.Confirmed &&
(cu.CollectionId != default(Guid) || cg.CollectionId != default(Guid) || ou.AccessAll || g.AccessAll))
select new { u, ou, cu, gu, g, cg };
var users = query.Select(x => x.u);
await users.ForEachAsync(u =>
{
dbContext.Attach(u);
u.RevisionDate = DateTime.UtcNow;
});
await dbContext.SaveChangesAsync();
}
}
protected async Task UserBumpAccountRevisionDateByOrganizationUserId(Guid organizationUserId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from u in dbContext.Users
join ou in dbContext.OrganizationUsers
on u.Id equals ou.UserId
where ou.Id.Equals(organizationUserId) && ou.Status.Equals(OrganizationUserStatusType.Confirmed)
select new { u, ou };
var users = query.Select(x => x.u);
await users.ForEachAsync(u =>
{
dbContext.Attach(u);
u.AccountRevisionDate = DateTime.UtcNow;
});
await dbContext.SaveChangesAsync();
}
}
protected async Task UserBumpAccountRevisionDateByProviderUserIds(ICollection<Guid> providerUserIds)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from pu in dbContext.ProviderUsers
join u in dbContext.Users
on pu.UserId equals u.Id
where pu.Status.Equals(ProviderUserStatusType.Confirmed) &&
providerUserIds.Contains(pu.Id)
select new { pu, u };
var users = query.Select(x => x.u);
await users.ForEachAsync(u => {
dbContext.Attach(u);
u.AccountRevisionDate = DateTime.UtcNow;
});
await dbContext.SaveChangesAsync();
}
}
} }
} }

View File

@ -0,0 +1,642 @@
using AutoMapper;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table;
using Bit.Core.Repositories.EntityFramework.Queries;
using Bit.Core.Utilities;
using Core.Models.Data;
using EfModel = Bit.Core.Models.EntityFramework;
using LinqToDB.Data;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System;
using TableModel = Bit.Core.Models.Table;
using Bit.Core.Enums;
namespace Bit.Core.Repositories.EntityFramework
{
public class CipherRepository : Repository<TableModel.Cipher, EfModel.Cipher, Guid>, ICipherRepository
{
public CipherRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Ciphers)
{ }
public override async Task<Cipher> CreateAsync(Cipher cipher)
{
cipher = await base.CreateAsync(cipher);
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
if (cipher.OrganizationId.HasValue)
{
await UserBumpAccountRevisionDateByCipherId(cipher);
}
else if (cipher.UserId.HasValue)
{
await UserBumpAccountRevisionDate(cipher.UserId.Value);
}
}
return cipher;
}
public IQueryable<User> GetBumpedAccountsByCipherId(Cipher cipher)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new UserBumpAccountRevisionDateByCipherIdQuery(cipher);
return query.Run(dbContext);
}
}
public async Task CreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
{
cipher = await base.CreateAsync(cipher);
await UpdateCollections(cipher, collectionIds);
}
private async Task UpdateCollections(Cipher cipher, IEnumerable<Guid> collectionIds)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var cipherEntity = await dbContext.Ciphers.FindAsync(cipher.Id);
var query = new CipherUpdateCollectionsQuery(cipherEntity, collectionIds).Run(dbContext);
await dbContext.AddRangeAsync(query);
await dbContext.SaveChangesAsync();
}
}
public async Task CreateAsync(CipherDetails cipher)
{
await CreateAsyncReturnCipher(cipher);
}
private async Task<CipherDetails> CreateAsyncReturnCipher(CipherDetails cipher)
{
cipher.SetNewId();
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var userIdKey = $"\"{cipher.UserId}\"";
cipher.UserId = cipher.OrganizationId.HasValue ? null : cipher.UserId;
cipher.Favorites = cipher.Favorite ?
$"{{{userIdKey}:true}}" :
null;
cipher.Folders = cipher.FolderId.HasValue ?
$"{{{userIdKey}:\"{cipher.FolderId}\"}}" :
null;
var entity = Mapper.Map<EfModel.Cipher>((TableModel.Cipher)cipher);
await dbContext.AddAsync(entity);
await dbContext.SaveChangesAsync();
}
await UserBumpAccountRevisionDateByCipherId(cipher);
return cipher;
}
public async Task CreateAsync(CipherDetails cipher, IEnumerable<Guid> collectionIds)
{
cipher = await CreateAsyncReturnCipher(cipher);
await UpdateCollections(cipher, collectionIds);
}
public async Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders)
{
if (!ciphers.Any())
{
return;
}
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var folderEntities = Mapper.Map<List<EfModel.Folder>>(folders);
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, folderEntities);
var cipherEntities = Mapper.Map<List<EfModel.Cipher>>(ciphers);
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, cipherEntities);
await UserBumpAccountRevisionDateByCipherId(ciphers);
}
}
public async Task CreateAsync(IEnumerable<Cipher> ciphers, IEnumerable<Collection> collections, IEnumerable<CollectionCipher> collectionCiphers)
{
if (!ciphers.Any())
{
return;
}
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var cipherEntities = Mapper.Map<List<EfModel.Cipher>>(ciphers);
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, cipherEntities);
if (collections.Any())
{
var collectionEntities = Mapper.Map<List<EfModel.Collection>>(collections);
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, collectionEntities);
if (collectionCiphers.Any())
{
var collectionCipherEntities = Mapper.Map<List<EfModel.CollectionCipher>>(collectionCiphers);
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, collectionCipherEntities);
}
}
await UserBumpAccountRevisionDateByOrganizationId(ciphers.First().OrganizationId.Value);
}
}
public async Task DeleteAsync(IEnumerable<Guid> ids, Guid userId)
{
await ToggleCipherStates(ids, userId, CipherStateAction.HardDelete);
}
public async Task DeleteAttachmentAsync(Guid cipherId, string attachmentId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var cipher = await dbContext.Ciphers.FindAsync(cipherId);
var attachmentsJson = JObject.Parse(cipher.Attachments);
attachmentsJson.Remove(attachmentId);
cipher.Attachments = JsonConvert.SerializeObject(attachmentsJson);
await dbContext.SaveChangesAsync();
if (cipher.OrganizationId.HasValue)
{
await OrganizationUpdateStorage(cipher.OrganizationId.Value);
await UserBumpAccountRevisionDateByCipherId(cipher);
}
else if (cipher.UserId.HasValue)
{
await UserUpdateStorage(cipher.UserId.Value);
await UserBumpAccountRevisionDate(cipher.UserId.Value);
}
}
}
public async Task DeleteByIdsOrganizationIdAsync(IEnumerable<Guid> ids, Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var ciphers = from c in dbContext.Ciphers
where c.OrganizationId == organizationId &&
ids.Contains(c.Id)
select c;
dbContext.RemoveRange(ciphers);
await dbContext.SaveChangesAsync();
}
await OrganizationUpdateStorage(organizationId);
await UserBumpAccountRevisionDateByOrganizationId(organizationId);
}
public async Task DeleteByOrganizationIdAsync(Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var collectionCiphers = from cc in dbContext.CollectionCiphers
join c in dbContext.Collections
on cc.CollectionId equals c.Id
where c.OrganizationId == organizationId
select cc;
dbContext.RemoveRange(collectionCiphers);
var ciphers = from c in dbContext.Ciphers
where c.OrganizationId == organizationId
select c;
dbContext.RemoveRange(ciphers);
await dbContext.SaveChangesAsync();
}
await OrganizationUpdateStorage(organizationId);
await UserBumpAccountRevisionDateByOrganizationId(organizationId);
}
public async Task DeleteByUserIdAsync(Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var ciphers = from c in dbContext.Ciphers
where c.UserId == userId
select c;
dbContext.RemoveRange(ciphers);
var folders = from f in dbContext.Folders
where f.UserId == userId
select f;
dbContext.RemoveRange(folders);
await dbContext.SaveChangesAsync();
await UserUpdateStorage(userId);
await UserBumpAccountRevisionDate(userId);
}
}
public async Task DeleteDeletedAsync(DateTime deletedDateBefore)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = dbContext.Ciphers.Where(c => c.DeletedDate < deletedDateBefore);
dbContext.RemoveRange(query);
await dbContext.SaveChangesAsync();
}
}
public async Task<CipherDetails> GetByIdAsync(Guid id, Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var userCipherDetails = new UserCipherDetailsQuery(userId);
var data = await userCipherDetails.Run(dbContext).FirstOrDefaultAsync(c => c.Id == id);
return data;
}
}
public async Task<bool> GetCanEditByIdAsync(Guid userId, Guid cipherId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new CipherReadCanEditByIdUserIdQuery(userId, cipherId);
var canEdit = await query.Run(dbContext).AnyAsync();
return canEdit;
}
}
public async Task<ICollection<Cipher>> GetManyByOrganizationIdAsync(Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = dbContext.Ciphers.Where(x => !x.UserId.HasValue && x.OrganizationId == organizationId);
var data = await query.ToListAsync();
return Mapper.Map<List<TableModel.Cipher>>(data);
}
}
public async Task<ICollection<CipherDetails>> GetManyByUserIdAsync(Guid userId, bool withOrganizations = true)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
IQueryable<CipherDetails> cipherDetailsView = withOrganizations ?
new UserCipherDetailsQuery(userId).Run(dbContext) :
new CipherDetailsQuery(userId).Run(dbContext);
if (!withOrganizations)
{
cipherDetailsView = from c in cipherDetailsView
where c.UserId == userId
select new CipherDetails {
Id = c.Id,
UserId = c.UserId,
OrganizationId = c.OrganizationId,
Type= c.Type,
Data = c.Data,
Attachments = c.Attachments,
CreationDate = c.CreationDate,
RevisionDate = c.RevisionDate,
DeletedDate = c.DeletedDate,
Favorite = c.Favorite,
FolderId = c.FolderId,
Edit = true,
ViewPassword = true,
OrganizationUseTotp = false,
};
}
var ciphers = await cipherDetailsView.ToListAsync();
return ciphers;
}
}
public async Task<CipherOrganizationDetails> GetOrganizationDetailsByIdAsync(Guid id)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new CipherOrganizationDetailsReadByIdQuery(id);
var data = await query.Run(dbContext).FirstOrDefaultAsync();
return data;
}
}
public async Task MoveAsync(IEnumerable<Guid> ids, Guid? folderId, Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var cipherEntities = dbContext.Ciphers.Where(c => ids.Contains(c.Id));
var userCipherDetails = new UserCipherDetailsQuery(userId).Run(dbContext);
var idsToMove = from ucd in userCipherDetails
join c in cipherEntities
on ucd.Id equals c.Id
where ucd.Edit
select c;
await idsToMove.ForEachAsync(cipher =>
{
var foldersJson = string.IsNullOrWhiteSpace(cipher.Folders) ?
new JObject() :
JObject.Parse(cipher.Folders);
if (folderId.HasValue)
{
foldersJson.Remove(userId.ToString());
foldersJson.Add(userId.ToString(), folderId.Value.ToString());
}
else if (!string.IsNullOrWhiteSpace(cipher.Folders))
{
foldersJson.Remove(userId.ToString());
}
dbContext.Attach(cipher);
cipher.Folders = JsonConvert.SerializeObject(foldersJson);
});
await dbContext.SaveChangesAsync();
await UserBumpAccountRevisionDate(userId);
}
}
public async Task ReplaceAsync(CipherDetails cipher)
{
cipher.UserId = cipher.OrganizationId.HasValue ?
null :
cipher.UserId;
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var entity = await dbContext.Ciphers.FindAsync(cipher.Id);
if (entity != null)
{
var userIdKey = $"\"{cipher.UserId}\"";
if (cipher.Favorite)
{
if (cipher.Favorites == null)
{
cipher.Favorites = $"{{{userIdKey}:true}}";
}
else
{
var favorites = CoreHelpers.LoadClassFromJsonData<Dictionary<Guid, bool>>(cipher.Favorites);
favorites.Add(cipher.UserId.Value, true);
cipher.Favorites = JsonConvert.SerializeObject(favorites);
}
}
else
{
if (cipher.Favorites != null && cipher.Favorites.Contains(cipher.UserId.Value.ToString()))
{
var favorites = CoreHelpers.LoadClassFromJsonData<Dictionary<Guid, bool>>(cipher.Favorites);
favorites.Remove(cipher.UserId.Value);
cipher.Favorites = JsonConvert.SerializeObject(favorites);
}
}
if (cipher.FolderId.HasValue)
{
if (cipher.Folders == null)
{
cipher.Folders = $"{{{userIdKey}:\"{cipher.FolderId}\"}}";
}
else
{
var folders = CoreHelpers.LoadClassFromJsonData<Dictionary<Guid, Guid>>(cipher.Folders);
folders.Add(cipher.UserId.Value, cipher.FolderId.Value);
cipher.Folders = JsonConvert.SerializeObject(folders);
}
}
else
{
if (cipher.Folders != null && cipher.Folders.Contains(cipher.UserId.Value.ToString()))
{
var folders = CoreHelpers.LoadClassFromJsonData<Dictionary<Guid, bool>>(cipher.Favorites);
folders.Remove(cipher.UserId.Value);
cipher.Favorites = JsonConvert.SerializeObject(folders);
}
}
var mappedEntity = Mapper.Map<EfModel.Cipher>((TableModel.Cipher)cipher);
dbContext.Entry(entity).CurrentValues.SetValues(mappedEntity);
await UserBumpAccountRevisionDateByCipherId(cipher);
await dbContext.SaveChangesAsync();
}
}
}
public async Task<bool> ReplaceAsync(Cipher obj, IEnumerable<Guid> collectionIds)
{
await UpdateCollections(obj, collectionIds);
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var cipher = await dbContext.Ciphers.FindAsync(obj.Id);
cipher.UserId = null;
cipher.OrganizationId = obj.OrganizationId;
cipher.Data = obj.Data;
cipher.Attachments = obj.Attachments;
cipher.RevisionDate = obj.RevisionDate;
cipher.DeletedDate = obj.DeletedDate;
await dbContext.SaveChangesAsync();
if (!string.IsNullOrWhiteSpace(cipher.Attachments))
{
if (cipher.OrganizationId.HasValue)
{
await OrganizationUpdateStorage(cipher.OrganizationId.Value);
}
else if (cipher.UserId.HasValue)
{
await UserUpdateStorage(cipher.UserId.Value);
}
}
await UserBumpAccountRevisionDateByCipherId(cipher);
return true;
}
}
public async Task<DateTime> RestoreAsync(IEnumerable<Guid> ids, Guid userId)
{
return await ToggleCipherStates(ids, userId, CipherStateAction.Restore);
}
public async Task SoftDeleteAsync(IEnumerable<Guid> ids, Guid userId)
{
await ToggleCipherStates(ids, userId, CipherStateAction.SoftDelete);
}
private async Task<DateTime> ToggleCipherStates(IEnumerable<Guid> ids, Guid userId, CipherStateAction action)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var userCipherDetailsQuery = new UserCipherDetailsQuery(userId);
var cipherEntitiesToCheck = await (dbContext.Ciphers.Where(c => ids.Contains(c.Id))).ToListAsync();
var query = from ucd in await (userCipherDetailsQuery.Run(dbContext)).ToListAsync()
join c in cipherEntitiesToCheck
on ucd.Id equals c.Id
where ucd.Edit && ucd.DeletedDate == null
select c;
var utcNow = DateTime.UtcNow;
var cipherIdsToModify = query.Select(c => c.Id);
var cipherEntitiesToModify = dbContext.Ciphers.Where(x => cipherIdsToModify.Contains(x.Id));
if (action == CipherStateAction.HardDelete)
{
dbContext.RemoveRange(cipherEntitiesToModify);
}
else
{
await cipherEntitiesToModify.ForEachAsync(cipher =>
{
dbContext.Attach(cipher);
cipher.DeletedDate = action == CipherStateAction.Restore ? null : utcNow;
cipher.RevisionDate = utcNow;
});
}
var orgIds = query
.Where(c => c.OrganizationId.HasValue)
.GroupBy(c => c.OrganizationId).Select(x => x.Key);
foreach (var orgId in orgIds)
{
await OrganizationUpdateStorage(orgId.Value);
await UserBumpAccountRevisionDateByOrganizationId(orgId.Value);
}
if (query.Any(c => c.UserId.HasValue && !string.IsNullOrWhiteSpace(c.Attachments)))
{
await UserUpdateStorage(userId);
}
await UserBumpAccountRevisionDate(userId);
await dbContext.SaveChangesAsync();
return utcNow;
}
}
public async Task SoftDeleteByIdsOrganizationIdAsync(IEnumerable<Guid> ids, Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var utcNow = DateTime.UtcNow;
var ciphers = dbContext.Ciphers.Where(c => ids.Contains(c.Id) && c.OrganizationId == organizationId);
await ciphers.ForEachAsync(cipher =>
{
dbContext.Attach(cipher);
cipher.DeletedDate = utcNow;
cipher.RevisionDate = utcNow;
});
await dbContext.SaveChangesAsync();
await OrganizationUpdateStorage(organizationId);
await UserBumpAccountRevisionDateByOrganizationId(organizationId);
}
}
public async Task UpdateAttachmentAsync(CipherAttachment attachment)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var cipher = await dbContext.Ciphers.FindAsync(attachment.Id);
var attachmentsJson = string.IsNullOrWhiteSpace(cipher.Attachments) ? new JObject() : JObject.Parse(cipher.Attachments);
attachmentsJson.Add(attachment.AttachmentId, attachment.AttachmentData);
cipher.Attachments = JsonConvert.SerializeObject(attachmentsJson);
await dbContext.SaveChangesAsync();
if (attachment.OrganizationId.HasValue)
{
await OrganizationUpdateStorage(cipher.OrganizationId.Value);
await UserBumpAccountRevisionDateByCipherId(new List<Cipher> { cipher });
}
else if (attachment.UserId.HasValue)
{
await UserUpdateStorage(attachment.UserId.Value);
await UserBumpAccountRevisionDate(attachment.UserId.Value);
}
}
}
public async Task UpdateCiphersAsync(Guid userId, IEnumerable<Cipher> ciphers)
{
if (!ciphers.Any())
{
return;
}
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var entities = Mapper.Map<List<EfModel.Cipher>>(ciphers);
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, entities);
await UserBumpAccountRevisionDate(userId);
}
}
public async Task UpdatePartialAsync(Guid id, Guid userId, Guid? folderId, bool favorite)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var cipher = await dbContext.Ciphers.FindAsync(id);
var foldersJson = JObject.Parse(cipher.Folders);
if (foldersJson == null && folderId.HasValue)
{
foldersJson.Add(userId.ToString(), folderId.Value);
}
else if (foldersJson != null && folderId.HasValue)
{
foldersJson[userId] = folderId.Value;
}
else
{
foldersJson.Remove(userId.ToString());
}
var favoritesJson = JObject.Parse(cipher.Favorites);
if (favorite)
{
favoritesJson.Add(userId.ToString(), favorite);
}
else
{
favoritesJson.Remove(userId.ToString());
}
await dbContext.SaveChangesAsync();
await UserBumpAccountRevisionDate(userId);
}
}
public async Task UpdateUserKeysAndCiphersAsync(User user, IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders, IEnumerable<Send> sends)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
await UserUpdateKeys(user);
var cipherEntities = Mapper.Map<List<EfModel.Cipher>>(ciphers);
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, cipherEntities);
var folderEntities = Mapper.Map<List<EfModel.Folder>>(folders);
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, folderEntities);
var sendEntities = Mapper.Map<List<EfModel.Send>>(sends);
await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, sendEntities);
await dbContext.SaveChangesAsync();
}
}
public async Task UpsertAsync(CipherDetails cipher)
{
if (cipher.Id.Equals(default))
{
await CreateAsync(cipher);
}
else
{
await ReplaceAsync(cipher);
}
}
}
}

View File

@ -0,0 +1,242 @@
using AutoMapper;
using Bit.Core.Enums;
using Bit.Core.Models.Table;
using Bit.Core.Repositories.EntityFramework.Queries;
using EfModel = Bit.Core.Models.EntityFramework;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System;
namespace Bit.Core.Repositories.EntityFramework
{
public class CollectionCipherRepository : BaseEntityFrameworkRepository, ICollectionCipherRepository
{
public CollectionCipherRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper)
{ }
public async Task<CollectionCipher> CreateAsync(CollectionCipher obj)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var entity = Mapper.Map<EfModel.CollectionCipher>(obj);
dbContext.Add(entity);
await dbContext.SaveChangesAsync();
var organizationId = (await dbContext.Ciphers.FirstOrDefaultAsync(c => c.Id.Equals(obj.CipherId))).OrganizationId;
if (organizationId.HasValue)
{
await UserBumpAccountRevisionDateByCollectionId(obj.CollectionId, organizationId.Value);
}
return obj;
}
}
public async Task<ICollection<CollectionCipher>> GetManyByOrganizationIdAsync(Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var data = await (from cc in dbContext.CollectionCiphers
join c in dbContext.Collections
on cc.CollectionId equals c.Id
where c.OrganizationId == organizationId
select cc).ToArrayAsync();
return data;
}
}
public async Task<ICollection<CollectionCipher>> GetManyByUserIdAsync(Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var data = await new CollectionCipherReadByUserIdQuery(userId)
.Run(dbContext)
.ToArrayAsync();
return data;
}
}
public async Task<ICollection<CollectionCipher>> GetManyByUserIdCipherIdAsync(Guid userId, Guid cipherId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var data = await new CollectionCipherReadByUserIdCipherIdQuery(userId, cipherId)
.Run(dbContext)
.ToArrayAsync();
return data;
}
}
public async Task UpdateCollectionsAsync(Guid cipherId, Guid userId, IEnumerable<Guid> collectionIds)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var organizationId = (await dbContext.Ciphers.FindAsync(cipherId)).OrganizationId;
var availableCollectionsCte = from c in dbContext.Collections
join o in dbContext.Organizations
on c.OrganizationId equals o.Id
join ou in dbContext.OrganizationUsers
on o.Id equals ou.OrganizationId
where ou.UserId == userId
join cu in dbContext.CollectionUsers
on ou.Id equals cu.OrganizationUserId into cu_g
from cu in cu_g.DefaultIfEmpty()
where !ou.AccessAll && cu.CollectionId == c.Id
join gu in dbContext.GroupUsers
on ou.Id equals gu.OrganizationUserId into gu_g
from gu in gu_g.DefaultIfEmpty()
where cu.CollectionId == null && !ou.AccessAll
join g in dbContext.Groups
on gu.GroupId equals g.Id into g_g
from g in g_g.DefaultIfEmpty()
join cg in dbContext.CollectionGroups
on gu.GroupId equals cg.GroupId into cg_g
from cg in cg_g.DefaultIfEmpty()
where !g.AccessAll && cg.CollectionId == c.Id &&
(o.Id == organizationId && o.Enabled && ou.Status == OrganizationUserStatusType.Confirmed && (
ou.AccessAll || !cu.ReadOnly || g.AccessAll || !cg.ReadOnly))
select new { c, o, cu, gu, g, cg };
var target = from cc in dbContext.CollectionCiphers
where cc.CipherId == cipherId
select new { cc.CollectionId, cc.CipherId };
var source = collectionIds.Select(x => new { CollectionId = x, CipherId = cipherId });
var merge1 = from t in target
join s in source
on t.CollectionId equals s.CollectionId into s_g
from s in s_g.DefaultIfEmpty()
where t.CipherId == s.CipherId
select new { t, s };
var merge2 = from s in source
join t in target
on s.CollectionId equals t.CollectionId into t_g
from t in t_g.DefaultIfEmpty()
where t.CipherId == s.CipherId
select new { t, s };
var union = merge1.Union(merge2).Distinct();
var insert = union
.Where(x => x.t == null && collectionIds.Contains(x.s.CollectionId))
.Select(x => new EfModel.CollectionCipher
{
CollectionId = x.s.CollectionId,
CipherId = x.s.CipherId,
});
var delete = union
.Where(x => x.s == null && x.t.CipherId == cipherId && collectionIds.Contains(x.t.CollectionId))
.Select(x => new EfModel.CollectionCipher
{
CollectionId = x.t.CollectionId,
CipherId = x.t.CipherId,
});
await dbContext.AddRangeAsync(insert);
dbContext.RemoveRange(delete);
await dbContext.SaveChangesAsync();
if (organizationId.HasValue)
{
await UserBumpAccountRevisionDateByOrganizationId(organizationId.Value);
}
}
}
public async Task UpdateCollectionsForAdminAsync(Guid cipherId, Guid organizationId, IEnumerable<Guid> collectionIds)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var availableCollectionsCte = from c in dbContext.Collections
where c.OrganizationId == organizationId
select c;
var target = from cc in dbContext.CollectionCiphers
where cc.CipherId == cipherId
select new { cc.CollectionId, cc.CipherId };
var source = collectionIds.Select(x => new { CollectionId = x, CipherId = cipherId });
var merge1 = from t in target
join s in source
on t.CollectionId equals s.CollectionId into s_g
from s in s_g.DefaultIfEmpty()
where t.CipherId == s.CipherId
select new { t, s };
var merge2 = from s in source
join t in target
on s.CollectionId equals t.CollectionId into t_g
from t in t_g.DefaultIfEmpty()
where t.CipherId == s.CipherId
select new { t, s };
var union = merge1.Union(merge2).Distinct();
var insert = union
.Where(x => x.t == null && collectionIds.Contains(x.s.CollectionId))
.Select(x => new EfModel.CollectionCipher
{
CollectionId = x.s.CollectionId,
CipherId = x.s.CipherId,
});
var delete = union
.Where(x => x.s == null && x.t.CipherId == cipherId)
.Select(x => new EfModel.CollectionCipher
{
CollectionId = x.t.CollectionId,
CipherId = x.t.CipherId,
});
await dbContext.AddRangeAsync(insert);
dbContext.RemoveRange(delete);
await dbContext.SaveChangesAsync();
await UserBumpAccountRevisionDateByOrganizationId(organizationId);
}
}
public async Task UpdateCollectionsForCiphersAsync(IEnumerable<Guid> cipherIds, Guid userId, Guid organizationId, IEnumerable<Guid> collectionIds)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var availibleCollections = from c in dbContext.Collections
join o in dbContext.Organizations
on c.OrganizationId equals o.Id
join ou in dbContext.OrganizationUsers
on o.Id equals ou.OrganizationId
where ou.UserId == userId
join cu in dbContext.CollectionUsers
on ou.Id equals cu.OrganizationUserId into cu_g
from cu in cu_g.DefaultIfEmpty()
where !ou.AccessAll && cu.CollectionId == c.Id
join gu in dbContext.GroupUsers
on ou.Id equals gu.OrganizationUserId into gu_g
from gu in gu_g.DefaultIfEmpty()
where cu.CollectionId == null && !ou.AccessAll
join g in dbContext.Groups
on gu.GroupId equals g.Id into g_g
from g in g_g.DefaultIfEmpty()
join cg in dbContext.CollectionGroups
on gu.GroupId equals cg.GroupId into cg_g
from cg in cg_g.DefaultIfEmpty()
where !g.AccessAll && cg.CollectionId == c.Id &&
(o.Id == organizationId && o.Enabled && ou.Status == OrganizationUserStatusType.Confirmed &&
(ou.AccessAll || !cu.ReadOnly || g.AccessAll || !cg.ReadOnly))
select new { c, o, ou, cu, gu, g, cg };
var count = await availibleCollections.CountAsync();
if (await availibleCollections.CountAsync() < 1)
{
return;
}
var insertData = from collectionId in collectionIds
from cipherId in cipherIds
where availibleCollections.Select(x => x.c.Id).Contains(collectionId)
select new EfModel.CollectionCipher
{
CollectionId = collectionId,
CipherId = cipherId,
};
await dbContext.AddRangeAsync(insertData);
await UserBumpAccountRevisionDateByOrganizationId(organizationId);
}
}
}
}

View File

@ -0,0 +1,247 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table;
using Bit.Core.Repositories.EntityFramework.Queries;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using EfModel = Bit.Core.Models.EntityFramework;
using TableModel = Bit.Core.Models.Table;
namespace Bit.Core.Repositories.EntityFramework
{
public class CollectionRepository : Repository<TableModel.Collection, EfModel.Collection, Guid>, ICollectionRepository
{
public CollectionRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Collections)
{ }
public override async Task<TableModel.Collection> CreateAsync(Collection obj)
{
await base.CreateAsync(obj);
await UserBumpAccountRevisionDateByCollectionId(obj.Id, obj.OrganizationId);
return obj;
}
public async Task CreateAsync(Collection obj, IEnumerable<SelectionReadOnly> groups)
{
await base.CreateAsync(obj);
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var availibleGroups = await (from g in dbContext.Groups
where g.OrganizationId == obj.OrganizationId
select g.Id).ToListAsync();
var collectionGroups = groups
.Where(g => availibleGroups.Contains(g.Id))
.Select(g => new EfModel.CollectionGroup
{
CollectionId = obj.Id,
GroupId = g.Id,
ReadOnly = g.ReadOnly,
HidePasswords = g.HidePasswords,
});
await dbContext.AddRangeAsync(collectionGroups);
await dbContext.SaveChangesAsync();
await UserBumpAccountRevisionDateByOrganizationId(obj.OrganizationId);
}
}
public async Task DeleteUserAsync(Guid collectionId, Guid organizationUserId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from cu in dbContext.CollectionUsers
where cu.CollectionId == collectionId &&
cu.OrganizationUserId == organizationUserId
select cu;
dbContext.RemoveRange(await query.ToListAsync());
await dbContext.SaveChangesAsync();
await UserBumpAccountRevisionDateByOrganizationUserId(organizationUserId);
}
}
public async Task<CollectionDetails> GetByIdAsync(Guid id, Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new UserCollectionDetailsQuery(userId);
var collection = await query.Run(dbContext).FirstOrDefaultAsync();
return collection;
}
}
public async Task<Tuple<Collection, ICollection<SelectionReadOnly>>> GetByIdWithGroupsAsync(Guid id)
{
var collection = await base.GetByIdAsync(id);
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var collectionGroups = await (from cg in dbContext.CollectionGroups
where cg.CollectionId == id
select cg).ToListAsync();
var selectionReadOnlys = collectionGroups.Select(cg => new SelectionReadOnly
{
Id = cg.GroupId,
ReadOnly = cg.ReadOnly,
HidePasswords = cg.HidePasswords,
}).ToList();
return new Tuple<Collection, ICollection<SelectionReadOnly>>(collection, selectionReadOnlys);
}
}
public async Task<Tuple<CollectionDetails, ICollection<SelectionReadOnly>>> GetByIdWithGroupsAsync(Guid id, Guid userId)
{
var collection = await GetByIdAsync(id, userId);
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from cg in dbContext.CollectionGroups
where cg.CollectionId.Equals(id)
select new SelectionReadOnly
{
Id = cg.GroupId,
ReadOnly = cg.ReadOnly,
HidePasswords = cg.HidePasswords,
};
var configurations = await query.ToArrayAsync();
return new Tuple<CollectionDetails, ICollection<SelectionReadOnly>>(collection, configurations);
}
}
public async Task<int> GetCountByOrganizationIdAsync(Guid organizationId)
{
var query = new CollectionReadCountByOrganizationIdQuery(organizationId);
return await GetCountFromQuery(query);
}
public async Task<ICollection<Collection>> GetManyByOrganizationIdAsync(Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from c in dbContext.Collections
where c.OrganizationId == organizationId
select c;
var collections = await query.ToArrayAsync();
return collections;
}
}
public async Task<ICollection<CollectionDetails>> GetManyByUserIdAsync(Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new UserCollectionDetailsQuery(userId).Run(dbContext);
var data = await query.ToListAsync();
return data.GroupBy(c => c.Id).Select(c => c.First()).ToList();
}
}
public async Task<ICollection<SelectionReadOnly>> GetManyUsersByIdAsync(Guid id)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from cu in dbContext.CollectionUsers
where cu.CollectionId == id
select cu;
var collectionUsers = await query.ToListAsync();
return collectionUsers.Select(cu => new SelectionReadOnly
{
Id = cu.OrganizationUserId,
ReadOnly = cu.ReadOnly,
HidePasswords = cu.HidePasswords,
}).ToArray();
}
}
public async Task ReplaceAsync(Collection collection, IEnumerable<SelectionReadOnly> groups)
{
await base.ReplaceAsync(collection);
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var groupsInOrg = dbContext.Groups.Where(g => g.OrganizationId == collection.OrganizationId);
var modifiedGroupEntities = dbContext.Groups.Where(x => groups.Select(x => x.Id).Contains(x.Id));
var target = (from cg in dbContext.CollectionGroups
join g in modifiedGroupEntities
on cg.CollectionId equals collection.Id into s_g
from g in s_g.DefaultIfEmpty()
where g == null || cg.GroupId == g.Id
select new {cg, g}).AsNoTracking();
var source = (from g in modifiedGroupEntities
from cg in dbContext.CollectionGroups
.Where(cg => cg.CollectionId == collection.Id && cg.GroupId == g.Id).DefaultIfEmpty()
select new {cg, g}).AsNoTracking();
var union = await target
.Union(source)
.Where(x =>
x.cg == null ||
((x.g == null || x.g.Id == x.cg.GroupId) &&
(x.cg.CollectionId == collection.Id)))
.AsNoTracking()
.ToListAsync();
var insert = union.Where(x => x.cg == null && groupsInOrg.Any(c => x.g.Id == c.Id))
.Select(x => new EfModel.CollectionGroup
{
CollectionId = collection.Id,
GroupId = x.g.Id,
ReadOnly = groups.FirstOrDefault(g => g.Id == x.g.Id).ReadOnly,
HidePasswords = groups.FirstOrDefault(g => g.Id == x.g.Id).HidePasswords,
}).ToList();
var update = union
.Where(
x => x.g != null &&
x.cg != null &&
(x.cg.ReadOnly != groups.FirstOrDefault(g => g.Id == x.g.Id).ReadOnly ||
x.cg.HidePasswords != groups.FirstOrDefault(g => g.Id == x.g.Id).HidePasswords)
)
.Select(x => new EfModel.CollectionGroup
{
CollectionId = collection.Id,
GroupId = x.g.Id,
ReadOnly = groups.FirstOrDefault(g => g.Id == x.g.Id).ReadOnly,
HidePasswords = groups.FirstOrDefault(g => g.Id == x.g.Id).HidePasswords,
});
var delete = union
.Where(
x => x.g == null &&
x.cg.CollectionId == collection.Id
)
.Select(x => new EfModel.CollectionGroup
{
CollectionId = collection.Id,
GroupId = x.cg.GroupId,
})
.ToList();
await dbContext.AddRangeAsync(insert);
dbContext.UpdateRange(update);
dbContext.RemoveRange(delete);
await dbContext.SaveChangesAsync();
await UserBumpAccountRevisionDateByCollectionId(collection.Id, collection.OrganizationId);
}
}
public async Task UpdateUsersAsync(Guid id, IEnumerable<SelectionReadOnly> users)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var procedure = new CollectionUserUpdateUsersQuery(id, users);
var updateData = await procedure.Update.BuildInMemory(dbContext);
dbContext.UpdateRange(updateData);
var insertData = await procedure.Insert.BuildInMemory(dbContext);
await dbContext.AddRangeAsync(insertData);
dbContext.RemoveRange(await procedure.Delete.Run(dbContext).ToListAsync());
}
}
}
}

View File

@ -1,39 +1,136 @@
using System; using Bit.Core.Models.EntityFramework;
using Bit.Core.Models.EntityFramework; using Bit.Core.Models.EntityFramework.Provider;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Bit.Core.Repositories.EntityFramework namespace Bit.Core.Repositories.EntityFramework
{ {
public class DatabaseContext : DbContext public class DatabaseContext : DbContext
{ {
public const string postgresIndetermanisticCollation = "postgresIndetermanisticCollation";
public DatabaseContext(DbContextOptions<DatabaseContext> options) public DatabaseContext(DbContextOptions<DatabaseContext> options)
: base(options) : base(options)
{ } { }
public DbSet<User> Users { get; set; }
public DbSet<Cipher> Ciphers { get; set; } public DbSet<Cipher> Ciphers { get; set; }
public DbSet<Collection> Collections { get; set; }
public DbSet<CollectionCipher> CollectionCiphers { get; set; }
public DbSet<CollectionGroup> CollectionGroups { get; set; }
public DbSet<CollectionUser> CollectionUsers { get; set; }
public DbSet<Device> Devices { get; set; }
public DbSet<EmergencyAccess> EmergencyAccesses { get; set; }
public DbSet<Event> Events { get; set; }
public DbSet<Folder> Folders { get; set; }
public DbSet<Grant> Grants { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<GroupUser> GroupUsers { get; set; }
public DbSet<Installation> Installations { get; set; }
public DbSet<Organization> Organizations { get; set; } public DbSet<Organization> Organizations { get; set; }
public DbSet<OrganizationUser> OrganizationUsers { get; set; }
public DbSet<Policy> Policies { get; set; }
public DbSet<Provider> Providers { get; set; }
public DbSet<ProviderUser> ProviderUsers { get; set; }
public DbSet<ProviderOrganization> ProviderOrganizations { get; set; }
public DbSet<ProviderOrganizationProviderUser> ProviderOrganizationProviderUsers { get; set; }
public DbSet<Send> Sends { get; set; }
public DbSet<SsoConfig> SsoConfigs { get; set; }
public DbSet<SsoUser> SsoUsers { get; set; }
public DbSet<TaxRate> TaxRates { get; set; }
public DbSet<Transaction> Transactions { get; set; }
public DbSet<U2f> U2fs { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder builder) protected override void OnModelCreating(ModelBuilder builder)
{ {
builder.Entity<Cipher>().Ignore(e => e.Data); var eCipher = builder.Entity<Cipher>();
builder.Entity<Cipher>().Property(e => e.DataJson).HasColumnName("Data"); var eCollection = builder.Entity<Collection>();
builder.Entity<Cipher>().Ignore(e => e.Attachments); var eCollectionCipher = builder.Entity<CollectionCipher>();
builder.Entity<Cipher>().Property(e => e.AttachmentsJson).HasColumnName("Attachments"); var eCollectionUser = builder.Entity<CollectionUser>();
builder.Entity<Cipher>().Ignore(e => e.Favorites); var eCollectionGroup = builder.Entity<CollectionGroup>();
builder.Entity<Cipher>().Property(e => e.FavoritesJson).HasColumnName("Favorites"); var eDevice = builder.Entity<Device>();
builder.Entity<Cipher>().Ignore(e => e.Folders); var eEmergencyAccess = builder.Entity<EmergencyAccess>();
builder.Entity<Cipher>().Property(e => e.FoldersJson).HasColumnName("Folders"); var eEvent = builder.Entity<Event>();
var eFolder = builder.Entity<Folder>();
var eGrant = builder.Entity<Grant>();
var eGroup = builder.Entity<Group>();
var eGroupUser = builder.Entity<GroupUser>();
var eInstallation = builder.Entity<Installation>();
var eOrganization = builder.Entity<Organization>();
var eOrganizationUser = builder.Entity<OrganizationUser>();
var ePolicy = builder.Entity<Policy>();
var eProvider = builder.Entity<Provider>();
var eProviderUser = builder.Entity<ProviderUser>();
var eProviderOrganization = builder.Entity<ProviderOrganization>();
var eProviderOrganizationProviderUser = builder.Entity<ProviderOrganizationProviderUser>();
var eSend = builder.Entity<Send>();
var eSsoConfig = builder.Entity<SsoConfig>();
var eSsoUser = builder.Entity<SsoUser>();
var eTaxRate = builder.Entity<TaxRate>();
var eTransaction = builder.Entity<Transaction>();
var eU2f = builder.Entity<U2f>();
var eUser = builder.Entity<User>();
builder.Entity<User>().Ignore(e => e.TwoFactorProviders); eCipher.Property(c => c.Id).ValueGeneratedNever();
builder.Entity<User>().Property(e => e.TwoFactorProvidersJson).HasColumnName("TwoFactorProviders"); eCollection.Property(c => c.Id).ValueGeneratedNever();
eEmergencyAccess.Property(c => c.Id).ValueGeneratedNever();
eEvent.Property(c => c.Id).ValueGeneratedNever();
eFolder.Property(c => c.Id).ValueGeneratedNever();
eGroup.Property(c => c.Id).ValueGeneratedNever();
eInstallation.Property(c => c.Id).ValueGeneratedNever();
eOrganization.Property(c => c.Id).ValueGeneratedNever();
eOrganizationUser.Property(c => c.Id).ValueGeneratedNever();
ePolicy.Property(c => c.Id).ValueGeneratedNever();
eProvider.Property(c => c.Id).ValueGeneratedNever();
eProviderUser.Property(c => c.Id).ValueGeneratedNever();
eProviderOrganization.Property(c => c.Id).ValueGeneratedNever();
eProviderOrganizationProviderUser.Property(c => c.Id).ValueGeneratedNever();
eSend.Property(c => c.Id).ValueGeneratedNever();
eTransaction.Property(c => c.Id).ValueGeneratedNever();
eUser.Property(c => c.Id).ValueGeneratedNever();
builder.Entity<Organization>().Ignore(e => e.TwoFactorProviders); eCollectionCipher.HasKey(cc => new { cc.CollectionId, cc.CipherId });
builder.Entity<Organization>().Property(e => e.TwoFactorProvidersJson).HasColumnName("TwoFactorProviders"); eCollectionUser.HasKey(cu => new { cu.CollectionId, cu.OrganizationUserId });
eCollectionGroup.HasKey(cg => new { cg.CollectionId, cg.GroupId });
eGrant.HasKey(x => x.Key);
eGroupUser.HasKey(gu => new { gu.GroupId, gu.OrganizationUserId });
builder.Entity<User>().ToTable(nameof(User));
builder.Entity<Cipher>().ToTable(nameof(Cipher)); if (Database.IsNpgsql())
builder.Entity<Organization>().ToTable(nameof(Organization)); {
// the postgres provider doesn't currently support database level non-deterministic collations.
// see https://www.npgsql.org/efcore/misc/collations-and-case-sensitivity.html#database-collation
builder.HasCollation(postgresIndetermanisticCollation, locale: "en-u-ks-primary", provider: "icu", deterministic: false);
eUser.Property(e => e.Email).UseCollation(postgresIndetermanisticCollation);
eSsoUser.Property(e => e.ExternalId).UseCollation(postgresIndetermanisticCollation);
eOrganization.Property(e => e.Identifier).UseCollation(postgresIndetermanisticCollation);
//
}
eCipher.ToTable(nameof(Cipher));
eCollection.ToTable(nameof(Collection));
eCollectionCipher.ToTable(nameof(CollectionCipher));
eDevice.ToTable(nameof(Device));
eEmergencyAccess.ToTable(nameof(EmergencyAccess));
eEvent.ToTable(nameof(Event));
eFolder.ToTable(nameof(Folder));
eGrant.ToTable(nameof(Grant));
eGroup.ToTable(nameof(Group));
eGroupUser.ToTable(nameof(GroupUser));
eInstallation.ToTable(nameof(Installation));
eOrganization.ToTable(nameof(Organization));
eOrganizationUser.ToTable(nameof(OrganizationUser));
ePolicy.ToTable(nameof(Policy));
eProvider.ToTable(nameof(Provider));
eProviderUser.ToTable(nameof(ProviderUser));
eProviderOrganization.ToTable(nameof(ProviderOrganization));
eProviderOrganizationProviderUser.ToTable(nameof(ProviderOrganizationProviderUser));
eSend.ToTable(nameof(Send));
eSsoConfig.ToTable(nameof(SsoConfig));
eSsoUser.ToTable(nameof(SsoUser));
eTaxRate.ToTable(nameof(TaxRate));
eTransaction.ToTable(nameof(Transaction));
eU2f.ToTable(nameof(U2f));
eUser.ToTable(nameof(User));
} }
} }
} }

View File

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Bit.Core.Models.Table;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using EfModel = Bit.Core.Models.EntityFramework;
using TableModel = Bit.Core.Models.Table;
namespace Bit.Core.Repositories.EntityFramework
{
public class DeviceRepository : Repository<TableModel.Device, EfModel.Device, Guid>, IDeviceRepository
{
public DeviceRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Devices)
{ }
public async Task ClearPushTokenAsync(Guid id)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = dbContext.Devices.Where(d => d.Id == id);
dbContext.AttachRange(query);
await query.ForEachAsync(x => x.PushToken = null);
await dbContext.SaveChangesAsync();
}
}
public async Task<Device> GetByIdAsync(Guid id, Guid userId)
{
var device = await base.GetByIdAsync(id);
if (device == null || device.UserId != userId)
{
return null;
}
return Mapper.Map<TableModel.Device>(device);
}
public async Task<Device> GetByIdentifierAsync(string identifier)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = dbContext.Devices.Where(d => d.Identifier == identifier);
var device = await query.FirstOrDefaultAsync();
return Mapper.Map<TableModel.Device>(device);
}
}
public async Task<Device> GetByIdentifierAsync(string identifier, Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = dbContext.Devices.Where(d => d.Identifier == identifier && d.UserId == userId);
var device = await query.FirstOrDefaultAsync();
return Mapper.Map<TableModel.Device>(device);
}
}
public async Task<ICollection<Device>> GetManyByUserIdAsync(Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = dbContext.Devices.Where(d => d.UserId == userId);
var devices = await query.ToListAsync();
return Mapper.Map<List<TableModel.Device>>(devices);
}
}
}
}

View File

@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Repositories.EntityFramework.Queries;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using EfModel = Bit.Core.Models.EntityFramework;
using TableModel = Bit.Core.Models.Table;
namespace Bit.Core.Repositories.EntityFramework
{
public class EmergencyAccessRepository : Repository<TableModel.EmergencyAccess, EfModel.EmergencyAccess, Guid>, IEmergencyAccessRepository
{
public EmergencyAccessRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.EmergencyAccesses)
{ }
public async Task<int> GetCountByGrantorIdEmailAsync(Guid grantorId, string email, bool onlyRegisteredUsers)
{
var query = new EmergencyAccessReadCountByGrantorIdEmailQuery(grantorId, email, onlyRegisteredUsers);
return await GetCountFromQuery(query);
}
public async Task<EmergencyAccessDetails> GetDetailsByIdGrantorIdAsync(Guid id, Guid grantorId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var view = new EmergencyAccessDetailsViewQuery();
var query = view.Run(dbContext).Where(ea =>
ea.Id == id &&
ea.GrantorId == grantorId
);
return await query.FirstOrDefaultAsync();
}
}
public async Task<ICollection<EmergencyAccessDetails>> GetExpiredRecoveriesAsync()
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var view = new EmergencyAccessDetailsViewQuery();
var query = view.Run(dbContext).Where(ea =>
ea.Status == EmergencyAccessStatusType.RecoveryInitiated
);
return await query.ToListAsync();
}
}
public async Task<ICollection<EmergencyAccessDetails>> GetManyDetailsByGranteeIdAsync(Guid granteeId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var view = new EmergencyAccessDetailsViewQuery();
var query = view.Run(dbContext).Where(ea =>
ea.GranteeId == granteeId
);
return await query.ToListAsync();
}
}
public async Task<ICollection<EmergencyAccessDetails>> GetManyDetailsByGrantorIdAsync(Guid grantorId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var view = new EmergencyAccessDetailsViewQuery();
var query = view.Run(dbContext).Where(ea =>
ea.GrantorId == grantorId
);
return await query.ToListAsync();
}
}
public async Task<ICollection<EmergencyAccessNotify>> GetManyToNotifyAsync()
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var view = new EmergencyAccessDetailsViewQuery();
var query = view.Run(dbContext).Where(ea =>
ea.Status == EmergencyAccessStatusType.RecoveryInitiated
);
var notifies = await query.Select(ea => new EmergencyAccessNotify
{
Id = ea.Id,
GrantorId = ea.GrantorId,
GranteeId = ea.GranteeId,
Email = ea.Email,
KeyEncrypted = ea.KeyEncrypted,
Type = ea.Type,
Status = ea.Status,
WaitTimeDays = ea.WaitTimeDays,
RecoveryInitiatedDate = ea.RecoveryInitiatedDate,
LastNotificationDate = ea.LastNotificationDate,
CreationDate = ea.CreationDate,
RevisionDate = ea.RevisionDate,
GranteeName = ea.GranteeName,
GranteeEmail = ea.GranteeEmail,
GrantorEmail = ea.GrantorEmail,
}).ToListAsync();
return notifies;
}
}
}
}

View File

@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table;
using Bit.Core.Repositories.EntityFramework.Queries;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using DataModel = Bit.Core.Models.Data;
using EfModel = Bit.Core.Models.EntityFramework;
using TableModel = Bit.Core.Models.Table;
namespace Bit.Core.Repositories.EntityFramework
{
public class EventRepository : Repository<TableModel.Event, EfModel.Event, Guid>, IEventRepository
{
public EventRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Events)
{ }
public async Task CreateAsync(IEvent e)
{
if (e is not 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 scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var tableEvents = entities.Select(e => e as Event ?? new Event(e));
var entityEvents = Mapper.Map<List<EfModel.Event>>(tableEvents);
await dbContext.BulkCopyAsync(entityEvents);
}
}
public async Task<PagedResult<IEvent>> GetManyByCipherAsync(Cipher cipher, 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);
}
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new EventReadPageByCipherIdQuery(cipher, startDate, endDate, beforeDate, pageOptions);
var events = await query.Run(dbContext).ToListAsync();
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;
}
}
public async Task<PagedResult<IEvent>> GetManyByOrganizationActingUserAsync(Guid organizationId, Guid actingUserId, 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);
}
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new EventReadPageByOrganizationIdActingUserIdQuery(organizationId, actingUserId,
startDate, endDate, beforeDate, pageOptions);
var events = await query.Run(dbContext).ToListAsync();
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;
}
}
public async Task<PagedResult<IEvent>> GetManyByOrganizationAsync(Guid organizationId, 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);
}
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new EventReadPageByOrganizationIdQuery(organizationId, startDate,
endDate, beforeDate, pageOptions);
var events = await query.Run(dbContext).ToListAsync();
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;
}
}
public async Task<PagedResult<IEvent>> GetManyByUserAsync(Guid userId, 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);
}
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new EventReadPageByUserIdQuery(userId, startDate,
endDate, beforeDate, pageOptions);
var events = await query.Run(dbContext).ToListAsync();
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;
}
}
}
}

View File

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Bit.Core.Models.Table;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using DataModel = Bit.Core.Models.Data;
using EfModel = Bit.Core.Models.EntityFramework;
using TableModel = Bit.Core.Models.Table;
namespace Bit.Core.Repositories.EntityFramework
{
public class FolderRepository : Repository<TableModel.Folder, EfModel.Folder, Guid>, IFolderRepository
{
public FolderRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Folders)
{ }
public async Task<Folder> GetByIdAsync(Guid id, Guid userId)
{
var folder = await base.GetByIdAsync(id);
if (folder == null || folder.UserId != userId)
{
return null;
}
return folder;
}
public async Task<ICollection<Folder>> GetManyByUserIdAsync(Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from f in dbContext.Folders
where f.UserId == userId
select f;
var folders = await query.ToListAsync();
return Mapper.Map<List<TableModel.Folder>>(folders);
}
}
}
}

View File

@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Bit.Core.Models.Table;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using DataModel = Bit.Core.Models.Data;
using EfModel = Bit.Core.Models.EntityFramework;
using TableModel = Bit.Core.Models.Table;
namespace Bit.Core.Repositories.EntityFramework
{
public class GrantRepository : BaseEntityFrameworkRepository, IGrantRepository
{
public GrantRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper)
{ }
public async Task DeleteByKeyAsync(string key)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from g in dbContext.Grants
where g.Key == key
select g;
dbContext.Remove(query);
await dbContext.SaveChangesAsync();
}
}
public async Task DeleteManyAsync(string subjectId, string sessionId, string clientId, string type)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from g in dbContext.Grants
where g.SubjectId == subjectId &&
g.ClientId == clientId &&
g.SessionId == sessionId &&
g.Type == type
select g;
dbContext.Remove(query);
await dbContext.SaveChangesAsync();
}
}
public async Task<Grant> GetByKeyAsync(string key)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from g in dbContext.Grants
where g.Key == key
select g;
var grant = await query.FirstOrDefaultAsync();
return grant;
}
}
public async Task<ICollection<Grant>> GetManyAsync(string subjectId, string sessionId, string clientId, string type)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from g in dbContext.Grants
where g.SubjectId == subjectId &&
g.ClientId == clientId &&
g.SessionId == sessionId &&
g.Type == type
select g;
var grants = await query.ToListAsync();
return (ICollection<Grant>)grants;
}
}
public async Task SaveAsync(Grant obj)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var existingGrant = await (from g in dbContext.Grants
where g.Key == obj.Key
select g).FirstOrDefaultAsync();
if (existingGrant != null)
{
dbContext.Entry(existingGrant).CurrentValues.SetValues(obj);
}
else
{
var entity = Mapper.Map<EfModel.Grant>(obj);
await dbContext.AddAsync(entity);
await dbContext.SaveChangesAsync();
}
}
}
}
}

View File

@ -0,0 +1,174 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using DataModel = Bit.Core.Models.Data;
using EfModel = Bit.Core.Models.EntityFramework;
using TableModel = Bit.Core.Models.Table;
namespace Bit.Core.Repositories.EntityFramework
{
public class GroupRepository : Repository<TableModel.Group, EfModel.Group, Guid>, IGroupRepository
{
public GroupRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Groups)
{ }
public async Task CreateAsync(Group obj, IEnumerable<SelectionReadOnly> collections)
{
var grp = await base.CreateAsync(obj);
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var availibleCollections = await (
from c in dbContext.Collections
where c.OrganizationId == grp.OrganizationId
select c).ToListAsync();
var filteredCollections = collections.Where(c => availibleCollections.Any(a => c.Id == a.Id));
var collectionGroups = filteredCollections.Select(y => new EfModel.CollectionGroup
{
CollectionId = y.Id,
GroupId = grp.Id,
ReadOnly = y.ReadOnly,
HidePasswords = y.HidePasswords,
});
await dbContext.CollectionGroups.AddRangeAsync(collectionGroups);
await dbContext.SaveChangesAsync();
}
}
public async Task DeleteUserAsync(Guid groupId, Guid organizationUserId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from gu in dbContext.GroupUsers
where gu.GroupId == groupId &&
gu.OrganizationUserId == organizationUserId
select gu;
dbContext.RemoveRange(await query.ToListAsync());
await dbContext.SaveChangesAsync();
}
}
public async Task<Tuple<Group, ICollection<SelectionReadOnly>>> GetByIdWithCollectionsAsync(Guid id)
{
var grp = await base.GetByIdAsync(id);
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = await (
from cg in dbContext.CollectionGroups
where cg.GroupId == id
select cg).ToListAsync();
var collections = query.Select(c => new SelectionReadOnly
{
Id = c.CollectionId,
ReadOnly = c.ReadOnly,
HidePasswords = c.HidePasswords,
}).ToList();
return new Tuple<Group, ICollection<SelectionReadOnly>>(
grp, collections);
}
}
public async Task<ICollection<Group>> GetManyByOrganizationIdAsync(Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var data = await (
from g in dbContext.Groups
where g.OrganizationId == organizationId
select g).ToListAsync();
return Mapper.Map<List<TableModel.Group>>(data);
}
}
public async Task<ICollection<GroupUser>> GetManyGroupUsersByOrganizationIdAsync(Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query =
from gu in dbContext.GroupUsers
join g in dbContext.Groups
on gu.GroupId equals g.Id
where g.OrganizationId == organizationId
select gu;
var groupUsers = await query.ToListAsync();
return Mapper.Map<List<TableModel.GroupUser>>(groupUsers);
}
}
public async Task<ICollection<Guid>> GetManyIdsByUserIdAsync(Guid organizationUserId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query =
from gu in dbContext.GroupUsers
where gu.OrganizationUserId == organizationUserId
select gu;
var groupIds = await query.Select(x => x.GroupId).ToListAsync();
return groupIds;
}
}
public async Task<ICollection<Guid>> GetManyUserIdsByIdAsync(Guid id)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query =
from gu in dbContext.GroupUsers
where gu.GroupId == id
select gu;
var groupIds = await query.Select(x => x.OrganizationUserId).ToListAsync();
return groupIds;
}
}
public async Task ReplaceAsync(Group obj, IEnumerable<SelectionReadOnly> collections)
{
await base.ReplaceAsync(obj);
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
await UserBumpAccountRevisionDateByOrganizationId(obj.OrganizationId);
}
}
public async Task UpdateUsersAsync(Guid groupId, IEnumerable<Guid> organizationUserIds)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var orgId = (await dbContext.Groups.FindAsync(groupId)).OrganizationId;
var insert = from ou in dbContext.OrganizationUsers
where organizationUserIds.Contains(ou.Id) &&
ou.OrganizationId == orgId &&
!dbContext.GroupUsers.Any(gu => gu.GroupId == groupId && ou.Id == gu.OrganizationUserId)
select new EfModel.GroupUser
{
GroupId = groupId,
OrganizationUserId = ou.Id,
};
await dbContext.AddRangeAsync(insert);
var delete = from gu in dbContext.GroupUsers
where gu.GroupId == groupId &&
!organizationUserIds.Contains(gu.OrganizationUserId)
select gu;
dbContext.RemoveRange(delete);
await dbContext.SaveChangesAsync();
await UserBumpAccountRevisionDateByOrganizationId(orgId);
}
}
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using DataModel = Bit.Core.Models.Data;
using EfModel = Bit.Core.Models.EntityFramework;
using TableModel = Bit.Core.Models.Table;
namespace Bit.Core.Repositories.EntityFramework
{
public class InstallationRepository : Repository<TableModel.Installation, EfModel.Installation, Guid>, IInstallationRepository
{
public InstallationRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Installations)
{ }
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.Extensions.DependencyInjection;
namespace Bit.Core.Repositories.EntityFramework
{
public class MaintenanceRepository : BaseEntityFrameworkRepository, IMaintenanceRepository
{
public MaintenanceRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper)
{ }
public async Task DeleteExpiredGrantsAsync()
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from g in dbContext.Grants
where g.ExpirationDate < DateTime.UtcNow
select g;
dbContext.RemoveRange(query);
await dbContext.SaveChangesAsync();
}
}
public Task DisableCipherAutoStatsAsync()
{
return Task.CompletedTask;
}
public Task RebuildIndexesAsync()
{
return Task.CompletedTask;
}
public Task UpdateStatisticsAsync()
{
return Task.CompletedTask;
}
}
}

View File

@ -1,14 +1,14 @@
using System; using AutoMapper;
using System.Threading.Tasks; using Bit.Core.Models.Table;
using TableModel = Bit.Core.Models.Table;
using DataModel = Bit.Core.Models.Data; using DataModel = Bit.Core.Models.Data;
using EFModel = Bit.Core.Models.EntityFramework; using EFModel = Bit.Core.Models.EntityFramework;
using System.Linq;
using System.Collections.Generic;
using AutoMapper;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Bit.Core.Models.Table; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System;
using TableModel = Bit.Core.Models.Table;
namespace Bit.Core.Repositories.EntityFramework namespace Bit.Core.Repositories.EntityFramework
{ {
@ -40,48 +40,35 @@ namespace Bit.Core.Repositories.EntityFramework
} }
public async Task<ICollection<TableModel.Organization>> GetManyByUserIdAsync(Guid userId) public async Task<ICollection<TableModel.Organization>> GetManyByUserIdAsync(Guid userId)
{
// TODO
return await Task.FromResult(null as ICollection<TableModel.Organization>);
}
public async Task<ICollection<TableModel.Organization>> SearchAsync(string name, string userEmail, bool? paid,
int skip, int take)
{ {
using (var scope = ServiceScopeFactory.CreateScope()) using (var scope = ServiceScopeFactory.CreateScope())
{ {
var dbContext = GetDatabaseContext(scope); var dbContext = GetDatabaseContext(scope);
// TODO: more filters
var organizations = await GetDbSet(dbContext) var organizations = await GetDbSet(dbContext)
.Where(e => name == null || e.Name.StartsWith(name)) .Select(e => e.OrganizationUsers
.OrderBy(e => e.Name) .Where(ou => ou.UserId == userId)
.Skip(skip).Take(take) .Select(ou => ou.Organization))
.ToListAsync(); .ToListAsync();
return Mapper.Map<List<TableModel.Organization>>(organizations); return Mapper.Map<List<TableModel.Organization>>(organizations);
} }
} }
public async Task UpdateStorageAsync(Guid id) public async Task<ICollection<TableModel.Organization>> SearchAsync(string name, string userEmail,
bool? paid, int skip, int take)
{ {
using (var scope = ServiceScopeFactory.CreateScope()) using (var scope = ServiceScopeFactory.CreateScope())
{ {
var dbContext = GetDatabaseContext(scope); var dbContext = GetDatabaseContext(scope);
var ciphers = await dbContext.Ciphers var organizations = await GetDbSet(dbContext)
.Where(e => e.UserId == null && e.OrganizationId == id).ToListAsync(); .Where(e => name == null || e.Name.Contains(name))
var storage = ciphers.Sum(e => e.AttachmentsJson?.RootElement.EnumerateArray() .Where(e => userEmail == null || e.OrganizationUsers.Any(u => u.Email == userEmail))
.Sum(p => p.GetProperty("Size").GetInt64()) ?? 0); .Where(e => paid == null ||
var organization = new EFModel.Organization (paid == true && !string.IsNullOrWhiteSpace(e.GatewaySubscriptionId)) ||
{ (paid == false && e.GatewaySubscriptionId == null))
Id = id, .OrderBy(e => e.CreationDate)
RevisionDate = DateTime.UtcNow, .Skip(skip).Take(take)
Storage = storage, .ToListAsync();
}; return Mapper.Map<List<TableModel.Organization>>(organizations);
var set = GetDbSet(dbContext);
set.Attach(organization);
var entry = dbContext.Entry(organization);
entry.Property(e => e.RevisionDate).IsModified = true;
entry.Property(e => e.Storage).IsModified = true;
await dbContext.SaveChangesAsync();
} }
} }
@ -103,5 +90,10 @@ namespace Bit.Core.Repositories.EntityFramework
}).ToListAsync(); }).ToListAsync();
} }
} }
public async Task UpdateStorageAsync(Guid id)
{
await OrganizationUpdateStorage(id);
}
} }
} }

View File

@ -0,0 +1,393 @@
using AutoMapper;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table;
using Bit.Core.Repositories.EntityFramework.Queries;
using EfModel = Bit.Core.Models.EntityFramework;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System;
using TableModel = Bit.Core.Models.Table;
namespace Bit.Core.Repositories.EntityFramework
{
public class OrganizationUserRepository : Repository<TableModel.OrganizationUser, EfModel.OrganizationUser, Guid>, IOrganizationUserRepository
{
public OrganizationUserRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.OrganizationUsers)
{ }
public async Task CreateAsync(OrganizationUser obj, IEnumerable<SelectionReadOnly> collections)
{
var organizationUser = await base.CreateAsync(obj);
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var availibleCollections = await (
from c in dbContext.Collections
where c.OrganizationId == organizationUser.OrganizationId
select c).ToListAsync();
var filteredCollections = collections.Where(c => availibleCollections.Any(a => c.Id == a.Id));
var collectionUsers = filteredCollections.Select(y => new EfModel.CollectionUser
{
CollectionId = y.Id,
OrganizationUserId = organizationUser.Id,
ReadOnly = y.ReadOnly,
HidePasswords = y.HidePasswords,
});
await dbContext.CollectionUsers.AddRangeAsync(collectionUsers);
await dbContext.SaveChangesAsync();
}
}
public async Task CreateManyAsync(IEnumerable<OrganizationUser> organizationUsers)
{
if (!organizationUsers.Any())
{
return;
}
foreach (var organizationUser in organizationUsers)
{
organizationUser.SetNewId();
}
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var entities = Mapper.Map<List<EfModel.OrganizationUser>>(organizationUsers);
await dbContext.AddRangeAsync(entities);
}
}
public async Task DeleteManyAsync(IEnumerable<Guid> organizationUserIds)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var entities = dbContext.FindAsync<EfModel.OrganizationUser>(organizationUserIds);
dbContext.RemoveRange(entities);
await dbContext.SaveChangesAsync();
}
}
public async Task<Tuple<OrganizationUser, ICollection<SelectionReadOnly>>> GetByIdWithCollectionsAsync(Guid id)
{
var organizationUser = await base.GetByIdAsync(id);
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = await (
from ou in dbContext.OrganizationUsers
join cu in dbContext.CollectionUsers
on ou.Id equals cu.OrganizationUserId
where !ou.AccessAll &&
ou.Id == id
select cu).ToListAsync();
var collections = query.Select(cu => new SelectionReadOnly
{
Id = cu.CollectionId,
ReadOnly = cu.ReadOnly,
HidePasswords = cu.HidePasswords,
});
return new Tuple<OrganizationUser, ICollection<SelectionReadOnly>>(
organizationUser, collections.ToList());
}
}
public async Task<OrganizationUser> GetByOrganizationAsync(Guid organizationId, Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var entity = await GetDbSet(dbContext)
.FirstOrDefaultAsync(e => e.OrganizationId == organizationId && e.UserId == userId);
return entity;
}
}
public async Task<OrganizationUser> GetByOrganizationEmailAsync(Guid organizationId, string email)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var entity = await GetDbSet(dbContext)
.FirstOrDefaultAsync(ou => ou.OrganizationId == organizationId &&
!string.IsNullOrWhiteSpace(ou.Email) &&
ou.Email == email);
return entity;
}
}
public async Task<int> GetCountByFreeOrganizationAdminUserAsync(Guid userId)
{
var query = new OrganizationUserReadCountByFreeOrganizationAdminUserQuery(userId);
return await GetCountFromQuery(query);
}
public async Task<int> GetCountByOnlyOwnerAsync(Guid userId)
{
var query = new OrganizationUserReadCountByOnlyOwnerQuery(userId);
return await GetCountFromQuery(query);
}
public async Task<int> GetCountByOrganizationAsync(Guid organizationId, string email, bool onlyRegisteredUsers)
{
var query = new OrganizationUserReadCountByOrganizationIdEmailQuery(organizationId, email, onlyRegisteredUsers);
return await GetCountFromQuery(query);
}
public async Task<int> GetCountByOrganizationIdAsync(Guid organizationId)
{
var query = new OrganizationUserReadCountByOrganizationIdQuery(organizationId);
return await GetCountFromQuery(query);
}
public async Task<OrganizationUserUserDetails> GetDetailsByIdAsync(Guid id)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var view = new OrganizationUserUserDetailsViewQuery();
var entity = await view.Run(dbContext).FirstOrDefaultAsync(ou => ou.Id == id);
return entity;
}
}
public async Task<Tuple<OrganizationUserUserDetails, ICollection<SelectionReadOnly>>> GetDetailsByIdWithCollectionsAsync(Guid id)
{
var organizationUserUserDetails = await GetDetailsByIdAsync(id);
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from ou in dbContext.OrganizationUsers
join cu in dbContext.CollectionUsers on ou.Id equals cu.OrganizationUserId
where !ou.AccessAll && ou.Id == id
select cu;
var collections = await query.Select(cu => new SelectionReadOnly
{
Id = cu.CollectionId,
ReadOnly = cu.ReadOnly,
HidePasswords = cu.HidePasswords,
}).ToListAsync();
return new Tuple<OrganizationUserUserDetails, ICollection<SelectionReadOnly>>(organizationUserUserDetails, collections);
}
}
public async Task<OrganizationUserOrganizationDetails> GetDetailsByUserAsync(Guid userId, Guid organizationId, OrganizationUserStatusType? status = null)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var view = new OrganizationUserOrganizationDetailsViewQuery();
var t = await (view.Run(dbContext)).ToArrayAsync();
var entity = await view.Run(dbContext)
.FirstOrDefaultAsync(o => o.UserId == userId &&
o.OrganizationId == organizationId &&
(status == null || o.Status == status));
return entity;
}
}
public async Task<ICollection<OrganizationUser>> GetManyAsync(IEnumerable<Guid> Ids)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from ou in dbContext.OrganizationUsers
where Ids.Contains(ou.Id)
select ou;
var data = await query.ToArrayAsync();
return data;
}
}
public async Task<ICollection<OrganizationUser>> GetManyByManyUsersAsync(IEnumerable<Guid> userIds)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from ou in dbContext.OrganizationUsers
where userIds.Contains(ou.Id)
select ou;
return Mapper.Map<List<TableModel.OrganizationUser>>(await query.ToListAsync());
}
}
public async Task<ICollection<OrganizationUser>> GetManyByOrganizationAsync(Guid organizationId, OrganizationUserType? type)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from ou in dbContext.OrganizationUsers
where ou.OrganizationId == organizationId &&
(type == null || ou.Type == type)
select ou;
return Mapper.Map<List<TableModel.OrganizationUser>>(await query.ToListAsync());
}
}
public async Task<ICollection<OrganizationUser>> GetManyByUserAsync(Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from ou in dbContext.OrganizationUsers
where ou.UserId == userId
select ou;
return Mapper.Map<List<TableModel.OrganizationUser>>(await query.ToListAsync());
}
}
public async Task<ICollection<OrganizationUserUserDetails>> GetManyDetailsByOrganizationAsync(Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var view = new OrganizationUserUserDetailsViewQuery();
var query = from ou in view.Run(dbContext)
where ou.OrganizationId == organizationId
select ou;
return await query.ToListAsync();
}
}
public async Task<ICollection<OrganizationUserOrganizationDetails>> GetManyDetailsByUserAsync(Guid userId,
OrganizationUserStatusType? status = null)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var view = new OrganizationUserOrganizationDetailsViewQuery();
var query = from ou in view.Run(dbContext)
where ou.UserId == userId &&
(status == null || ou.Status == status)
select ou;
var organizationUsers = await query.ToListAsync();
return organizationUsers;
}
}
public async Task<IEnumerable<OrganizationUserPublicKey>> GetManyPublicKeysByOrganizationUserAsync(Guid organizationId, IEnumerable<Guid> Ids)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from ou in dbContext.OrganizationUsers
where Ids.Contains(ou.Id) && ou.Status == OrganizationUserStatusType.Accepted
join u in dbContext.Users
on ou.UserId equals u.Id
where ou.OrganizationId == organizationId
select new { ou, u };
var data = await query
.Select(x => new OrganizationUserPublicKey()
{
Id = x.ou.Id,
PublicKey = x.u.PublicKey,
}).ToListAsync();
return data;
}
}
public async Task ReplaceAsync(OrganizationUser obj, IEnumerable<SelectionReadOnly> collections)
{
await base.ReplaceAsync(obj);
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var procedure = new OrganizationUserUpdateWithCollectionsQuery(obj, collections);
var update = procedure.Update.Run(dbContext);
dbContext.UpdateRange(await update.ToListAsync());
var insert = procedure.Insert.Run(dbContext);
await dbContext.AddRangeAsync(await insert.ToListAsync());
dbContext.RemoveRange(await procedure.Delete.Run(dbContext).ToListAsync());
await dbContext.SaveChangesAsync();
}
}
public async Task ReplaceManyAsync(IEnumerable<OrganizationUser> organizationUsers)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
dbContext.UpdateRange(organizationUsers);
await dbContext.SaveChangesAsync();
await UserBumpManyAccountRevisionDates(organizationUsers
.Where(ou => ou.UserId.HasValue)
.Select(ou => ou.UserId.Value).ToArray());
}
}
public async Task<IEnumerable<string>> SelectKnownEmailsAsync(Guid organizationId, IEnumerable<string> emails, bool onlyRegisteredUsers)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var usersQuery = from ou in dbContext.OrganizationUsers
join u in dbContext.Users
on ou.UserId equals u.Id into u_g
from u in u_g
where ou.OrganizationId == organizationId
select new { ou, u };
var ouu = await usersQuery.ToListAsync();
var ouEmails = ouu.Select(x => x.ou.Email);
var uEmails = ouu.Select(x => x.u.Email);
var knownEmails = from e in emails
where (ouEmails.Contains(e) || uEmails.Contains(e)) &&
(!onlyRegisteredUsers && (uEmails.Contains(e) || ouEmails.Contains(e))) ||
(onlyRegisteredUsers && uEmails.Contains(e))
select e;
return knownEmails;
}
}
public async Task UpdateGroupsAsync(Guid orgUserId, IEnumerable<Guid> groupIds)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var procedure = new GroupUserUpdateGroupsQuery(orgUserId, groupIds);
var insert = procedure.Insert.Run(dbContext);
var data = await insert.ToListAsync();
await dbContext.AddRangeAsync(data);
var delete = procedure.Delete.Run(dbContext);
var deleteData = await delete.ToListAsync();
dbContext.RemoveRange(deleteData);
await UserBumpAccountRevisionDateByOrganizationUserId(orgUserId);
await dbContext.SaveChangesAsync();
}
}
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);
}
Task<ICollection<string>> IOrganizationUserRepository.SelectKnownEmailsAsync(Guid organizationId, IEnumerable<string> emails, bool onlyRegisteredUsers) => throw new NotImplementedException();
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Bit.Core.Enums;
using Bit.Core.Models.Table;
using Bit.Core.Repositories.EntityFramework.Queries;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using DataModel = Bit.Core.Models.Data;
using EfModel = Bit.Core.Models.EntityFramework;
using TableModel = Bit.Core.Models.Table;
namespace Bit.Core.Repositories.EntityFramework
{
public class PolicyRepository : Repository<TableModel.Policy, EfModel.Policy, Guid>, IPolicyRepository
{
public PolicyRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Policies)
{ }
public async Task<Policy> GetByOrganizationIdTypeAsync(Guid organizationId, PolicyType type)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var results = await dbContext.Policies
.FirstOrDefaultAsync(p => p.OrganizationId == organizationId && p.Type == type);
return Mapper.Map<TableModel.Policy>(results);
}
}
public async Task<ICollection<Policy>> GetManyByOrganizationIdAsync(Guid organizationId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var results = await dbContext.Policies
.Where(p => p.OrganizationId == organizationId)
.ToListAsync();
return Mapper.Map<List<TableModel.Policy>>(results);
}
}
public async Task<ICollection<Policy>> GetManyByUserIdAsync(Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new PolicyReadByUserIdQuery(userId);
var results = await query.Run(dbContext).ToListAsync();
return Mapper.Map<List<TableModel.Policy>>(results);
}
}
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.Core.Models.Table.Provider;
using Bit.Core.Repositories.EntityFramework;
using TableModel = Bit.Core.Models.Table;
using EfModel = Bit.Core.Models.EntityFramework;
using Microsoft.Extensions.DependencyInjection;
using AutoMapper;
namespace Bit.Core.Repositories.EntityFramework
{
public class ProviderOrganizationProviderUserRepository :
Repository<TableModel.Provider.ProviderOrganizationProviderUser, EfModel.Provider.ProviderOrganizationProviderUser, Guid>, IProviderOrganizationProviderUserRepository
{
public ProviderOrganizationProviderUserRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.ProviderOrganizationProviderUsers)
{ }
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.Core.Models.Table.Provider;
using Bit.Core.Repositories.EntityFramework;
using TableModel = Bit.Core.Models.Table;
using EfModel = Bit.Core.Models.EntityFramework;
using Microsoft.Extensions.DependencyInjection;
using AutoMapper;
using Bit.Core.Models.Data;
using Bit.Core.Repositories.EntityFramework.Queries;
using Microsoft.EntityFrameworkCore;
namespace Bit.Core.Repositories.EntityFramework
{
public class ProviderOrganizationRepository :
Repository<TableModel.Provider.ProviderOrganization, EfModel.Provider.ProviderOrganization, Guid>, IProviderOrganizationRepository
{
public ProviderOrganizationRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.ProviderOrganizations)
{ }
public async Task<ICollection<ProviderOrganizationOrganizationDetails>> GetManyDetailsByProviderAsync(Guid providerId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new ProviderOrganizationOrganizationDetailsReadByProviderIdQuery(providerId);
var data = await query.Run(dbContext).ToListAsync();
return data;
}
}
}
}

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Models.Table.Provider;
using Bit.Core.Repositories.EntityFramework;
using TableModel = Bit.Core.Models.Table;
using EfModel = Bit.Core.Models.EntityFramework;
using Microsoft.Extensions.DependencyInjection;
using AutoMapper;
using Microsoft.EntityFrameworkCore;
using Bit.Core.Models.Data;
namespace Bit.Core.Repositories.EntityFramework
{
public class ProviderRepository : Repository<TableModel.Provider.Provider, EfModel.Provider.Provider, Guid>, IProviderRepository
{
public ProviderRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.Providers)
{ }
public Task<ICollection<ProviderAbility>> GetManyAbilitiesAsync() => throw new NotImplementedException();
public async Task<ICollection<Provider>> SearchAsync(string name, string userEmail, int skip, int take)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = !string.IsNullOrWhiteSpace(userEmail) ?
(from p in dbContext.Providers
join pu in dbContext.ProviderUsers
on p.Id equals pu.ProviderId
join u in dbContext.Users
on pu.UserId equals u.Id
where (string.IsNullOrWhiteSpace(name) || p.Name.Contains(name)) &&
u.Email == userEmail
orderby p.CreationDate descending
select new { p, pu, u }).Skip(skip).Take(take).Select(x => x.p) :
(from p in dbContext.Providers
where string.IsNullOrWhiteSpace(name) || p.Name.Contains(name)
orderby p.CreationDate descending
select new { p }).Skip(skip).Take(take).Select(x => x.p);
return await query.ToArrayAsync();
}
}
}
}

View File

@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Models.Table.Provider;
using TableModel = Bit.Core.Models.Table;
using EfModel = Bit.Core.Models.EntityFramework;
using Microsoft.Extensions.DependencyInjection;
using AutoMapper;
using Bit.Core.Enums.Provider;
using Microsoft.EntityFrameworkCore;
using Bit.Core.Models.Data;
using Bit.Core.Repositories.EntityFramework.Queries;
namespace Bit.Core.Repositories.EntityFramework
{
public class ProviderUserRepository :
Repository<TableModel.Provider.ProviderUser, EfModel.Provider.ProviderUser, Guid>, IProviderUserRepository
{
public ProviderUserRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.ProviderUsers)
{ }
public async Task<int> GetCountByProviderAsync(Guid providerId, string email, bool onlyRegisteredUsers)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from pu in dbContext.ProviderUsers
join u in dbContext.Users
on pu.UserId equals u.Id into u_g
from u in u_g.DefaultIfEmpty()
where pu.ProviderId == providerId &&
((!onlyRegisteredUsers && (pu.Email == email || u.Email == email)) ||
(onlyRegisteredUsers && u.Email == email))
select new { pu, u };
return await query.CountAsync();
}
}
public async Task<ICollection<ProviderUser>> GetManyAsync(IEnumerable<Guid> ids)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = dbContext.ProviderUsers.Where(item => ids.Contains(item.Id));
return await query.ToArrayAsync();
}
}
public async Task<ICollection<ProviderUser>> GetManyByProviderAsync(Guid providerId, ProviderUserType? type = null)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = dbContext.ProviderUsers.Where(pu => pu.ProviderId.Equals(providerId) &&
(type != null && pu.Type.Equals(type)));
return await query.ToArrayAsync();
}
}
public async Task DeleteManyAsync(IEnumerable<Guid> providerUserIds)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
await UserBumpAccountRevisionDateByProviderUserIds(providerUserIds.ToArray());
var entities = dbContext.ProviderUsers.Where(pu => providerUserIds.Contains(pu.Id));
dbContext.ProviderUsers.RemoveRange(entities);
await dbContext.SaveChangesAsync();
}
}
public async Task<ICollection<ProviderUser>> GetManyByUserAsync(Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from pu in dbContext.ProviderUsers
where pu.UserId == userId
select pu;
return await query.ToArrayAsync();
}
}
public async Task<ProviderUser> GetByProviderUserAsync(Guid providerId, Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = from pu in dbContext.ProviderUsers
where pu.UserId == userId &&
pu.ProviderId == providerId
select pu;
return await query.FirstOrDefaultAsync();
}
}
public async Task<ICollection<ProviderUserUserDetails>> GetManyDetailsByProviderAsync(Guid providerId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var view = from pu in dbContext.ProviderUsers
join u in dbContext.Users
on pu.UserId equals u.Id into u_g
from u in u_g.DefaultIfEmpty()
select new {pu, u};
var data = await view.Where(e => e.pu.ProviderId == providerId).Select(e => new ProviderUserUserDetails
{
Id = e.pu.Id,
UserId = e.pu.UserId,
ProviderId = e.pu.ProviderId,
Name = e.u.Name,
Email = e.u.Email ?? e.pu.Email,
Status = e.pu.Status,
Type = e.pu.Type,
Permissions = e.pu.Permissions,
}).ToArrayAsync();
return data;
}
}
public async Task<ICollection<ProviderUserProviderDetails>> GetManyDetailsByUserAsync(Guid userId, ProviderUserStatusType? status = null)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new ProviderUserProviderDetailsReadByUserIdStatusQuery(userId, status);
var data = await query.Run(dbContext).ToArrayAsync();
return data;
}
}
public async Task<IEnumerable<ProviderUserPublicKey>> GetManyPublicKeysByProviderUserAsync(Guid providerId, IEnumerable<Guid> Ids)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var query = new UserReadPublicKeysByProviderUserIdsQuery(providerId, Ids);
var data = await query.Run(dbContext).ToListAsync();
return data;
}
}
}
}

View File

@ -0,0 +1,40 @@
using System.Linq;
using System;
using System.Collections.Generic;
using Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class CipherDetailsQuery : IQuery<CipherDetails>
{
private readonly Guid? _userId;
private readonly bool _ignoreFolders;
public CipherDetailsQuery(Guid? userId, bool ignoreFolders = false)
{
_userId = userId;
_ignoreFolders = ignoreFolders;
}
public virtual IQueryable<CipherDetails> Run(DatabaseContext dbContext)
{
var query = from c in dbContext.Ciphers
select new CipherDetails
{
Id = c.Id,
UserId = c.UserId,
OrganizationId = c.OrganizationId,
Type= c.Type,
Data = c.Data,
Attachments = c.Attachments,
CreationDate = c.CreationDate,
RevisionDate = c.RevisionDate,
DeletedDate = c.DeletedDate,
Favorite = _userId.HasValue && c.Favorites != null && c.Favorites.Contains($"\"{_userId}\":true"),
FolderId = (_ignoreFolders || !_userId.HasValue || c.Folders == null || !c.Folders.Contains(_userId.Value.ToString())) ?
null :
CoreHelpers.LoadClassFromJsonData<Dictionary<Guid, Guid>>(c.Folders)[_userId.Value],
};
return query;
}
}
}

View File

@ -0,0 +1,41 @@
using System.Linq;
using System;
using Core.Models.Data;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class CipherOrganizationDetailsReadByIdQuery : IQuery<CipherOrganizationDetails>
{
private readonly Guid _cipherId;
public CipherOrganizationDetailsReadByIdQuery(Guid cipherId)
{
_cipherId = cipherId;
}
public virtual IQueryable<CipherOrganizationDetails> Run(DatabaseContext dbContext)
{
var query = from c in dbContext.Ciphers
join o in dbContext.Organizations
on c.OrganizationId equals o.Id into o_g
from o in o_g.DefaultIfEmpty()
where c.Id == _cipherId
select new CipherOrganizationDetails
{
Id = c.Id,
UserId = c.UserId,
OrganizationId = c.OrganizationId,
Type = c.Type,
Data = c.Data,
Favorites = c.Favorites,
Folders = c.Folders,
Attachments = c.Attachments,
CreationDate = c.CreationDate,
RevisionDate = c.RevisionDate,
DeletedDate = c.DeletedDate,
OrganizationUseTotp = o.UseTotp,
};
return query;
}
}
}

View File

@ -0,0 +1,58 @@
using System.Linq;
using Bit.Core.Models.EntityFramework;
using System;
using Bit.Core.Enums;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class CipherReadCanEditByIdUserIdQuery : IQuery<Cipher>
{
private readonly Guid _userId;
private readonly Guid _cipherId;
public CipherReadCanEditByIdUserIdQuery(Guid userId, Guid cipherId)
{
_userId = userId;
_cipherId = cipherId;
}
public virtual IQueryable<Cipher> Run(DatabaseContext dbContext)
{
var query = from c in dbContext.Ciphers
join o in dbContext.Organizations
on c.OrganizationId equals o.Id into o_g
from o in o_g.DefaultIfEmpty()
where !c.UserId.HasValue
join ou in dbContext.OrganizationUsers
on o.Id equals ou.OrganizationId into ou_g
from ou in ou_g.DefaultIfEmpty()
where ou.UserId == _userId
join cc in dbContext.CollectionCiphers
on c.Id equals cc.CipherId into cc_g
from cc in cc_g.DefaultIfEmpty()
where !c.UserId.HasValue && !ou.AccessAll
join cu in dbContext.CollectionUsers
on cc.CollectionId equals cu.CollectionId into cu_g
from cu in cu_g.DefaultIfEmpty()
where ou.Id == cu.OrganizationUserId
join gu in dbContext.GroupUsers
on ou.Id equals gu.OrganizationUserId into gu_g
from gu in gu_g.DefaultIfEmpty()
where !c.UserId.HasValue && cu.CollectionId == null && !ou.AccessAll
join g in dbContext.Groups
on gu.GroupId equals g.Id into g_g
from g in g_g.DefaultIfEmpty()
join cg in dbContext.CollectionGroups
on gu.GroupId equals cg.GroupId into cg_g
from cg in cg_g.DefaultIfEmpty()
where !g.AccessAll && cg.CollectionId == cc.CollectionId &&
(c.Id == _cipherId &&
(c.UserId == _userId ||
(!c.UserId.HasValue && ou.Status == OrganizationUserStatusType.Confirmed && o.Enabled &&
(ou.AccessAll || cu.CollectionId != null || g.AccessAll || cg.CollectionId != null)))) &&
(c.UserId.HasValue || ou.AccessAll || !cu.ReadOnly || g.AccessAll || !cg.ReadOnly)
select c;
return query;
}
}
}

View File

@ -0,0 +1,69 @@
using System.Linq;
using Bit.Core.Models.EntityFramework;
using System;
using Bit.Core.Enums;
using System.Collections.Generic;
using Table = Bit.Core.Models.Table;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class CipherUpdateCollectionsQuery : IQuery<CollectionCipher>
{
private readonly Table.Cipher _cipher;
private readonly IEnumerable<Guid> _collectionIds;
public CipherUpdateCollectionsQuery(Table.Cipher cipher, IEnumerable<Guid> collectionIds)
{
_cipher = cipher;
_collectionIds = collectionIds;
}
public virtual IQueryable<CollectionCipher> Run(DatabaseContext dbContext)
{
if (!_cipher.OrganizationId.HasValue || !_collectionIds.Any())
{
return null;
}
var availibleCollections = !_cipher.UserId.HasValue ?
from c in dbContext.Collections
where c.OrganizationId == _cipher.OrganizationId
select c.Id :
from c in dbContext.Collections
join o in dbContext.Organizations
on c.OrganizationId equals o.Id
join ou in dbContext.OrganizationUsers
on o.Id equals ou.OrganizationId
where ou.UserId == _cipher.UserId
join cu in dbContext.CollectionUsers
on c.Id equals cu.CollectionId into cu_g
from cu in cu_g.DefaultIfEmpty()
where !ou.AccessAll && cu.OrganizationUserId == ou.Id
join gu in dbContext.GroupUsers
on ou.Id equals gu.OrganizationUserId into gu_g
from gu in gu_g.DefaultIfEmpty()
where cu.CollectionId == null && !ou.AccessAll
join g in dbContext.Groups
on gu.GroupId equals g.Id into g_g
from g in g_g.DefaultIfEmpty()
join cg in dbContext.CollectionGroups
on c.Id equals cg.CollectionId into cg_g
from cg in cg_g.DefaultIfEmpty()
where !g.AccessAll && gu.GroupId == cg.GroupId &&
o.Id == _cipher.OrganizationId &&
o.Enabled &&
ou.Status == OrganizationUserStatusType.Confirmed &&
(ou.AccessAll || !cu.ReadOnly || g.AccessAll || !cg.ReadOnly)
select c.Id;
if (!availibleCollections.Any())
{
return null;
}
var query = from c in availibleCollections
select new CollectionCipher { CollectionId = c, CipherId = _cipher.Id };
return query;
}
}
}

View File

@ -0,0 +1,22 @@
using System.Linq;
using Bit.Core.Models.EntityFramework;
using System;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class CollectionCipherReadByUserIdCipherIdQuery : CollectionCipherReadByUserIdQuery
{
private readonly Guid _cipherId;
public CollectionCipherReadByUserIdCipherIdQuery(Guid userId, Guid cipherId) : base(userId)
{
_cipherId = cipherId;
}
public override IQueryable<CollectionCipher> Run(DatabaseContext dbContext)
{
var query = base.Run(dbContext);
return query.Where(x => x.CipherId == _cipherId);
}
}
}

View File

@ -0,0 +1,46 @@
using System.Linq;
using Bit.Core.Models.EntityFramework;
using System;
using Bit.Core.Enums;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class CollectionCipherReadByUserIdQuery : IQuery<CollectionCipher>
{
private readonly Guid _userId;
public CollectionCipherReadByUserIdQuery(Guid userId)
{
_userId = userId;
}
public virtual IQueryable<CollectionCipher> Run(DatabaseContext dbContext)
{
var query = from cc in dbContext.CollectionCiphers
join c in dbContext.Collections
on cc.CollectionId equals c.Id
join ou in dbContext.OrganizationUsers
on c.OrganizationId equals ou.OrganizationId
where ou.UserId == _userId
join cu in dbContext.CollectionUsers
on c.Id equals cu.CollectionId into cu_g
from cu in cu_g
where ou.AccessAll && cu.OrganizationUserId == ou.Id
join gu in dbContext.GroupUsers
on ou.Id equals gu.OrganizationUserId into gu_g
from gu in gu_g
where cu.CollectionId == null && !ou.AccessAll
join g in dbContext.Groups
on gu.GroupId equals g.Id into g_g
from g in g_g
join cg in dbContext.CollectionGroups
on cc.CollectionId equals cg.CollectionId into cg_g
from cg in cg_g
where g.AccessAll && cg.GroupId == gu.GroupId &&
ou.Status == OrganizationUserStatusType.Confirmed &&
(ou.AccessAll || cu.CollectionId != null || g.AccessAll || cg.CollectionId != null)
select cc;
return query;
}
}
}

View File

@ -0,0 +1,24 @@
using System.Linq;
using Bit.Core.Models.EntityFramework;
using System;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class CollectionReadCountByOrganizationIdQuery : IQuery<Collection>
{
private readonly Guid _organizationId;
public CollectionReadCountByOrganizationIdQuery(Guid organizationId)
{
_organizationId = organizationId;
}
public IQueryable<Collection> Run(DatabaseContext dbContext)
{
var query = from c in dbContext.Collections
where c.OrganizationId == _organizationId
select c;
return query;
}
}
}

View File

@ -0,0 +1,121 @@
using System.Collections.Generic;
using System.Linq;
using EfModel = Bit.Core.Models.EntityFramework;
using Bit.Core.Models.Data;
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class CollectionUserUpdateUsersQuery
{
public readonly CollectionUserUpdateUsersInsertQuery Insert;
public readonly CollectionUserUpdateUsersUpdateQuery Update;
public readonly CollectionUserUpdateUsersDeleteQuery Delete;
public CollectionUserUpdateUsersQuery(Guid collectionId, IEnumerable<SelectionReadOnly> users)
{
Insert = new CollectionUserUpdateUsersInsertQuery(collectionId, users);
Update = new CollectionUserUpdateUsersUpdateQuery(collectionId, users);
Delete = new CollectionUserUpdateUsersDeleteQuery(collectionId, users);
}
}
public class CollectionUserUpdateUsersInsertQuery : IQuery<EfModel.OrganizationUser>
{
private readonly Guid _collectionId;
private readonly IEnumerable<SelectionReadOnly> _users;
public CollectionUserUpdateUsersInsertQuery(Guid collectionId, IEnumerable<SelectionReadOnly> users)
{
_collectionId = collectionId;
_users = users;
}
public IQueryable<EfModel.OrganizationUser> Run(DatabaseContext dbContext)
{
var orgId = dbContext.Collections.FirstOrDefault(c => c.Id == _collectionId)?.OrganizationId;
var organizationUserIds = _users.Select(u => u.Id);
var insertQuery = from ou in dbContext.OrganizationUsers
where
organizationUserIds.Contains(ou.Id) &&
ou.OrganizationId == orgId &&
!dbContext.CollectionUsers.Any(
x => x.CollectionId != _collectionId && x.OrganizationUserId == ou.Id)
select ou;
return insertQuery;
}
public async Task<IEnumerable<EfModel.CollectionUser>> BuildInMemory(DatabaseContext dbContext)
{
var data = await Run(dbContext).ToListAsync();
var collectionUsers = data.Select(x => new EfModel.CollectionUser()
{
CollectionId = _collectionId,
OrganizationUserId = x.Id,
ReadOnly = _users.FirstOrDefault(u => u.Id.Equals(x.Id)).ReadOnly,
HidePasswords = _users.FirstOrDefault(u => u.Id.Equals(x.Id)).HidePasswords,
});
return collectionUsers;
}
}
public class CollectionUserUpdateUsersUpdateQuery: IQuery<EfModel.CollectionUser>
{
private readonly Guid _collectionId;
private readonly IEnumerable<SelectionReadOnly> _users;
public CollectionUserUpdateUsersUpdateQuery(Guid collectionId, IEnumerable<SelectionReadOnly> users)
{
_collectionId = collectionId;
_users = users;
}
public IQueryable<EfModel.CollectionUser> Run(DatabaseContext dbContext)
{
var orgId = dbContext.Collections.FirstOrDefault(c => c.Id == _collectionId)?.OrganizationId;
var ids = _users.Select(x => x.Id);
var updateQuery = from target in dbContext.CollectionUsers
where target.CollectionId == _collectionId &&
ids.Contains(target.OrganizationUserId)
select target;
return updateQuery;
}
public async Task<IEnumerable<EfModel.CollectionUser>> BuildInMemory(DatabaseContext dbContext)
{
var data = await Run(dbContext).ToListAsync();
var collectionUsers = data.Select(x => new EfModel.CollectionUser
{
CollectionId = _collectionId,
OrganizationUserId = x.OrganizationUserId,
ReadOnly = _users.FirstOrDefault(u => u.Id.Equals(x.OrganizationUserId)).ReadOnly,
HidePasswords = _users.FirstOrDefault(u => u.Id.Equals(x.OrganizationUserId)).HidePasswords,
});
return collectionUsers;
}
}
public class CollectionUserUpdateUsersDeleteQuery: IQuery<EfModel.CollectionUser>
{
private readonly Guid _collectionId;
private readonly IEnumerable<SelectionReadOnly> _users;
public CollectionUserUpdateUsersDeleteQuery(Guid collectionId, IEnumerable<SelectionReadOnly> users)
{
_collectionId = collectionId;
_users = users;
}
public IQueryable<EfModel.CollectionUser> Run(DatabaseContext dbContext)
{
var orgId = dbContext.Collections.FirstOrDefault(c => c.Id == _collectionId)?.OrganizationId;
var deleteQuery = from cu in dbContext.CollectionUsers
where !dbContext.Users.Any(
u => u.Id == cu.OrganizationUserId)
select cu;
return deleteQuery;
}
}
}

View File

@ -0,0 +1,39 @@
using System.Linq;
using Bit.Core.Models.Data;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class EmergencyAccessDetailsViewQuery : IQuery<EmergencyAccessDetails>
{
public IQueryable<EmergencyAccessDetails> Run(DatabaseContext dbContext)
{
var query = from ea in dbContext.EmergencyAccesses
join grantee in dbContext.Users
on ea.GranteeId equals grantee.Id into grantee_g
from grantee in grantee_g.DefaultIfEmpty()
join grantor in dbContext.Users
on ea.GrantorId equals grantor.Id into grantor_g
from grantor in grantor_g.DefaultIfEmpty()
select new {ea, grantee, grantor};
return query.Select(x => new EmergencyAccessDetails
{
Id = x.ea.Id,
GrantorId = x.ea.GrantorId,
GranteeId = x.ea.GranteeId,
Email = x.ea.Email,
KeyEncrypted = x.ea.KeyEncrypted,
Type = x.ea.Type,
Status = x.ea.Status,
WaitTimeDays = x.ea.WaitTimeDays,
RecoveryInitiatedDate = x.ea.RecoveryInitiatedDate,
LastNotificationDate = x.ea.LastNotificationDate,
CreationDate = x.ea.CreationDate,
RevisionDate = x.ea.RevisionDate,
GranteeName = x.grantee.Name,
GranteeEmail = x.grantee.Email,
GrantorName = x.grantor.Name,
GrantorEmail = x.grantor.Email,
});
}
}
}

View File

@ -0,0 +1,33 @@
using System.Linq;
using Bit.Core.Models.EntityFramework;
using System;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class EmergencyAccessReadCountByGrantorIdEmailQuery : IQuery<EmergencyAccess>
{
private readonly Guid _grantorId;
private readonly string _email;
private readonly bool _onlyRegisteredUsers;
public EmergencyAccessReadCountByGrantorIdEmailQuery(Guid grantorId, string email, bool onlyRegisteredUsers)
{
_grantorId = grantorId;
_email = email;
_onlyRegisteredUsers = onlyRegisteredUsers;
}
public IQueryable<EmergencyAccess> Run(DatabaseContext dbContext)
{
var query = from ea in dbContext.EmergencyAccesses
join u in dbContext.Users
on ea.GranteeId equals u.Id into u_g
from u in u_g.DefaultIfEmpty()
where ea.GrantorId == _grantorId &&
((!_onlyRegisteredUsers && (ea.Email == _email || u.Email == _email))
|| (_onlyRegisteredUsers && u.Email == _email))
select ea;
return query;
}
}
}

View File

@ -0,0 +1,50 @@
using System.Linq;
using Bit.Core.Models.EntityFramework;
using System;
using Bit.Core.Models.Data;
using Table = Bit.Core.Models.Table;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class EventReadPageByCipherIdQuery : IQuery<Event>
{
private readonly Table.Cipher _cipher;
private readonly DateTime _startDate;
private readonly DateTime _endDate;
private readonly DateTime? _beforeDate;
private readonly PageOptions _pageOptions;
public EventReadPageByCipherIdQuery(Table.Cipher cipher, DateTime startDate, DateTime endDate, PageOptions pageOptions)
{
_cipher = cipher;
_startDate = startDate;
_endDate = endDate;
_beforeDate = null;
_pageOptions = pageOptions;
}
public EventReadPageByCipherIdQuery(Table.Cipher cipher, DateTime startDate, DateTime endDate, DateTime? beforeDate, PageOptions pageOptions)
{
_cipher = cipher;
_startDate = startDate;
_endDate = endDate;
_beforeDate = beforeDate;
_pageOptions = pageOptions;
}
public IQueryable<Event> Run(DatabaseContext dbContext)
{
var q = from e in dbContext.Events
where e.Date >= _startDate &&
(_beforeDate == null || e.Date < _beforeDate.Value) &&
((!_cipher.OrganizationId.HasValue && !e.OrganizationId.HasValue) ||
(_cipher.OrganizationId.HasValue && _cipher.OrganizationId == e.OrganizationId)) &&
((!_cipher.UserId.HasValue && !e.UserId.HasValue) ||
(_cipher.UserId.HasValue && _cipher.UserId == e.UserId)) &&
_cipher.Id == e.CipherId
orderby e.Date descending
select e;
return q.Skip(0).Take(_pageOptions.PageSize);
}
}
}

View File

@ -0,0 +1,41 @@
using System.Linq;
using Bit.Core.Models.EntityFramework;
using System;
using Bit.Core.Models.Data;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class EventReadPageByOrganizationIdActingUserIdQuery : IQuery<Event>
{
private readonly Guid _organizationId;
private readonly Guid _actingUserId;
private readonly DateTime _startDate;
private readonly DateTime _endDate;
private readonly DateTime? _beforeDate;
private readonly PageOptions _pageOptions;
public EventReadPageByOrganizationIdActingUserIdQuery(Guid organizationId, Guid actingUserId,
DateTime startDate, DateTime endDate, DateTime? beforeDate, PageOptions pageOptions)
{
_organizationId = organizationId;
_actingUserId = actingUserId;
_startDate = startDate;
_endDate = endDate;
_beforeDate = beforeDate;
_pageOptions = pageOptions;
}
public IQueryable<Event> Run(DatabaseContext dbContext)
{
var q = from e in dbContext.Events
where e.Date >= _startDate &&
(_beforeDate != null || e.Date <= _endDate) &&
(_beforeDate == null || e.Date < _beforeDate.Value) &&
e.OrganizationId == _organizationId &&
e.ActingUserId == _actingUserId
orderby e.Date descending
select e;
return q.Skip(0).Take(_pageOptions.PageSize);
}
}
}

View File

@ -0,0 +1,38 @@
using System.Linq;
using Bit.Core.Models.EntityFramework;
using System;
using Bit.Core.Models.Data;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class EventReadPageByOrganizationIdQuery: IQuery<Event>
{
private readonly Guid _organizationId;
private readonly DateTime _startDate;
private readonly DateTime _endDate;
private readonly DateTime? _beforeDate;
private readonly PageOptions _pageOptions;
public EventReadPageByOrganizationIdQuery(Guid organizationId, DateTime startDate,
DateTime endDate, DateTime? beforeDate, PageOptions pageOptions)
{
_organizationId = organizationId;
_startDate = startDate;
_endDate = endDate;
_beforeDate = beforeDate;
_pageOptions = pageOptions;
}
public IQueryable<Event> Run(DatabaseContext dbContext)
{
var q = from e in dbContext.Events
where e.Date >= _startDate &&
(_beforeDate != null || e.Date <= _endDate) &&
(_beforeDate == null || e.Date < _beforeDate.Value) &&
e.OrganizationId == _organizationId
orderby e.Date descending
select e;
return q.Skip(0).Take(_pageOptions.PageSize);
}
}
}

View File

@ -0,0 +1,39 @@
using System.Linq;
using Bit.Core.Models.EntityFramework;
using System;
using Bit.Core.Models.Data;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class EventReadPageByUserIdQuery: IQuery<Event>
{
private readonly Guid _userId;
private readonly DateTime _startDate;
private readonly DateTime _endDate;
private readonly DateTime? _beforeDate;
private readonly PageOptions _pageOptions;
public EventReadPageByUserIdQuery(Guid userId, DateTime startDate,
DateTime endDate, DateTime? beforeDate, PageOptions pageOptions)
{
_userId = userId;
_startDate = startDate;
_endDate = endDate;
_beforeDate = beforeDate;
_pageOptions = pageOptions;
}
public IQueryable<Event> Run(DatabaseContext dbContext)
{
var q = from e in dbContext.Events
where e.Date >= _startDate &&
(_beforeDate != null || e.Date <= _endDate) &&
(_beforeDate == null || e.Date < _beforeDate.Value) &&
!e.OrganizationId.HasValue &&
e.ActingUserId == _userId
orderby e.Date descending
select e;
return q.Skip(0).Take(_pageOptions.PageSize);
}
}
}

View File

@ -0,0 +1,73 @@
using System.Linq;
using Bit.Core.Models.EntityFramework;
using System;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public class GroupUserUpdateGroupsQuery
{
public readonly GroupUserUpdateGroupsInsertQuery Insert;
public readonly GroupUserUpdateGroupsDeleteQuery Delete;
public GroupUserUpdateGroupsQuery(Guid organizationUserId, IEnumerable<Guid> groupIds)
{
Insert = new GroupUserUpdateGroupsInsertQuery(organizationUserId, groupIds);
Delete = new GroupUserUpdateGroupsDeleteQuery(organizationUserId, groupIds);
}
}
public class GroupUserUpdateGroupsInsertQuery : IQuery<GroupUser>
{
private readonly Guid _organizationUserId;
private readonly IEnumerable<Guid> _groupIds;
public GroupUserUpdateGroupsInsertQuery(Guid organizationUserId, IEnumerable<Guid> collections)
{
_organizationUserId = organizationUserId;
_groupIds = collections;
}
public IQueryable<GroupUser> Run(DatabaseContext dbContext)
{
var orgUser = from ou in dbContext.OrganizationUsers
where ou.Id == _organizationUserId
select ou;
var groupIdEntities = dbContext.Groups.Where(x => _groupIds.Contains(x.Id));
var query = from g in dbContext.Groups
join ou in orgUser
on g.OrganizationId equals ou.OrganizationId
join gie in groupIdEntities
on g.Id equals gie.Id
where !dbContext.GroupUsers.Any(gu => _groupIds.Contains(gu.GroupId) && gu.OrganizationUserId == _organizationUserId)
select g;
return query.Select(x => new GroupUser
{
GroupId = x.Id,
OrganizationUserId = _organizationUserId,
});
}
}
public class GroupUserUpdateGroupsDeleteQuery : IQuery<GroupUser>
{
private readonly Guid _organizationUserId;
private readonly IEnumerable<Guid> _groupIds;
public GroupUserUpdateGroupsDeleteQuery(Guid organizationUserId, IEnumerable<Guid> groupIds)
{
_organizationUserId = organizationUserId;
_groupIds = groupIds;
}
public IQueryable<GroupUser> Run(DatabaseContext dbContext)
{
var deleteQuery = from gu in dbContext.GroupUsers
where gu.OrganizationUserId == _organizationUserId &&
!_groupIds.Any(x => gu.GroupId == x)
select gu;
return deleteQuery;
}
}
}

View File

@ -0,0 +1,9 @@
using System.Linq;
namespace Bit.Core.Repositories.EntityFramework.Queries
{
public interface IQuery<TOut>
{
IQueryable<TOut> Run(DatabaseContext dbContext);
}
}

Some files were not shown because too many files have changed in this diff Show More