diff --git a/src/Api/Controllers/OrganizationUsersController.cs b/src/Api/Controllers/OrganizationUsersController.cs index 903a6c6f6..17d73ff67 100644 --- a/src/Api/Controllers/OrganizationUsersController.cs +++ b/src/Api/Controllers/OrganizationUsersController.cs @@ -6,6 +6,7 @@ using Bit.Api.Models.Request.Organizations; using Bit.Api.Models.Response; using Bit.Api.Models.Response.Organizations; using Bit.Core.Context; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Business; @@ -375,5 +376,69 @@ namespace Bit.Api.Controllers return new ListResponseModel(result.Select(r => new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2))); } + + [HttpPatch("{id}/deactivate")] + [HttpPut("{id}/deactivate")] + public async Task Deactivate(Guid orgId, Guid id) + { + await ActivateOrDeactivateUserAsync(orgId, id, _organizationService.DeactivateUserAsync); + } + + [HttpPatch("deactivate")] + [HttpPut("deactivate")] + public async Task> BulkDeactivate(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model) + { + return await ActivateOrDeactivateUsersAsync(orgId, model, _organizationService.DeactivateUsersAsync); + } + + [HttpPatch("{id}/activate")] + [HttpPut("{id}/activate")] + public async Task Activate(Guid orgId, Guid id) + { + await ActivateOrDeactivateUserAsync(orgId, id, _organizationService.ActivateUserAsync); + } + + [HttpPatch("activate")] + [HttpPut("activate")] + public async Task> BulkActivate(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model) + { + return await ActivateOrDeactivateUsersAsync(orgId, model, _organizationService.ActivateUsersAsync); + } + + private async Task ActivateOrDeactivateUserAsync( + Guid orgId, + Guid id, + Func statusAction) + { + if (!await _currentContext.ManageUsers(orgId)) + { + throw new NotFoundException(); + } + + var userId = _userService.GetProperUserId(User); + var orgUser = await _organizationUserRepository.GetByIdAsync(id); + if (orgUser == null || orgUser.OrganizationId != orgId) + { + throw new NotFoundException(); + } + + await statusAction(orgUser, userId); + } + + private async Task> ActivateOrDeactivateUsersAsync( + Guid orgId, + OrganizationUserBulkRequestModel model, + Func, Guid?, Task>>> statusAction) + { + if (!await _currentContext.ManageUsers(orgId)) + { + throw new NotFoundException(); + } + + var userId = _userService.GetProperUserId(User); + var result = await statusAction(orgId, model.Ids, userId.Value); + return new ListResponseModel(result.Select(r => + new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2))); + } } } diff --git a/src/Core/Enums/EventType.cs b/src/Core/Enums/EventType.cs index a873772fe..56a627a04 100644 --- a/src/Core/Enums/EventType.cs +++ b/src/Core/Enums/EventType.cs @@ -51,6 +51,8 @@ OrganizationUser_AdminResetPassword = 1508, OrganizationUser_ResetSsoLink = 1509, OrganizationUser_FirstSsoLogin = 1510, + OrganizationUser_Deactivated = 1511, + OrganizationUser_Activated = 1512, Organization_Updated = 1600, Organization_PurgedVault = 1601, diff --git a/src/Core/Enums/OrganizationUserStatusType.cs b/src/Core/Enums/OrganizationUserStatusType.cs index 7d2246dc8..592d5bcd7 100644 --- a/src/Core/Enums/OrganizationUserStatusType.cs +++ b/src/Core/Enums/OrganizationUserStatusType.cs @@ -1,9 +1,10 @@ namespace Bit.Core.Enums { - public enum OrganizationUserStatusType : byte + public enum OrganizationUserStatusType : short { Invited = 0, Accepted = 1, - Confirmed = 2 + Confirmed = 2, + Deactivated = -1, } } diff --git a/src/Core/Repositories/IOrganizationUserRepository.cs b/src/Core/Repositories/IOrganizationUserRepository.cs index 053440282..3412c331d 100644 --- a/src/Core/Repositories/IOrganizationUserRepository.cs +++ b/src/Core/Repositories/IOrganizationUserRepository.cs @@ -39,5 +39,7 @@ namespace Bit.Core.Repositories Task GetByOrganizationEmailAsync(Guid organizationId, string email); Task> GetManyPublicKeysByOrganizationUserAsync(Guid organizationId, IEnumerable Ids); Task> GetManyByMinimumRoleAsync(Guid organizationId, OrganizationUserType minRole); + Task DeactivateAsync(Guid id); + Task ActivateAsync(Guid id, OrganizationUserStatusType status); } } diff --git a/src/Core/Services/IOrganizationService.cs b/src/Core/Services/IOrganizationService.cs index d27391a71..6624c8830 100644 --- a/src/Core/Services/IOrganizationService.cs +++ b/src/Core/Services/IOrganizationService.cs @@ -61,5 +61,11 @@ namespace Bit.Core.Services Task DeleteSsoUserAsync(Guid userId, Guid? organizationId); Task UpdateOrganizationKeysAsync(Guid orgId, string publicKey, string privateKey); Task HasConfirmedOwnersExceptAsync(Guid organizationId, IEnumerable organizationUsersId, bool includeProvider = true); + Task DeactivateUserAsync(OrganizationUser organizationUser, Guid? disablingUserId); + Task>> DeactivateUsersAsync(Guid organizationId, + IEnumerable organizationUserIds, Guid? disablingUserId); + Task ActivateUserAsync(OrganizationUser organizationUser, Guid? enablingUserId); + Task>> ActivateUsersAsync(Guid organizationId, + IEnumerable organizationUserIds, Guid? enablingUserId); } } diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index 0ce9ca066..ae4ab7b32 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -2186,5 +2186,186 @@ namespace Bit.Core.Services throw new BadRequestException("You cannot delete an Organization that is using Key Connector."); } } + + public async Task DeactivateUserAsync(OrganizationUser organizationUser, Guid? disablingUserId) + { + if (organizationUser.Status == OrganizationUserStatusType.Deactivated) + { + throw new BadRequestException("Already deactivated."); + } + + if (disablingUserId.HasValue && organizationUser.UserId == disablingUserId.Value) + { + throw new BadRequestException("You cannot deactivate yourself."); + } + + if (organizationUser.Type == OrganizationUserType.Owner && disablingUserId.HasValue && + !await _currentContext.OrganizationOwner(organizationUser.OrganizationId)) + { + throw new BadRequestException("Only owners can deactivate other owners."); + } + + if (!await HasConfirmedOwnersExceptAsync(organizationUser.OrganizationId, new[] { organizationUser.Id })) + { + throw new BadRequestException("Organization must have at least one confirmed owner."); + } + + await _organizationUserRepository.DeactivateAsync(organizationUser.Id); + organizationUser.Status = OrganizationUserStatusType.Deactivated; + await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Deactivated); + } + + public async Task>> DeactivateUsersAsync(Guid organizationId, + IEnumerable organizationUserIds, Guid? disablingUserId) + { + var orgUsers = await _organizationUserRepository.GetManyAsync(organizationUserIds); + var filteredUsers = orgUsers.Where(u => u.OrganizationId == organizationId) + .ToList(); + + if (!filteredUsers.Any()) + { + throw new BadRequestException("Users invalid."); + } + + if (!await HasConfirmedOwnersExceptAsync(organizationId, organizationUserIds)) + { + throw new BadRequestException("Organization must have at least one confirmed owner."); + } + + var deletingUserIsOwner = false; + if (disablingUserId.HasValue) + { + deletingUserIsOwner = await _currentContext.OrganizationOwner(organizationId); + } + + var result = new List>(); + + foreach (var organizationUser in filteredUsers) + { + try + { + if (disablingUserId.HasValue && organizationUser.UserId == disablingUserId) + { + throw new BadRequestException("You cannot deactivate yourself."); + } + + if (organizationUser.Type == OrganizationUserType.Owner && disablingUserId.HasValue && !deletingUserIsOwner) + { + throw new BadRequestException("Only owners can deactivate other owners."); + } + + await _organizationUserRepository.DeactivateAsync(organizationUser.Id); + organizationUser.Status = OrganizationUserStatusType.Deactivated; + await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Deactivated); + + result.Add(Tuple.Create(organizationUser, "")); + } + catch (BadRequestException e) + { + result.Add(Tuple.Create(organizationUser, e.Message)); + } + } + + return result; + } + + public async Task ActivateUserAsync(OrganizationUser organizationUser, Guid? enablingUserId) + { + if (organizationUser.Status != OrganizationUserStatusType.Deactivated) + { + throw new BadRequestException("Already active."); + } + + if (enablingUserId.HasValue && organizationUser.UserId == enablingUserId.Value) + { + throw new BadRequestException("You cannot activate yourself."); + } + + if (organizationUser.Type == OrganizationUserType.Owner && enablingUserId.HasValue && + !await _currentContext.OrganizationOwner(organizationUser.OrganizationId)) + { + throw new BadRequestException("Only owners can activate other owners."); + } + + var status = GetPriorActiveOrganizationUserStatusType(organizationUser); + + await _organizationUserRepository.ActivateAsync(organizationUser.Id, status); + organizationUser.Status = status; + await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Activated); + } + + public async Task>> ActivateUsersAsync(Guid organizationId, + IEnumerable organizationUserIds, Guid? enablingUserId) + { + var orgUsers = await _organizationUserRepository.GetManyAsync(organizationUserIds); + var filteredUsers = orgUsers.Where(u => u.OrganizationId == organizationId) + .ToList(); + + if (!filteredUsers.Any()) + { + throw new BadRequestException("Users invalid."); + } + + var deletingUserIsOwner = false; + if (enablingUserId.HasValue) + { + deletingUserIsOwner = await _currentContext.OrganizationOwner(organizationId); + } + + var result = new List>(); + + foreach (var organizationUser in filteredUsers) + { + try + { + if (organizationUser.Status != OrganizationUserStatusType.Deactivated) + { + throw new BadRequestException("Already active."); + } + + if (enablingUserId.HasValue && organizationUser.UserId == enablingUserId) + { + throw new BadRequestException("You cannot activate yourself."); + } + + if (organizationUser.Type == OrganizationUserType.Owner && enablingUserId.HasValue && !deletingUserIsOwner) + { + throw new BadRequestException("Only owners can activate other owners."); + } + + var status = GetPriorActiveOrganizationUserStatusType(organizationUser); + + await _organizationUserRepository.ActivateAsync(organizationUser.Id, status); + organizationUser.Status = status; + await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Activated); + + result.Add(Tuple.Create(organizationUser, "")); + } + catch (BadRequestException e) + { + result.Add(Tuple.Create(organizationUser, e.Message)); + } + } + + return result; + } + + static OrganizationUserStatusType GetPriorActiveOrganizationUserStatusType(OrganizationUser organizationUser) + { + // Determine status to revert back to + var status = OrganizationUserStatusType.Invited; + if (organizationUser.UserId.HasValue && string.IsNullOrWhiteSpace(organizationUser.Email)) + { + // Has UserId & Email is null, then Accepted + status = OrganizationUserStatusType.Accepted; + if (!string.IsNullOrWhiteSpace(organizationUser.Key)) + { + // We have an org key for this user, user was confirmed + status = OrganizationUserStatusType.Confirmed; + } + } + + return status; + } } } diff --git a/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs b/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs index 361f71b6c..bc25076a1 100644 --- a/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs @@ -409,5 +409,27 @@ namespace Bit.Infrastructure.Dapper.Repositories return results.ToList(); } } + + public async Task DeactivateAsync(Guid id) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.ExecuteAsync( + $"[{Schema}].[{Table}_Deactivate]", + new { Id = id }, + commandType: CommandType.StoredProcedure); + } + } + + public async Task ActivateAsync(Guid id, OrganizationUserStatusType status) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.ExecuteAsync( + $"[{Schema}].[{Table}_Activate]", + new { Id = id, Status = status }, + commandType: CommandType.StoredProcedure); + } + } } } diff --git a/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs b/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs index fc68375be..96f43ca97 100644 --- a/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs @@ -427,5 +427,43 @@ namespace Bit.Infrastructure.EntityFramework.Repositories return await query.ToListAsync(); } } + + public async Task DeactivateAsync(Guid id) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var orgUser = await GetDbSet(dbContext).FindAsync(id); + if (orgUser != null) + { + dbContext.Update(orgUser); + orgUser.Status = OrganizationUserStatusType.Deactivated; + await dbContext.SaveChangesAsync(); + if (orgUser.UserId.HasValue) + { + await UserBumpAccountRevisionDate(orgUser.UserId.Value); + } + } + } + } + + public async Task ActivateAsync(Guid id, OrganizationUserStatusType status) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var orgUser = await GetDbSet(dbContext).FindAsync(id); + if (orgUser != null) + { + dbContext.Update(orgUser); + orgUser.Status = status; + await dbContext.SaveChangesAsync(); + if (orgUser.UserId.HasValue) + { + await UserBumpAccountRevisionDate(orgUser.UserId.Value); + } + } + } + } } } diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj index 758701a30..e5b29b008 100644 --- a/src/Sql/Sql.sqlproj +++ b/src/Sql/Sql.sqlproj @@ -122,6 +122,8 @@ + + diff --git a/src/Sql/dbo/Functions/PolicyApplicableToUser.sql b/src/Sql/dbo/Functions/PolicyApplicableToUser.sql index 83a925d9d..aab2b1952 100644 --- a/src/Sql/dbo/Functions/PolicyApplicableToUser.sql +++ b/src/Sql/dbo/Functions/PolicyApplicableToUser.sql @@ -2,7 +2,7 @@ CREATE FUNCTION [dbo].[PolicyApplicableToUser] ( @UserId UNIQUEIDENTIFIER, @PolicyType TINYINT, - @MinimumStatus TINYINT + @MinimumStatus SMALLINT ) RETURNS TABLE AS RETURN diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUserOrganizationDetails_ReadByUserIdStatus.sql b/src/Sql/dbo/Stored Procedures/OrganizationUserOrganizationDetails_ReadByUserIdStatus.sql index c9296d01d..0fed695cc 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationUserOrganizationDetails_ReadByUserIdStatus.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationUserOrganizationDetails_ReadByUserIdStatus.sql @@ -1,6 +1,6 @@ CREATE PROCEDURE [dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatus] @UserId UNIQUEIDENTIFIER, - @Status TINYINT + @Status SMALLINT AS BEGIN SET NOCOUNT ON diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId.sql b/src/Sql/dbo/Stored Procedures/OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId.sql index a3e15d78e..c9948b535 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId.sql @@ -1,6 +1,6 @@ CREATE PROCEDURE [dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId] @UserId UNIQUEIDENTIFIER, - @Status TINYINT, + @Status SMALLINT, @OrganizationId UNIQUEIDENTIFIER AS BEGIN diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_Activate.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_Activate.sql new file mode 100644 index 000000000..e1728dbb3 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_Activate.sql @@ -0,0 +1,17 @@ +CREATE PROCEDURE [dbo].[OrganizationUser_Activate] + @Id UNIQUEIDENTIFIER, + @Status SMALLINT +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[OrganizationUser] + SET + [Status] = @Status + WHERE + [Id] = @Id + AND [Status] = -1 -- Deactivated + + EXEC [dbo].[User_BumpAccountRevisionDateByOrganizationUserId] @Id +END diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_Create.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_Create.sql index 9dacb45f9..95795067d 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationUser_Create.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_Create.sql @@ -4,7 +4,7 @@ @UserId UNIQUEIDENTIFIER, @Email NVARCHAR(256), @Key VARCHAR(MAX), - @Status TINYINT, + @Status SMALLINT, @Type TINYINT, @AccessAll BIT, @ExternalId NVARCHAR(300), diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateWithCollections.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateWithCollections.sql index 984b7644b..55d5f9c08 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateWithCollections.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_CreateWithCollections.sql @@ -4,7 +4,7 @@ @UserId UNIQUEIDENTIFIER, @Email NVARCHAR(256), @Key VARCHAR(MAX), - @Status TINYINT, + @Status SMALLINT, @Type TINYINT, @AccessAll BIT, @ExternalId NVARCHAR(300), diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_Deactivate.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_Deactivate.sql new file mode 100644 index 000000000..f24d6c7aa --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_Deactivate.sql @@ -0,0 +1,15 @@ +CREATE PROCEDURE [dbo].[OrganizationUser_Deactivate] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[OrganizationUser] + SET + [Status] = -1 -- Deactivated + WHERE + [Id] = @Id + + EXEC [dbo].[User_BumpAccountRevisionDateByOrganizationUserId] @Id +END diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_Update.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_Update.sql index 31c409d59..c8cfa08a4 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationUser_Update.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_Update.sql @@ -4,7 +4,7 @@ @UserId UNIQUEIDENTIFIER, @Email NVARCHAR(256), @Key VARCHAR(MAX), - @Status TINYINT, + @Status SMALLINT, @Type TINYINT, @AccessAll BIT, @ExternalId NVARCHAR(300), diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateWithCollections.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateWithCollections.sql index 872bcf364..20390a02c 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateWithCollections.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_UpdateWithCollections.sql @@ -4,7 +4,7 @@ @UserId UNIQUEIDENTIFIER, @Email NVARCHAR(256), @Key VARCHAR(MAX), - @Status TINYINT, + @Status SMALLINT, @Type TINYINT, @AccessAll BIT, @ExternalId NVARCHAR(300), diff --git a/src/Sql/dbo/Tables/OrganizationUser.sql b/src/Sql/dbo/Tables/OrganizationUser.sql index f486ff4bb..fc1fa9950 100644 --- a/src/Sql/dbo/Tables/OrganizationUser.sql +++ b/src/Sql/dbo/Tables/OrganizationUser.sql @@ -5,7 +5,7 @@ [Email] NVARCHAR (256) NULL, [Key] VARCHAR (MAX) NULL, [ResetPasswordKey] VARCHAR (MAX) NULL, - [Status] TINYINT NOT NULL, + [Status] SMALLINT NOT NULL, [Type] TINYINT NOT NULL, [AccessAll] BIT NOT NULL, [ExternalId] NVARCHAR (300) NULL, diff --git a/src/Sql/dbo/User Defined Types/OrganizationUserType.sql b/src/Sql/dbo/User Defined Types/OrganizationUserType.sql index cfb4871c6..6d18cf044 100644 --- a/src/Sql/dbo/User Defined Types/OrganizationUserType.sql +++ b/src/Sql/dbo/User Defined Types/OrganizationUserType.sql @@ -4,7 +4,7 @@ CREATE TYPE [dbo].[OrganizationUserType] AS TABLE( [UserId] UNIQUEIDENTIFIER, [Email] NVARCHAR(256), [Key] VARCHAR(MAX), - [Status] TINYINT, + [Status] SMALLINT, [Type] TINYINT, [AccessAll] BIT, [ExternalId] NVARCHAR(300), diff --git a/util/Migrator/DbScripts/2022-06-08_00_DeactivatedUserStatus.sql b/util/Migrator/DbScripts/2022-06-08_00_DeactivatedUserStatus.sql new file mode 100644 index 000000000..cbc0d0f99 --- /dev/null +++ b/util/Migrator/DbScripts/2022-06-08_00_DeactivatedUserStatus.sql @@ -0,0 +1,515 @@ +/**************************************************************** + * + * WARNING: Index Rebuild on OrganizationUser Table! + * Ensure [IX_OrganizationUser_UserIdOrganizationIdStatus] impact is done after-hours + * or scale DB instance up to handle increased load during update. + * + ***************************************************************/ + +PRINT N'Starting migration for 2022-06-08_00_DeactivatedUserStatus'; +GO + +PRINT N'Checking dbo.OrganizationUser.Status is TINYINT...'; +GO +IF EXISTS ( + SELECT TOP 1 NULL + FROM [information_schema].[columns] + WHERE [table_name] = 'OrganizationUser' + AND [table_schema] = 'dbo' + AND [column_name] = 'Status' + AND [data_type] = 'TINYINT' +) +BEGIN + PRINT N'Dropping index IX_OrganizationUser_UserIdOrganizationIdStatus...'; + DROP INDEX IF EXISTS [IX_OrganizationUser_UserIdOrganizationIdStatus] + ON [dbo].[OrganizationUser]; + + PRINT N'Altering dbo.OrganizationUser.Status to SMALLINT...'; + ALTER TABLE [dbo].[OrganizationUser] + ALTER COLUMN [Status] SMALLINT NOT NULL; + + PRINT N'Recreating index IX_OrganizationUser_UserIdOrganizationIdStatus...'; + CREATE NONCLUSTERED INDEX [IX_OrganizationUser_UserIdOrganizationIdStatus] + ON [dbo].[OrganizationUser]([UserId] ASC, [OrganizationId] ASC, [Status] ASC) + INCLUDE ([AccessAll]); +END +GO + +PRINT N'Dropping stored procedure, dbo.OrganizationUser_CreateMany...'; +GO +IF OBJECT_ID('[dbo].[OrganizationUser_CreateMany]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[OrganizationUser_CreateMany] +END +GO + +PRINT N'Dropping stored procedure, dbo.OrganizationUser_UpdateMany...'; +GO +IF OBJECT_ID('[dbo].[OrganizationUser_UpdateMany]') IS NOT NULL +BEGIN + DROP PROCEDURE [dbo].[OrganizationUser_UpdateMany] +END +GO + +PRINT N'Dropping type dbo.OrganizationUserType...'; +GO +IF TYPE_ID(N'[dbo].[OrganizationUserType]') IS NOT NULL +BEGIN + DROP TYPE [dbo].[OrganizationUserType]; +END +GO +PRINT N'Recreating Type dbo.OrganizationUserType...'; +GO +CREATE TYPE [dbo].[OrganizationUserType] AS TABLE( + [Id] UNIQUEIDENTIFIER, + [OrganizationId] UNIQUEIDENTIFIER, + [UserId] UNIQUEIDENTIFIER, + [Email] NVARCHAR(256), + [Key] VARCHAR(MAX), + [Status] SMALLINT, + [Type] TINYINT, + [AccessAll] BIT, + [ExternalId] NVARCHAR(300), + [CreationDate] DATETIME2(7), + [RevisionDate] DATETIME2(7), + [Permissions] NVARCHAR(MAX), + [ResetPasswordKey] VARCHAR(MAX) +); +GO + +PRINT N'Altering stored procedure, dbo.OrganizationUser_CreateMany...'; +GO +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_CreateMany] + @OrganizationUsersInput [dbo].[OrganizationUserType] READONLY +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[OrganizationUser] + ( + [Id], + [OrganizationId], + [UserId], + [Email], + [Key], + [Status], + [Type], + [AccessAll], + [ExternalId], + [CreationDate], + [RevisionDate], + [Permissions], + [ResetPasswordKey] + ) + SELECT + OU.[Id], + OU.[OrganizationId], + OU.[UserId], + OU.[Email], + OU.[Key], + OU.[Status], + OU.[Type], + OU.[AccessAll], + OU.[ExternalId], + OU.[CreationDate], + OU.[RevisionDate], + OU.[Permissions], + OU.[ResetPasswordKey] + FROM + @OrganizationUsersInput OU +END +GO + +PRINT N'Altering stored procedure, dbo.OrganizationUser_UpdateMany...'; +GO +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_UpdateMany] + @OrganizationUsersInput [dbo].[OrganizationUserType] READONLY +AS +BEGIN + SET NOCOUNT ON + + UPDATE + OU + SET + [OrganizationId] = OUI.[OrganizationId], + [UserId] = OUI.[UserId], + [Email] = OUI.[Email], + [Key] = OUI.[Key], + [Status] = OUI.[Status], + [Type] = OUI.[Type], + [AccessAll] = OUI.[AccessAll], + [ExternalId] = OUI.[ExternalId], + [CreationDate] = OUI.[CreationDate], + [RevisionDate] = OUI.[RevisionDate], + [Permissions] = OUI.[Permissions], + [ResetPasswordKey] = OUI.[ResetPasswordKey] + FROM + [dbo].[OrganizationUser] OU + INNER JOIN + @OrganizationUsersInput OUI ON OU.Id = OUI.Id + + EXEC [dbo].[User_BumpManyAccountRevisionDates] + ( + SELECT UserId + FROM @OrganizationUsersInput + ) +END +GO + +PRINT N'Altering stored procedure, dbo.OrganizationUser_Create...'; +GO +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_Create] + @Id UNIQUEIDENTIFIER OUTPUT, + @OrganizationId UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @Email NVARCHAR(256), + @Key VARCHAR(MAX), + @Status SMALLINT, + @Type TINYINT, + @AccessAll BIT, + @ExternalId NVARCHAR(300), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @Permissions NVARCHAR(MAX), + @ResetPasswordKey VARCHAR(MAX) +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[OrganizationUser] + ( + [Id], + [OrganizationId], + [UserId], + [Email], + [Key], + [Status], + [Type], + [AccessAll], + [ExternalId], + [CreationDate], + [RevisionDate], + [Permissions], + [ResetPasswordKey] + ) + VALUES + ( + @Id, + @OrganizationId, + @UserId, + @Email, + @Key, + @Status, + @Type, + @AccessAll, + @ExternalId, + @CreationDate, + @RevisionDate, + @Permissions, + @ResetPasswordKey + ) +END +GO + +PRINT N'Altering stored procedure, dbo.OrganizationUser_CreateWithCollections...'; +GO +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_CreateWithCollections] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @Email NVARCHAR(256), + @Key VARCHAR(MAX), + @Status SMALLINT, + @Type TINYINT, + @AccessAll BIT, + @ExternalId NVARCHAR(300), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @Permissions NVARCHAR(MAX), + @ResetPasswordKey VARCHAR(MAX), + @Collections AS [dbo].[SelectionReadOnlyArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[OrganizationUser_Create] @Id, @OrganizationId, @UserId, @Email, @Key, @Status, @Type, @AccessAll, @ExternalId, @CreationDate, @RevisionDate, @Permissions, @ResetPasswordKey + + ;WITH [AvailableCollectionsCTE] AS( + SELECT + [Id] + FROM + [dbo].[Collection] + WHERE + [OrganizationId] = @OrganizationId + ) + INSERT INTO [dbo].[CollectionUser] + ( + [CollectionId], + [OrganizationUserId], + [ReadOnly], + [HidePasswords] + ) + SELECT + [Id], + @Id, + [ReadOnly], + [HidePasswords] + FROM + @Collections + WHERE + [Id] IN (SELECT [Id] FROM [AvailableCollectionsCTE]) +END +GO + +PRINT N'Altering stored procedure, dbo.OrganizationUser_Update...'; +GO +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_Update] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @Email NVARCHAR(256), + @Key VARCHAR(MAX), + @Status SMALLINT, + @Type TINYINT, + @AccessAll BIT, + @ExternalId NVARCHAR(300), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @Permissions NVARCHAR(MAX), + @ResetPasswordKey VARCHAR(MAX) +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[OrganizationUser] + SET + [OrganizationId] = @OrganizationId, + [UserId] = @UserId, + [Email] = @Email, + [Key] = @Key, + [Status] = @Status, + [Type] = @Type, + [AccessAll] = @AccessAll, + [ExternalId] = @ExternalId, + [CreationDate] = @CreationDate, + [RevisionDate] = @RevisionDate, + [Permissions] = @Permissions, + [ResetPasswordKey] = @ResetPasswordKey + WHERE + [Id] = @Id + + EXEC [dbo].[User_BumpAccountRevisionDate] @UserId +END +GO + +PRINT N'Altering stored procedure, dbo.OrganizationUser_UpdateWithCollections...'; +GO +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_UpdateWithCollections] + @Id UNIQUEIDENTIFIER, + @OrganizationId UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER, + @Email NVARCHAR(256), + @Key VARCHAR(MAX), + @Status SMALLINT, + @Type TINYINT, + @AccessAll BIT, + @ExternalId NVARCHAR(300), + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7), + @Permissions NVARCHAR(MAX), + @ResetPasswordKey VARCHAR(MAX), + @Collections AS [dbo].[SelectionReadOnlyArray] READONLY +AS +BEGIN + SET NOCOUNT ON + + EXEC [dbo].[OrganizationUser_Update] @Id, @OrganizationId, @UserId, @Email, @Key, @Status, @Type, @AccessAll, @ExternalId, @CreationDate, @RevisionDate, @Permissions, @ResetPasswordKey + -- Update + UPDATE + [Target] + SET + [Target].[ReadOnly] = [Source].[ReadOnly], + [Target].[HidePasswords] = [Source].[HidePasswords] + FROM + [dbo].[CollectionUser] AS [Target] + INNER JOIN + @Collections AS [Source] ON [Source].[Id] = [Target].[CollectionId] + WHERE + [Target].[OrganizationUserId] = @Id + AND ( + [Target].[ReadOnly] != [Source].[ReadOnly] + OR [Target].[HidePasswords] != [Source].[HidePasswords] + ) + + -- Insert + INSERT INTO + [dbo].[CollectionUser] + SELECT + [Source].[Id], + @Id, + [Source].[ReadOnly], + [Source].[HidePasswords] + FROM + @Collections AS [Source] + INNER JOIN + [dbo].[Collection] C ON C.[Id] = [Source].[Id] AND C.[OrganizationId] = @OrganizationId + WHERE + NOT EXISTS ( + SELECT + 1 + FROM + [dbo].[CollectionUser] + WHERE + [CollectionId] = [Source].[Id] + AND [OrganizationUserId] = @Id + ) + + -- Delete + DELETE + CU + FROM + [dbo].[CollectionUser] CU + WHERE + CU.[OrganizationUserId] = @Id + AND NOT EXISTS ( + SELECT + 1 + FROM + @Collections + WHERE + [Id] = CU.[CollectionId] + ) +END +GO + +PRINT N'Altering stored procedure, dbo.OrganizationUserOrganizationDetails_ReadByUserIdStatus...'; +GO +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatus] + @UserId UNIQUEIDENTIFIER, + @Status SMALLINT +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[OrganizationUserOrganizationDetailsView] + WHERE + [UserId] = @UserId + AND (@Status IS NULL OR [Status] = @Status) +END +GO + +PRINT N'Altering stored procedure, dbo.OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId...'; +GO +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUserOrganizationDetails_ReadByUserIdStatusOrganizationId] + @UserId UNIQUEIDENTIFIER, + @Status SMALLINT, + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[OrganizationUserOrganizationDetailsView] + WHERE + [UserId] = @UserId + AND [OrganizationId] = @OrganizationId + AND (@Status IS NULL OR [Status] = @Status) +END +GO + +PRINT N'Altering function, dbo.PolicyApplicableToUser...'; +GO +CREATE OR ALTER FUNCTION [dbo].[PolicyApplicableToUser] +( + @UserId UNIQUEIDENTIFIER, + @PolicyType TINYINT, + @MinimumStatus SMALLINT +) +RETURNS TABLE +AS RETURN +SELECT + P.* +FROM + [dbo].[PolicyView] P +INNER JOIN + [dbo].[OrganizationUserView] OU ON P.[OrganizationId] = OU.[OrganizationId] +LEFT JOIN + (SELECT + PU.UserId, + PO.OrganizationId + FROM + [dbo].[ProviderUserView] PU + INNER JOIN + [ProviderOrganizationView] PO ON PO.[ProviderId] = PU.[ProviderId]) PUPO + ON PUPO.UserId = OU.UserId + AND PUPO.OrganizationId = P.OrganizationId +WHERE + ( + ( + OU.[Status] > 0 + AND OU.[UserId] = @UserId + ) + OR ( + OU.[Status] = 0 -- 'Invited' OrgUsers are not linked to a UserId yet, so we have to look up their email + AND OU.[Email] IN (SELECT U.Email FROM [dbo].[UserView] U WHERE U.Id = @UserId) + ) + ) + AND P.[Type] = @PolicyType + AND P.[Enabled] = 1 + AND OU.[Status] >= @MinimumStatus + AND OU.[Type] >= 2 -- Not an owner (0) or admin (1) + AND ( -- Can't manage policies + OU.[Permissions] IS NULL + OR COALESCE(JSON_VALUE(OU.[Permissions], '$.managePolicies'), 'false') = 'false' + ) + AND PUPO.[UserId] IS NULL -- Not a provider +GO + +PRINT N'Altering stored procedure, dbo.OrganizationUser_Deactivate'; +GO +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_Deactivate] + @Id UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[OrganizationUser] + SET + [Status] = -1 -- Deactivated + WHERE + [Id] = @Id + + EXEC [dbo].[User_BumpAccountRevisionDateByOrganizationUserId] @Id +END +GO + +PRINT N'Altering stored procedure, dbo.OrganizationUser_Activate'; +GO +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_Activate] + @Id UNIQUEIDENTIFIER, + @Status SMALLINT +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[OrganizationUser] + SET + [Status] = @Status + WHERE + [Id] = @Id + AND [Status] = -1 -- Deactivated + + EXEC [dbo].[User_BumpAccountRevisionDateByOrganizationUserId] @Id +END +GO + +PRINT N'Finished migration for 2022-06-08_00_DeactivatedUserStatus'; +GO \ No newline at end of file diff --git a/util/MySqlMigrations/Migrations/20220608191914_DeactivatedUserStatus.Designer.cs b/util/MySqlMigrations/Migrations/20220608191914_DeactivatedUserStatus.Designer.cs new file mode 100644 index 000000000..b1689ab2f --- /dev/null +++ b/util/MySqlMigrations/Migrations/20220608191914_DeactivatedUserStatus.Designer.cs @@ -0,0 +1,1591 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Bit.MySqlMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20220608191914_DeactivatedUserStatus")] + partial class DeactivatedUserStatus + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Relational:MaxIdentifierLength", 64) + .HasAnnotation("ProductVersion", "5.0.12"); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Attachments") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Favorites") + .HasColumnType("longtext"); + + b.Property("Folders") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Reprompt") + .HasColumnType("tinyint unsigned"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Device"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("GranteeId") + .HasColumnType("char(36)"); + + b.Property("GrantorId") + .HasColumnType("char(36)"); + + b.Property("KeyEncrypted") + .HasColumnType("longtext"); + + b.Property("LastNotificationDate") + .HasColumnType("datetime(6)"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("WaitTimeDays") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ActingUserId") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("DeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("InstallationId") + .HasColumnType("char(36)"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("PolicyId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("ProviderOrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderUserId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.ToTable("Event"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Grant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ClientId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ConsumedDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Key"); + + b.ToTable("Grant"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.HasIndex("UserId"); + + b.ToTable("GroupUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Installation", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.HasKey("Id"); + + b.ToTable("Installation"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("int"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("datetime(6)"); + + b.Property("Plan") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PlanType") + .HasColumnType("tinyint unsigned"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Seats") + .HasColumnType("int"); + + b.Property("SelfHost") + .HasColumnType("tinyint(1)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("Use2fa") + .HasColumnType("tinyint(1)"); + + b.Property("UseApi") + .HasColumnType("tinyint(1)"); + + b.Property("UseDirectory") + .HasColumnType("tinyint(1)"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.Property("UseGroups") + .HasColumnType("tinyint(1)"); + + b.Property("UseKeyConnector") + .HasColumnType("tinyint(1)"); + + b.Property("UsePolicies") + .HasColumnType("tinyint(1)"); + + b.Property("UseResetPassword") + .HasColumnType("tinyint(1)"); + + b.Property("UseSso") + .HasColumnType("tinyint(1)"); + + b.Property("UseTotp") + .HasColumnType("tinyint(1)"); + + b.Property("UsersGetPremium") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ApiKey") + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Config") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("LastSyncDate") + .HasColumnType("datetime(6)"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("tinyint unsigned"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("char(36)"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("ToDelete") + .HasColumnType("tinyint(1)"); + + b.Property("ValidUntil") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.ToTable("OrganizationSponsorship"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessAll") + .HasColumnType("tinyint(1)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ResetPasswordKey") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Policy"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Provider", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasColumnType("longtext"); + + b.Property("BusinessAddress1") + .HasColumnType("longtext"); + + b.Property("BusinessAddress2") + .HasColumnType("longtext"); + + b.Property("BusinessAddress3") + .HasColumnType("longtext"); + + b.Property("BusinessCountry") + .HasColumnType("longtext"); + + b.Property("BusinessName") + .HasColumnType("longtext"); + + b.Property("BusinessTaxNumber") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Settings") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .HasColumnType("longtext"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Permissions") + .HasColumnType("longtext"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("DeletionDate") + .HasColumnType("datetime(6)"); + + b.Property("Disabled") + .HasColumnType("tinyint(1)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("HideEmail") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MaxAccessCount") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Send"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .HasColumnType("longtext"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("SsoUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("varchar(40)"); + + b.Property("Active") + .HasColumnType("tinyint(1)"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PostalCode") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Rate") + .HasColumnType("decimal(65,30)"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("varchar(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PaymentMethodType") + .HasColumnType("tinyint unsigned"); + + b.Property("Refunded") + .HasColumnType("tinyint(1)"); + + b.Property("RefundedAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Transaction"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccountRevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("varchar(30)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Culture") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailVerified") + .HasColumnType("tinyint(1)"); + + b.Property("EquivalentDomains") + .HasColumnType("longtext"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("longtext"); + + b.Property("FailedLoginCount") + .HasColumnType("int"); + + b.Property("ForcePasswordReset") + .HasColumnType("tinyint(1)"); + + b.Property("Gateway") + .HasColumnType("tinyint unsigned"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Kdf") + .HasColumnType("tinyint unsigned"); + + b.Property("KdfIterations") + .HasColumnType("int"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("LastFailedLoginDate") + .HasColumnType("datetime(6)"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Premium") + .HasColumnType("tinyint(1)"); + + b.Property("PremiumExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("PrivateKey") + .HasColumnType("longtext"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("ReferenceData") + .HasColumnType("longtext"); + + b.Property("RenewalReminderDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("UnknownDeviceVerificationEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UsesKeyConnector") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Ciphers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Ciphers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionGroups") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Collection"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Collection", "Collection") + .WithMany("CollectionUsers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany("CollectionUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("CollectionUsers") + .HasForeignKey("UserId"); + + b.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.EmergencyAccess", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantee") + .WithMany() + .HasForeignKey("GranteeId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "Grantor") + .WithMany() + .HasForeignKey("GrantorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Grantee"); + + b.Navigation("Grantor"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Folder", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Folders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Groups") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany("GroupUsers") + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", null) + .WithMany("GroupUsers") + .HasForeignKey("UserId"); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("ApiKeys") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "SponsoringOrganization") + .WithMany() + .HasForeignKey("SponsoringOrganizationId"); + + b.Navigation("SponsoredOrganization"); + + b.Navigation("SponsoringOrganization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("OrganizationUsers") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("OrganizationUsers") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("SsoUsers") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("SsoUsers") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Connections"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("CollectionUsers"); + + b.Navigation("Folders"); + + b.Navigation("GroupUsers"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/MySqlMigrations/Migrations/20220608191914_DeactivatedUserStatus.cs b/util/MySqlMigrations/Migrations/20220608191914_DeactivatedUserStatus.cs new file mode 100644 index 000000000..52b10dcb3 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20220608191914_DeactivatedUserStatus.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Bit.MySqlMigrations.Migrations +{ + public partial class DeactivatedUserStatus : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Status", + table: "OrganizationUser", + type: "smallint", + nullable: false, + oldClrType: typeof(byte), + oldType: "tinyint unsigned"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Status", + table: "OrganizationUser", + type: "tinyint unsigned", + nullable: false, + oldClrType: typeof(short), + oldType: "smallint"); + } + } +} diff --git a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs index fe49ca4ab..491d8edb9 100644 --- a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -713,8 +713,8 @@ namespace Bit.MySqlMigrations.Migrations b.Property("RevisionDate") .HasColumnType("datetime(6)"); - b.Property("Status") - .HasColumnType("tinyint unsigned"); + b.Property("Status") + .HasColumnType("smallint"); b.Property("Type") .HasColumnType("tinyint unsigned"); diff --git a/util/MySqlMigrations/Scripts/2022-06-08_00_DeactivatedUserStatus.sql b/util/MySqlMigrations/Scripts/2022-06-08_00_DeactivatedUserStatus.sql new file mode 100644 index 000000000..a0caa0443 --- /dev/null +++ b/util/MySqlMigrations/Scripts/2022-06-08_00_DeactivatedUserStatus.sql @@ -0,0 +1,8 @@ +START TRANSACTION; + +ALTER TABLE `OrganizationUser` MODIFY COLUMN `Status` smallint NOT NULL; + +INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`) +VALUES ('20220608191914_DeactivatedUserStatus', '5.0.12'); + +COMMIT; \ No newline at end of file