mirror of
https://github.com/bitwarden/server.git
synced 2024-11-24 12:35:25 +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:
parent
be13eb153a
commit
b13dda2799
@ -1,9 +1,9 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Version>1.41.5</Version>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -25,7 +25,7 @@ Please read the [Setup guide](https://github.com/bitwarden/server/blob/master/SE
|
||||
|
||||
### 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)
|
||||
|
||||
*These dependencies are free to use.*
|
||||
|
2
SETUP.md
2
SETUP.md
@ -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.
|
||||
|
||||
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.
|
||||
|
||||
|
@ -72,6 +72,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommCore.Test", "bitwarden_
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test - Bitwarden License", "test - Bitwarden License", "{287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}"
|
||||
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
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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}.Release|Any CPU.ActiveCfg = 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.Build.0 = Debug|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}
|
||||
{4866AF64-6640-4C65-A662-A31E02FF9064} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}
|
||||
{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}
|
||||
{0E99A21B-684B-4C59-9831-90F775CAB6F7} = {287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}
|
||||
EndGlobalSection
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
@ -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"
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
</Choose>
|
||||
|
||||
<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="Swashbuckle.AspNetCore" Version="5.5.1" />
|
||||
<PackageReference Include="Microsoft.Azure.EventGrid" Version="3.2.0" />
|
||||
|
@ -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"
|
||||
|
||||
|
@ -27,18 +27,20 @@
|
||||
<PackageReference Include="Fido2.AspNet" Version="1.1.0" />
|
||||
<PackageReference Include="Handlebars.Net" Version="1.10.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="Microsoft.AspNetCore.DataProtection.AzureStorage" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.AzureStorage" Version="3.1.16" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.2" />
|
||||
<PackageReference Include="Microsoft.Azure.Cosmos.Table" Version="1.0.7" />
|
||||
<PackageReference Include="Microsoft.Azure.NotificationHubs" Version="3.3.0" />
|
||||
<PackageReference Include="Microsoft.Azure.ServiceBus" Version="4.1.3" />
|
||||
<PackageReference Include="Microsoft.Azure.Storage.Blob" Version="11.1.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.6" />
|
||||
<PackageReference Include="Npgsql" Version="4.1.4" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="5.0.2" />
|
||||
<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="Serilog.AspNetCore" Version="3.4.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
|
||||
|
9
src/Core/Enums/CipherStateAction.cs
Normal file
9
src/Core/Enums/CipherStateAction.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Bit.Core.Enums
|
||||
{
|
||||
public enum CipherStateAction
|
||||
{
|
||||
Restore,
|
||||
SoftDelete,
|
||||
HardDelete,
|
||||
}
|
||||
}
|
10
src/Core/Enums/SupportedDatabaseProviders.cs
Normal file
10
src/Core/Enums/SupportedDatabaseProviders.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Bit.Core.Enums
|
||||
{
|
||||
public enum SupportedDatabaseProviders
|
||||
{
|
||||
SqlServer,
|
||||
MySql,
|
||||
Postgres,
|
||||
}
|
||||
}
|
||||
|
@ -52,8 +52,8 @@ namespace Bit.Core.Models.Api
|
||||
public bool UseBusinessPortal => UsePolicies || UseSso; // TODO add events if needed
|
||||
public bool UsersGetPremium { get; set; }
|
||||
public bool SelfHost { get; set; }
|
||||
public int Seats { get; set; }
|
||||
public int MaxCollections { get; set; }
|
||||
public int? Seats { get; set; }
|
||||
public short? MaxCollections { get; set; }
|
||||
public short? MaxStorageGb { get; set; }
|
||||
public string Key { get; set; }
|
||||
public OrganizationUserStatusType Status { get; set; }
|
||||
|
10
src/Core/Models/Data/GroupWithCollections.cs
Normal file
10
src/Core/Models/Data/GroupWithCollections.cs
Normal 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; }
|
||||
}
|
||||
}
|
@ -19,8 +19,8 @@ namespace Bit.Core.Models.Data
|
||||
public bool UseBusinessPortal => UsePolicies || UseSso;
|
||||
public bool SelfHost { get; set; }
|
||||
public bool UsersGetPremium { get; set; }
|
||||
public int Seats { get; set; }
|
||||
public int MaxCollections { get; set; }
|
||||
public int? Seats { get; set; }
|
||||
public short? MaxCollections { get; set; }
|
||||
public short? MaxStorageGb { get; set; }
|
||||
public string Key { get; set; }
|
||||
public Enums.OrganizationUserStatusType Status { get; set; }
|
||||
|
10
src/Core/Models/Data/OrganizationUserWithCollections.cs
Normal file
10
src/Core/Models/Data/OrganizationUserWithCollections.cs
Normal 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; }
|
||||
}
|
||||
}
|
@ -1,57 +1,14 @@
|
||||
using System.Text.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using AutoMapper;
|
||||
|
||||
namespace Bit.Core.Models.EntityFramework
|
||||
{
|
||||
public class Cipher : Table.Cipher
|
||||
{
|
||||
private JsonDocument _dataJson;
|
||||
private JsonDocument _attachmentsJson;
|
||||
private JsonDocument _favoritesJson;
|
||||
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 virtual User User { get; set; }
|
||||
public virtual Organization Organization { get; set; }
|
||||
public virtual ICollection<CollectionCipher> CollectionCiphers { get; set; }
|
||||
}
|
||||
|
||||
public class CipherMapperProfile : Profile
|
||||
|
22
src/Core/Models/EntityFramework/Collection.cs
Normal file
22
src/Core/Models/EntityFramework/Collection.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
20
src/Core/Models/EntityFramework/CollectionCipher.cs
Normal file
20
src/Core/Models/EntityFramework/CollectionCipher.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
18
src/Core/Models/EntityFramework/CollectionGroup.cs
Normal file
18
src/Core/Models/EntityFramework/CollectionGroup.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
18
src/Core/Models/EntityFramework/CollectionUser.cs
Normal file
18
src/Core/Models/EntityFramework/CollectionUser.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
19
src/Core/Models/EntityFramework/Device.cs
Normal file
19
src/Core/Models/EntityFramework/Device.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
20
src/Core/Models/EntityFramework/EmergencyAccess.cs
Normal file
20
src/Core/Models/EntityFramework/EmergencyAccess.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
18
src/Core/Models/EntityFramework/Event.cs
Normal file
18
src/Core/Models/EntityFramework/Event.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
19
src/Core/Models/EntityFramework/Folder.cs
Normal file
19
src/Core/Models/EntityFramework/Folder.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
18
src/Core/Models/EntityFramework/Grant.cs
Normal file
18
src/Core/Models/EntityFramework/Grant.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
20
src/Core/Models/EntityFramework/Group.cs
Normal file
20
src/Core/Models/EntityFramework/Group.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
21
src/Core/Models/EntityFramework/GroupUser.cs
Normal file
21
src/Core/Models/EntityFramework/GroupUser.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
src/Core/Models/EntityFramework/Installation.cs
Normal file
18
src/Core/Models/EntityFramework/Installation.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using AutoMapper;
|
||||
|
||||
namespace Bit.Core.Models.EntityFramework
|
||||
{
|
||||
public class Organization : Table.Organization
|
||||
{
|
||||
private JsonDocument _twoFactorProvidersJson;
|
||||
|
||||
public ICollection<Cipher> Ciphers { get; set; }
|
||||
|
||||
[IgnoreMap]
|
||||
public JsonDocument TwoFactorProvidersJson
|
||||
{
|
||||
get => _twoFactorProvidersJson;
|
||||
set
|
||||
{
|
||||
TwoFactorProviders = value?.ToString();
|
||||
_twoFactorProvidersJson = value;
|
||||
}
|
||||
}
|
||||
public virtual ICollection<Cipher> Ciphers { get; set; }
|
||||
public virtual ICollection<OrganizationUser> OrganizationUsers { get; set; }
|
||||
public virtual ICollection<Group> Groups { get; set; }
|
||||
public virtual ICollection<Policy> Policies { get; set; }
|
||||
public virtual ICollection<SsoConfig> SsoConfigs { get; set; }
|
||||
public virtual ICollection<SsoUser> SsoUsers { get; set; }
|
||||
public virtual ICollection<Transaction> Transactions { get; set; }
|
||||
}
|
||||
|
||||
public class OrganizationMapperProfile : Profile
|
||||
|
21
src/Core/Models/EntityFramework/OrganizationUser.cs
Normal file
21
src/Core/Models/EntityFramework/OrganizationUser.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
19
src/Core/Models/EntityFramework/Policy.cs
Normal file
19
src/Core/Models/EntityFramework/Policy.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
16
src/Core/Models/EntityFramework/Provider/Provider.cs
Normal file
16
src/Core/Models/EntityFramework/Provider/Provider.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
18
src/Core/Models/EntityFramework/Provider/ProviderUser.cs
Normal file
18
src/Core/Models/EntityFramework/Provider/ProviderUser.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
18
src/Core/Models/EntityFramework/Role.cs
Normal file
18
src/Core/Models/EntityFramework/Role.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
20
src/Core/Models/EntityFramework/Send.cs
Normal file
20
src/Core/Models/EntityFramework/Send.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
19
src/Core/Models/EntityFramework/SsoConfig.cs
Normal file
19
src/Core/Models/EntityFramework/SsoConfig.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
20
src/Core/Models/EntityFramework/SsoUser.cs
Normal file
20
src/Core/Models/EntityFramework/SsoUser.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
18
src/Core/Models/EntityFramework/TaxRate.cs
Normal file
18
src/Core/Models/EntityFramework/TaxRate.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
20
src/Core/Models/EntityFramework/Transaction.cs
Normal file
20
src/Core/Models/EntityFramework/Transaction.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
19
src/Core/Models/EntityFramework/U2f.cs
Normal file
19
src/Core/Models/EntityFramework/U2f.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -6,20 +6,14 @@ namespace Bit.Core.Models.EntityFramework
|
||||
{
|
||||
public class User : Table.User
|
||||
{
|
||||
private JsonDocument _twoFactorProvidersJson;
|
||||
|
||||
public ICollection<Cipher> Ciphers { get; set; }
|
||||
|
||||
[IgnoreMap]
|
||||
public JsonDocument TwoFactorProvidersJson
|
||||
{
|
||||
get => _twoFactorProvidersJson;
|
||||
set
|
||||
{
|
||||
TwoFactorProviders = value?.ToString();
|
||||
_twoFactorProvidersJson = value;
|
||||
}
|
||||
}
|
||||
public virtual ICollection<Cipher> Ciphers { get; set; }
|
||||
public virtual ICollection<Folder> Folders { get; set; }
|
||||
public virtual ICollection<CollectionUser> CollectionUsers { get; set; }
|
||||
public virtual ICollection<GroupUser> GroupUsers { get; set; }
|
||||
public virtual ICollection<OrganizationUser> OrganizationUsers { get; set; }
|
||||
public virtual ICollection<SsoUser> SsoUsers { get; set; }
|
||||
public virtual ICollection<Transaction> Transactions { get; set; }
|
||||
public virtual ICollection<U2f> U2fs { get; set; }
|
||||
}
|
||||
|
||||
public class UserMapperProfile : Profile
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Models.Table
|
||||
@ -8,6 +9,7 @@ namespace Bit.Core.Models.Table
|
||||
public Guid Id { get; set; }
|
||||
public Guid OrganizationId { get; set; }
|
||||
public string Name { get; set; }
|
||||
[MaxLength(300)]
|
||||
public string ExternalId { get; set; }
|
||||
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
||||
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;
|
||||
|
13
src/Core/Models/Table/CollectionGroup.cs
Normal file
13
src/Core/Models/Table/CollectionGroup.cs
Normal 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; }
|
||||
}
|
||||
}
|
13
src/Core/Models/Table/CollectionUser.cs
Normal file
13
src/Core/Models/Table/CollectionUser.cs
Normal 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; }
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Models.Table
|
||||
@ -7,9 +8,12 @@ namespace Bit.Core.Models.Table
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string Name { get; set; }
|
||||
public Enums.DeviceType Type { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string Identifier { get; set; }
|
||||
[MaxLength(255)]
|
||||
public string PushToken { get; set; }
|
||||
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
||||
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
@ -9,6 +10,7 @@ namespace Bit.Core.Models.Table
|
||||
public Guid Id { get; set; }
|
||||
public Guid GrantorId { get; set; }
|
||||
public Guid? GranteeId { get; set; }
|
||||
[MaxLength(256)]
|
||||
public string Email { get; set; }
|
||||
public string KeyEncrypted { get; set; }
|
||||
public EmergencyAccessType Type { get; set; }
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
@ -36,6 +37,7 @@ namespace Bit.Core.Models.Table
|
||||
public Guid? GroupId { get; set; }
|
||||
public Guid? OrganizationUserId { get; set; }
|
||||
public DeviceType? DeviceType { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string IpAddress { get; set; }
|
||||
public Guid? ActingUserId { get; set; }
|
||||
|
||||
|
@ -1,14 +1,21 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Core.Models.Table
|
||||
{
|
||||
public class Grant
|
||||
{
|
||||
[MaxLength(200)]
|
||||
public string Key { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string Type { get; set; }
|
||||
[MaxLength(200)]
|
||||
public string SubjectId { get; set; }
|
||||
[MaxLength(100)]
|
||||
public string SessionId { get; set; }
|
||||
[MaxLength(200)]
|
||||
public string ClientId { get; set; }
|
||||
[MaxLength(200)]
|
||||
public string Description { get; set; }
|
||||
public DateTime CreationDate { get; set; }
|
||||
public DateTime? ExpirationDate { get; set; }
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Models.Table
|
||||
@ -7,8 +8,10 @@ namespace Bit.Core.Models.Table
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid OrganizationId { get; set; }
|
||||
[MaxLength(100)]
|
||||
public string Name { get; set; }
|
||||
public bool AccessAll { get; set; }
|
||||
[MaxLength(300)]
|
||||
public string ExternalId { get; set; }
|
||||
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
||||
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;
|
||||
|
@ -1,12 +1,15 @@
|
||||
using Bit.Core.Utilities;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Core.Models.Table
|
||||
{
|
||||
public class Installation : ITableObject<Guid>
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
[MaxLength(256)]
|
||||
public string Email { get; set; }
|
||||
[MaxLength(150)]
|
||||
public string Key { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
||||
|
@ -4,6 +4,7 @@ using Bit.Core.Enums;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using System.Linq;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Core.Models.Table
|
||||
{
|
||||
@ -12,15 +13,25 @@ namespace Bit.Core.Models.Table
|
||||
private Dictionary<TwoFactorProviderType, TwoFactorProvider> _twoFactorProviders;
|
||||
|
||||
public Guid Id { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string Identifier { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string Name { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string BusinessName { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string BusinessAddress1 { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string BusinessAddress2 { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string BusinessAddress3 { get; set; }
|
||||
[MaxLength(2)]
|
||||
public string BusinessCountry { get; set; }
|
||||
[MaxLength(30)]
|
||||
public string BusinessTaxNumber { get; set; }
|
||||
[MaxLength(256)]
|
||||
public string BillingEmail { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string Plan { get; set; }
|
||||
public PlanType PlanType { get; set; }
|
||||
public int? Seats { get; set; }
|
||||
@ -39,11 +50,15 @@ namespace Bit.Core.Models.Table
|
||||
public long? Storage { get; set; }
|
||||
public short? MaxStorageGb { get; set; }
|
||||
public GatewayType? Gateway { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string GatewayCustomerId { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string GatewaySubscriptionId { get; set; }
|
||||
public string ReferenceData { get; set; }
|
||||
public bool Enabled { get; set; } = true;
|
||||
[MaxLength(100)]
|
||||
public string LicenseKey { get; set; }
|
||||
[MaxLength(30)]
|
||||
public string ApiKey { get; set; }
|
||||
public string PublicKey { get; set; }
|
||||
public string PrivateKey { get; set; }
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core.Enums;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Core.Models.Table
|
||||
{
|
||||
@ -9,12 +10,14 @@ namespace Bit.Core.Models.Table
|
||||
public Guid Id { get; set; }
|
||||
public Guid OrganizationId { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
[MaxLength(256)]
|
||||
public string Email { get; set; }
|
||||
public string Key { get; set; }
|
||||
public string ResetPasswordKey { get; set; }
|
||||
public OrganizationUserStatusType Status { get; set; }
|
||||
public OrganizationUserType Type { get; set; }
|
||||
public bool AccessAll { get; set; }
|
||||
[MaxLength(300)]
|
||||
public string ExternalId { get; set; }
|
||||
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
||||
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
@ -12,6 +13,7 @@ namespace Bit.Core.Models.Table
|
||||
public SendType Type { get; set; }
|
||||
public string Data { get; set; }
|
||||
public string Key { get; set; }
|
||||
[MaxLength(300)]
|
||||
public string Password { get; set; }
|
||||
public int? MaxAccessCount { get; set; }
|
||||
public int AccessCount { get; set; }
|
||||
|
@ -13,7 +13,8 @@ namespace Bit.Core.Models.Table
|
||||
|
||||
public void SetNewId()
|
||||
{
|
||||
// nothing - int will be auto-populated
|
||||
// int will be auto-populated
|
||||
Id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Core.Models.Table
|
||||
{
|
||||
@ -7,12 +8,14 @@ namespace Bit.Core.Models.Table
|
||||
public long Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
public Guid? OrganizationId { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string ExternalId { get; set; }
|
||||
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
||||
|
||||
public void SetNewId()
|
||||
{
|
||||
// nothing - int will be auto-populated
|
||||
// int will be auto-populated
|
||||
Id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,16 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Core.Models.Table
|
||||
{
|
||||
public class TaxRate: ITableObject<string>
|
||||
{
|
||||
[MaxLength(40)]
|
||||
public string Id { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string Country { get; set; }
|
||||
[MaxLength(2)]
|
||||
public string State { get; set; }
|
||||
[MaxLength(10)]
|
||||
public string PostalCode { get; set; }
|
||||
public decimal Rate { get; set; }
|
||||
public bool Active { get; set; }
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
@ -13,9 +14,11 @@ namespace Bit.Core.Models.Table
|
||||
public decimal Amount { get; set; }
|
||||
public bool? Refunded { get; set; }
|
||||
public decimal? RefundedAmount { get; set; }
|
||||
[MaxLength(100)]
|
||||
public string Details { get; set; }
|
||||
public PaymentMethodType? PaymentMethodType { get; set; }
|
||||
public GatewayType? Gateway { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string GatewayId { get; set; }
|
||||
public DateTime CreationDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Core.Models.Table
|
||||
{
|
||||
@ -6,15 +7,20 @@ namespace Bit.Core.Models.Table
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
[MaxLength(200)]
|
||||
public string KeyHandle { get; set; }
|
||||
[MaxLength(200)]
|
||||
public string Challenge { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string AppId { get; set; }
|
||||
[MaxLength(20)]
|
||||
public string Version { get; set; }
|
||||
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
||||
|
||||
public void SetNewId()
|
||||
{
|
||||
// do nothing since it is an identity
|
||||
// int will be auto-populated
|
||||
Id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ using Bit.Core.Utilities;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Core.Models.Table
|
||||
{
|
||||
@ -12,14 +13,23 @@ namespace Bit.Core.Models.Table
|
||||
private Dictionary<TwoFactorProviderType, TwoFactorProvider> _twoFactorProviders;
|
||||
|
||||
public Guid Id { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string Name { get; set; }
|
||||
[Required]
|
||||
[MaxLength(256)]
|
||||
public string Email { get; set; }
|
||||
public bool EmailVerified { get; set; }
|
||||
[MaxLength(300)]
|
||||
public string MasterPassword { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string MasterPasswordHint { get; set; }
|
||||
[MaxLength(10)]
|
||||
public string Culture { get; set; } = "en-US";
|
||||
[Required]
|
||||
[MaxLength(50)]
|
||||
public string SecurityStamp { get; set; }
|
||||
public string TwoFactorProviders { get; set; }
|
||||
[MaxLength(32)]
|
||||
public string TwoFactorRecoveryCode { get; set; }
|
||||
public string EquivalentDomains { get; set; }
|
||||
public string ExcludedGlobalEquivalentDomains { get; set; }
|
||||
@ -33,10 +43,15 @@ namespace Bit.Core.Models.Table
|
||||
public long? Storage { get; set; }
|
||||
public short? MaxStorageGb { get; set; }
|
||||
public GatewayType? Gateway { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string GatewayCustomerId { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string GatewaySubscriptionId { get; set; }
|
||||
public string ReferenceData { get; set; }
|
||||
[MaxLength(100)]
|
||||
public string LicenseKey { get; set; }
|
||||
[Required]
|
||||
[MaxLength(30)]
|
||||
public string ApiKey { get; set; }
|
||||
public KdfType Kdf { get; set; } = KdfType.PBKDF2_SHA256;
|
||||
public int KdfIterations { get; set; } = 5000;
|
||||
|
@ -1,10 +1,28 @@
|
||||
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 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
|
||||
{
|
||||
public abstract class BaseEntityFrameworkRepository
|
||||
{
|
||||
protected BulkCopyOptions DefaultBulkCopyOptions { get; set; } = new BulkCopyOptions
|
||||
{
|
||||
KeepIdentity = true,
|
||||
BulkCopyType = BulkCopyType.MultipleRows,
|
||||
};
|
||||
|
||||
public BaseEntityFrameworkRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
|
||||
{
|
||||
ServiceScopeFactory = serviceScopeFactory;
|
||||
@ -18,5 +36,233 @@ namespace Bit.Core.Repositories.EntityFramework
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
642
src/Core/Repositories/EntityFramework/CipherRepository.cs
Normal file
642
src/Core/Repositories/EntityFramework/CipherRepository.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
247
src/Core/Repositories/EntityFramework/CollectionRepository.cs
Normal file
247
src/Core/Repositories/EntityFramework/CollectionRepository.cs
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
namespace Bit.Core.Repositories.EntityFramework
|
||||
{
|
||||
public class DatabaseContext : DbContext
|
||||
{
|
||||
public const string postgresIndetermanisticCollation = "postgresIndetermanisticCollation";
|
||||
|
||||
public DatabaseContext(DbContextOptions<DatabaseContext> options)
|
||||
: base(options)
|
||||
{ }
|
||||
|
||||
public DbSet<User> Users { 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<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)
|
||||
{
|
||||
builder.Entity<Cipher>().Ignore(e => e.Data);
|
||||
builder.Entity<Cipher>().Property(e => e.DataJson).HasColumnName("Data");
|
||||
builder.Entity<Cipher>().Ignore(e => e.Attachments);
|
||||
builder.Entity<Cipher>().Property(e => e.AttachmentsJson).HasColumnName("Attachments");
|
||||
builder.Entity<Cipher>().Ignore(e => e.Favorites);
|
||||
builder.Entity<Cipher>().Property(e => e.FavoritesJson).HasColumnName("Favorites");
|
||||
builder.Entity<Cipher>().Ignore(e => e.Folders);
|
||||
builder.Entity<Cipher>().Property(e => e.FoldersJson).HasColumnName("Folders");
|
||||
var eCipher = builder.Entity<Cipher>();
|
||||
var eCollection = builder.Entity<Collection>();
|
||||
var eCollectionCipher = builder.Entity<CollectionCipher>();
|
||||
var eCollectionUser = builder.Entity<CollectionUser>();
|
||||
var eCollectionGroup = builder.Entity<CollectionGroup>();
|
||||
var eDevice = builder.Entity<Device>();
|
||||
var eEmergencyAccess = builder.Entity<EmergencyAccess>();
|
||||
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);
|
||||
builder.Entity<User>().Property(e => e.TwoFactorProvidersJson).HasColumnName("TwoFactorProviders");
|
||||
eCipher.Property(c => c.Id).ValueGeneratedNever();
|
||||
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);
|
||||
builder.Entity<Organization>().Property(e => e.TwoFactorProvidersJson).HasColumnName("TwoFactorProviders");
|
||||
eCollectionCipher.HasKey(cc => new { cc.CollectionId, cc.CipherId });
|
||||
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));
|
||||
builder.Entity<Organization>().ToTable(nameof(Organization));
|
||||
|
||||
if (Database.IsNpgsql())
|
||||
{
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
76
src/Core/Repositories/EntityFramework/DeviceRepository.cs
Normal file
76
src/Core/Repositories/EntityFramework/DeviceRepository.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
156
src/Core/Repositories/EntityFramework/EventRepository.cs
Normal file
156
src/Core/Repositories/EntityFramework/EventRepository.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
src/Core/Repositories/EntityFramework/FolderRepository.cs
Normal file
45
src/Core/Repositories/EntityFramework/FolderRepository.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
101
src/Core/Repositories/EntityFramework/GrantRepository.cs
Normal file
101
src/Core/Repositories/EntityFramework/GrantRepository.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
174
src/Core/Repositories/EntityFramework/GroupRepository.cs
Normal file
174
src/Core/Repositories/EntityFramework/GroupRepository.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
{ }
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using TableModel = Bit.Core.Models.Table;
|
||||
using AutoMapper;
|
||||
using Bit.Core.Models.Table;
|
||||
using DataModel = Bit.Core.Models.Data;
|
||||
using EFModel = Bit.Core.Models.EntityFramework;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using AutoMapper;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
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
|
||||
{
|
||||
@ -40,48 +40,35 @@ namespace Bit.Core.Repositories.EntityFramework
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
// TODO: more filters
|
||||
var organizations = await GetDbSet(dbContext)
|
||||
.Where(e => name == null || e.Name.StartsWith(name))
|
||||
.OrderBy(e => e.Name)
|
||||
.Skip(skip).Take(take)
|
||||
.ToListAsync();
|
||||
.Select(e => e.OrganizationUsers
|
||||
.Where(ou => ou.UserId == userId)
|
||||
.Select(ou => ou.Organization))
|
||||
.ToListAsync();
|
||||
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())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
var ciphers = await dbContext.Ciphers
|
||||
.Where(e => e.UserId == null && e.OrganizationId == id).ToListAsync();
|
||||
var storage = ciphers.Sum(e => e.AttachmentsJson?.RootElement.EnumerateArray()
|
||||
.Sum(p => p.GetProperty("Size").GetInt64()) ?? 0);
|
||||
var organization = new EFModel.Organization
|
||||
{
|
||||
Id = id,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
Storage = storage,
|
||||
};
|
||||
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();
|
||||
var organizations = await GetDbSet(dbContext)
|
||||
.Where(e => name == null || e.Name.Contains(name))
|
||||
.Where(e => userEmail == null || e.OrganizationUsers.Any(u => u.Email == userEmail))
|
||||
.Where(e => paid == null ||
|
||||
(paid == true && !string.IsNullOrWhiteSpace(e.GatewaySubscriptionId)) ||
|
||||
(paid == false && e.GatewaySubscriptionId == null))
|
||||
.OrderBy(e => e.CreationDate)
|
||||
.Skip(skip).Take(take)
|
||||
.ToListAsync();
|
||||
return Mapper.Map<List<TableModel.Organization>>(organizations);
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,5 +90,10 @@ namespace Bit.Core.Repositories.EntityFramework
|
||||
}).ToListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateStorageAsync(Guid id)
|
||||
{
|
||||
await OrganizationUpdateStorage(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
58
src/Core/Repositories/EntityFramework/PolicyRepository.cs
Normal file
58
src/Core/Repositories/EntityFramework/PolicyRepository.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
{ }
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
48
src/Core/Repositories/EntityFramework/ProviderRepository.cs
Normal file
48
src/Core/Repositories/EntityFramework/ProviderRepository.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
144
src/Core/Repositories/EntityFramework/ProviderUserRepository.cs
Normal file
144
src/Core/Repositories/EntityFramework/ProviderUserRepository.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
9
src/Core/Repositories/EntityFramework/Queries/IQuery.cs
Normal file
9
src/Core/Repositories/EntityFramework/Queries/IQuery.cs
Normal 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
Loading…
Reference in New Issue
Block a user