diff --git a/src/Api/AdminConsole/Controllers/OrganizationInviteLinksController.cs b/src/Api/AdminConsole/Controllers/OrganizationInviteLinksController.cs index 0a4c51ce9c65..44f843eba943 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationInviteLinksController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationInviteLinksController.cs @@ -29,7 +29,7 @@ public class OrganizationInviteLinksController( [HttpPost("/organizations/invite-link/status")] public async Task GetStatus([FromBody] GetOrganizationInviteLinkStatusRequestModel model) { - var result = await getOrganizationInviteLinkStatusQuery.GetStatusAsync(model.Code); + var result = await getOrganizationInviteLinkStatusQuery.GetStatusAsync(model.OrganizationId, model.Code); return Handle(result, status => TypedResults.Ok(new OrganizationInviteLinkStatusResponseModel( @@ -46,7 +46,7 @@ status.Sso is null [HttpPost("/organizations/invite-link/policies")] public async Task GetPolicies([FromBody] GetOrganizationInviteLinkPoliciesRequestModel model) { - var result = await getOrganizationInviteLinkPoliciesQuery.GetPoliciesAsync(model.Code); + var result = await getOrganizationInviteLinkPoliciesQuery.GetPoliciesAsync(model.OrganizationId, model.Code); return Handle(result, policies => TypedResults.Ok(new ListResponseModel( policies.Select(p => new PolicyResponseModel(p))))); @@ -57,7 +57,7 @@ public async Task GetPolicies([FromBody] GetOrganizationInviteLinkPolic public async Task ValidateEmailDomain( [FromBody] OrganizationInviteLinkValidateEmailDomainRequestModel model) { - var result = await validateOrganizationInviteLinkEmailDomainQuery.ValidateAsync(model.Code, model.Email); + var result = await validateOrganizationInviteLinkEmailDomainQuery.ValidateAsync(model.OrganizationId, model.Code, model.Email); return Handle(result, isAllowed => TypedResults.Ok(new OrganizationInviteLinkValidateEmailDomainResponseModel(isAllowed))); diff --git a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs index 62e07987d155..d30b2c729f96 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationUsersController.cs @@ -865,6 +865,7 @@ public async Task AcceptInviteLink([FromBody] AcceptOrganizationInviteL var result = await _acceptOrganizationInviteLinkCommand.AcceptAsync(new AcceptOrganizationInviteLinkRequest { + OrganizationId = model.OrganizationId, Code = model.Code, User = user, ResetPasswordKey = model.ResetPasswordKey, @@ -885,6 +886,7 @@ public async Task ConfirmInviteLink([FromBody] ConfirmOrganizationInvit var result = await _confirmOrganizationInviteLinkCommand.ConfirmAsync(new ConfirmOrganizationInviteLinkRequest { + OrganizationId = model.OrganizationId, Code = model.Code, User = user, OrgUserKey = model.OrgUserKey, diff --git a/src/Api/AdminConsole/Models/Request/Organizations/AcceptOrganizationInviteLinkRequestModel.cs b/src/Api/AdminConsole/Models/Request/Organizations/AcceptOrganizationInviteLinkRequestModel.cs index 791fcf99a78f..28131c2e95f3 100644 --- a/src/Api/AdminConsole/Models/Request/Organizations/AcceptOrganizationInviteLinkRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/Organizations/AcceptOrganizationInviteLinkRequestModel.cs @@ -4,7 +4,11 @@ namespace Bit.Api.AdminConsole.Models.Request.Organizations; public class AcceptOrganizationInviteLinkRequestModel { + [Required] + public required Guid OrganizationId { get; set; } + [Required] public required Guid Code { get; set; } + public string? ResetPasswordKey { get; set; } } diff --git a/src/Api/AdminConsole/Models/Request/Organizations/ConfirmOrganizationInviteLinkRequestModel.cs b/src/Api/AdminConsole/Models/Request/Organizations/ConfirmOrganizationInviteLinkRequestModel.cs index 569031192d38..f58e1a46b2bc 100644 --- a/src/Api/AdminConsole/Models/Request/Organizations/ConfirmOrganizationInviteLinkRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/Organizations/ConfirmOrganizationInviteLinkRequestModel.cs @@ -5,6 +5,9 @@ namespace Bit.Api.AdminConsole.Models.Request.Organizations; public class ConfirmOrganizationInviteLinkRequestModel { + [Required] + public required Guid OrganizationId { get; set; } + [Required] public required Guid Code { get; set; } diff --git a/src/Api/AdminConsole/Models/Request/Organizations/GetOrganizationInviteLinkPoliciesRequestModel.cs b/src/Api/AdminConsole/Models/Request/Organizations/GetOrganizationInviteLinkPoliciesRequestModel.cs index 2bdbaf87b60c..25c830e2c613 100644 --- a/src/Api/AdminConsole/Models/Request/Organizations/GetOrganizationInviteLinkPoliciesRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/Organizations/GetOrganizationInviteLinkPoliciesRequestModel.cs @@ -4,6 +4,9 @@ namespace Bit.Api.AdminConsole.Models.Request.Organizations; public class GetOrganizationInviteLinkPoliciesRequestModel { + [Required] + public required Guid OrganizationId { get; set; } + [Required] public required Guid Code { get; set; } } diff --git a/src/Api/AdminConsole/Models/Request/Organizations/GetOrganizationInviteLinkStatusRequestModel.cs b/src/Api/AdminConsole/Models/Request/Organizations/GetOrganizationInviteLinkStatusRequestModel.cs index 1173d2a95c17..2fe1c826cb65 100644 --- a/src/Api/AdminConsole/Models/Request/Organizations/GetOrganizationInviteLinkStatusRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/Organizations/GetOrganizationInviteLinkStatusRequestModel.cs @@ -4,6 +4,9 @@ namespace Bit.Api.AdminConsole.Models.Request.Organizations; public class GetOrganizationInviteLinkStatusRequestModel { + [Required] + public required Guid OrganizationId { get; set; } + [Required] public required Guid Code { get; set; } } diff --git a/src/Api/AdminConsole/Models/Request/Organizations/OrganizationInviteLinkValidateEmailDomainRequestModel.cs b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationInviteLinkValidateEmailDomainRequestModel.cs index 8538558817c8..acdf03e98e4a 100644 --- a/src/Api/AdminConsole/Models/Request/Organizations/OrganizationInviteLinkValidateEmailDomainRequestModel.cs +++ b/src/Api/AdminConsole/Models/Request/Organizations/OrganizationInviteLinkValidateEmailDomainRequestModel.cs @@ -4,6 +4,9 @@ namespace Bit.Api.AdminConsole.Models.Request.Organizations; public class OrganizationInviteLinkValidateEmailDomainRequestModel { + [Required] + public required Guid OrganizationId { get; set; } + [Required] public required Guid Code { get; set; } diff --git a/src/Api/AdminConsole/Models/Response/Organizations/OrganizationInviteLinkResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationInviteLinkResponseModel.cs index bd0cf8bef969..de604e62e083 100644 --- a/src/Api/AdminConsole/Models/Response/Organizations/OrganizationInviteLinkResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationInviteLinkResponseModel.cs @@ -13,7 +13,7 @@ public OrganizationInviteLinkResponseModel(OrganizationInviteLink inviteLink) ArgumentNullException.ThrowIfNull(inviteLink); Id = inviteLink.Id; - Code = inviteLink.Code; + Code = Guid.Parse(inviteLink.Code); OrganizationId = inviteLink.OrganizationId; AllowedDomains = inviteLink.GetAllowedDomains(); Invite = inviteLink.Invite; diff --git a/src/Core/AdminConsole/Entities/OrganizationInviteLink.cs b/src/Core/AdminConsole/Entities/OrganizationInviteLink.cs index 86d181ade622..ab67ca87bcff 100644 --- a/src/Core/AdminConsole/Entities/OrganizationInviteLink.cs +++ b/src/Core/AdminConsole/Entities/OrganizationInviteLink.cs @@ -16,12 +16,7 @@ public class OrganizationInviteLink : ITableObject /// /// A random, secret code embedded in the invite link to ensure it cannot be guessed. /// - /// - /// Uses rather than a sequential/comb GUID because this is not - /// a table identifier and therefore does not need index-friendly ordering. A comb GUID's embedded - /// timestamp would also make the code partially predictable. - /// - public Guid Code { get; set; } = Guid.NewGuid(); + public string Code { get; set; } = null!; /// /// The ID of the this invite link belongs to. /// @@ -75,4 +70,34 @@ public void SetNewId() Id = CoreHelpers.GenerateComb(); } } + + /// + /// Initializes to a new random GUID string. + /// + /// + /// Uses rather than a sequential/comb GUID because this is a + /// bearer secret — not a table identifier — and does not need index-friendly ordering. + /// A comb GUID's embedded timestamp would also make the code partially predictable. + /// + public void SetNewCode() + { + if (string.IsNullOrEmpty(Code)) + { + Code = Guid.NewGuid().ToString(); + } + } + + /// + /// Constant-time comparison of against ; + /// returns false for null or empty input. + /// + public bool CodeMatches(string? providedCode) + { + if (string.IsNullOrEmpty(providedCode) || string.IsNullOrEmpty(Code)) + { + return false; + } + + return CoreHelpers.FixedTimeEquals(providedCode, Code); + } } diff --git a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/AcceptOrganizationInviteLinkCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/AcceptOrganizationInviteLinkCommand.cs index 8e7c75969330..fd34fdfa519a 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/AcceptOrganizationInviteLinkCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/AcceptOrganizationInviteLinkCommand.cs @@ -42,8 +42,8 @@ public async Task> AcceptAsync(AcceptOrganizatio { var user = request.User; - var link = await organizationInviteLinkRepository.GetByCodeAsync(request.Code); - if (link is null) + var link = await organizationInviteLinkRepository.GetByOrganizationIdAsync(request.OrganizationId); + if (link is null || !link.CodeMatches(request.Code.ToString())) { return new InviteLinkNotFound(); } diff --git a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/AcceptOrganizationInviteLinkRequest.cs b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/AcceptOrganizationInviteLinkRequest.cs index 670f396e58f6..05b018b4f350 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/AcceptOrganizationInviteLinkRequest.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/AcceptOrganizationInviteLinkRequest.cs @@ -4,6 +4,7 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.InviteLinks; public record AcceptOrganizationInviteLinkRequest { + public required Guid OrganizationId { get; init; } public required Guid Code { get; init; } public required User User { get; init; } public string? ResetPasswordKey { get; init; } diff --git a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkCommand.cs index 6e89f8f626a1..1b83ccbeb7fb 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkCommand.cs @@ -46,6 +46,7 @@ public async Task ConfirmAsync(ConfirmOrganizationInviteLinkReque var validationResult = await confirmOrganizationInviteLinkValidator.ValidateAsync( new ConfirmOrganizationInviteLinkValidationRequest { + OrganizationId = request.OrganizationId, Code = request.Code, User = user, }); diff --git a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkRequest.cs b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkRequest.cs index dd2f6a574a09..21cba30efa1d 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkRequest.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkRequest.cs @@ -10,6 +10,11 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.InviteLinks; /// public record ConfirmOrganizationInviteLinkRequest { + /// + /// The organization the user is confirming into. + /// + public required Guid OrganizationId { get; init; } + /// /// The secret code embedded in the invite link the user is confirming with. /// diff --git a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkValidationRequest.cs b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkValidationRequest.cs index 955d8e788500..19ad3a1928ef 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkValidationRequest.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkValidationRequest.cs @@ -9,6 +9,11 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.InviteLinks; /// public record ConfirmOrganizationInviteLinkValidationRequest { + /// + /// The organization the user is attempting to join. + /// + public required Guid OrganizationId { get; init; } + /// /// The secret code embedded in the invite link the user is attempting to use. /// diff --git a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkValidator.cs b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkValidator.cs index 28bc4d5dde5b..0c33f16550b2 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkValidator.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkValidator.cs @@ -44,8 +44,8 @@ public async Task> { var user = request.User; - var link = await organizationInviteLinkRepository.GetByCodeAsync(request.Code); - if (link is null) + var link = await organizationInviteLinkRepository.GetByOrganizationIdAsync(request.OrganizationId); + if (link is null || !link.CodeMatches(request.Code.ToString())) { return new InviteLinkNotFound(); } diff --git a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/CreateOrganizationInviteLinkCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/CreateOrganizationInviteLinkCommand.cs index dce44815841a..e541dbcfb4f5 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/CreateOrganizationInviteLinkCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/CreateOrganizationInviteLinkCommand.cs @@ -49,6 +49,7 @@ public async Task> CreateAsync( }; inviteLink.SetAllowedDomains(sanitizedDomains); inviteLink.SetNewId(); + inviteLink.SetNewCode(); await organizationInviteLinkRepository.CreateAsync(inviteLink); diff --git a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkPoliciesQuery.cs b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkPoliciesQuery.cs index af1af824a9c4..c42918183aa4 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkPoliciesQuery.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkPoliciesQuery.cs @@ -13,10 +13,10 @@ public class GetOrganizationInviteLinkPoliciesQuery( IPolicyRepository policyRepository) : IGetOrganizationInviteLinkPoliciesQuery { - public async Task>> GetPoliciesAsync(Guid code) + public async Task>> GetPoliciesAsync(Guid organizationId, Guid code) { - var inviteLink = await organizationInviteLinkRepository.GetByCodeAsync(code); - if (inviteLink is null) + var inviteLink = await organizationInviteLinkRepository.GetByOrganizationIdAsync(organizationId); + if (inviteLink is null || !inviteLink.CodeMatches(code.ToString())) { return new InviteLinkNotFound(); } diff --git a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkStatusQuery.cs b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkStatusQuery.cs index a53eb2179f41..27e62546d96b 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkStatusQuery.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkStatusQuery.cs @@ -16,10 +16,10 @@ public class GetOrganizationInviteLinkStatusQuery( IPolicyRepository policyRepository) : IGetOrganizationInviteLinkStatusQuery { - public async Task> GetStatusAsync(Guid code) + public async Task> GetStatusAsync(Guid organizationId, Guid code) { - var inviteLink = await organizationInviteLinkRepository.GetByCodeAsync(code); - if (inviteLink is null) + var inviteLink = await organizationInviteLinkRepository.GetByOrganizationIdAsync(organizationId); + if (inviteLink is null || !inviteLink.CodeMatches(code.ToString())) { return new InviteLinkNotFound(); } diff --git a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/Interfaces/IGetOrganizationInviteLinkPoliciesQuery.cs b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/Interfaces/IGetOrganizationInviteLinkPoliciesQuery.cs index c96e5201272b..81e27c3f260a 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/Interfaces/IGetOrganizationInviteLinkPoliciesQuery.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/Interfaces/IGetOrganizationInviteLinkPoliciesQuery.cs @@ -6,12 +6,13 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.InviteLinks.Interfaces; public interface IGetOrganizationInviteLinkPoliciesQuery { /// - /// Gets the enabled policies for an organization by invite link code, for display to an anonymous user. + /// Gets the enabled policies for an organization by invite link, for display to an anonymous user. /// - /// The public invite link code (from the URL). + /// The organization's ID (from the URL path). + /// The public invite link code (bearer secret from the URL). /// /// The enabled records for the organization if the link is valid, or an error /// if the link is not found, the organization is disabled, or the feature / policies are unavailable. /// - Task>> GetPoliciesAsync(Guid code); + Task>> GetPoliciesAsync(Guid organizationId, Guid code); } diff --git a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/Interfaces/IGetOrganizationInviteLinkStatusQuery.cs b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/Interfaces/IGetOrganizationInviteLinkStatusQuery.cs index 56b142c26755..440b65025de3 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/Interfaces/IGetOrganizationInviteLinkStatusQuery.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/Interfaces/IGetOrganizationInviteLinkStatusQuery.cs @@ -5,12 +5,13 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.InviteLinks.Interfaces; public interface IGetOrganizationInviteLinkStatusQuery { /// - /// Gets the status of an invite link by its public code, for display to an anonymous user. + /// Gets the status of an invite link by its organization ID and code, for display to an anonymous user. /// - /// The public invite link code (from the URL). + /// The organization's ID (from the URL path). + /// The public invite link code (bearer secret from the URL). /// /// An if the link is valid, or an error if the link is not /// found or the organization is disabled. The status indicates whether the invite links feature is enabled. /// - Task> GetStatusAsync(Guid code); + Task> GetStatusAsync(Guid organizationId, Guid code); } diff --git a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/Interfaces/IValidateOrganizationInviteLinkEmailDomainQuery.cs b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/Interfaces/IValidateOrganizationInviteLinkEmailDomainQuery.cs index 7d7868200586..1f898b16538a 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/Interfaces/IValidateOrganizationInviteLinkEmailDomainQuery.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/Interfaces/IValidateOrganizationInviteLinkEmailDomainQuery.cs @@ -6,7 +6,7 @@ public interface IValidateOrganizationInviteLinkEmailDomainQuery { /// /// Returns whether the email's domain is allowed by the invite link, - /// or an error if the invite link does not exist. + /// or an error if the invite link does not exist or the code does not match. /// - Task> ValidateAsync(Guid code, string email); + Task> ValidateAsync(Guid organizationId, Guid code, string email); } diff --git a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/RefreshOrganizationInviteLinkCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/RefreshOrganizationInviteLinkCommand.cs index 940a264a7a5f..97f0db8768cb 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/RefreshOrganizationInviteLinkCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/RefreshOrganizationInviteLinkCommand.cs @@ -42,6 +42,7 @@ public async Task> RefreshAsync( RevisionDate = now, }; newLink.SetNewId(); + newLink.SetNewCode(); await organizationInviteLinkRepository.RefreshAsync(existing, newLink); diff --git a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ValidateOrganizationInviteLinkEmailDomainQuery.cs b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ValidateOrganizationInviteLinkEmailDomainQuery.cs index 37ef37ea9de3..c1dbeeaf56e8 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ValidateOrganizationInviteLinkEmailDomainQuery.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/InviteLinks/ValidateOrganizationInviteLinkEmailDomainQuery.cs @@ -9,10 +9,10 @@ public class ValidateOrganizationInviteLinkEmailDomainQuery( IOrganizationInviteLinkRepository organizationInviteLinkRepository) : IValidateOrganizationInviteLinkEmailDomainQuery { - public async Task> ValidateAsync(Guid code, string email) + public async Task> ValidateAsync(Guid organizationId, Guid code, string email) { - var link = await organizationInviteLinkRepository.GetByCodeAsync(code); - if (link is null) + var link = await organizationInviteLinkRepository.GetByOrganizationIdAsync(organizationId); + if (link is null || !link.CodeMatches(code.ToString())) { return new InviteLinkNotFound(); } diff --git a/src/Core/AdminConsole/Repositories/IOrganizationInviteLinkRepository.cs b/src/Core/AdminConsole/Repositories/IOrganizationInviteLinkRepository.cs index b1354783e1fd..237bf245f9e3 100644 --- a/src/Core/AdminConsole/Repositories/IOrganizationInviteLinkRepository.cs +++ b/src/Core/AdminConsole/Repositories/IOrganizationInviteLinkRepository.cs @@ -5,13 +5,6 @@ namespace Bit.Core.AdminConsole.Repositories; public interface IOrganizationInviteLinkRepository : IRepository { - /// - /// Gets an organization invite link by its code. - /// - /// The code of the organization invite link. - /// The organization invite link if found, otherwise null. - Task GetByCodeAsync(Guid code); - /// /// Gets an organization invite link by its organization ID. /// diff --git a/src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationInviteLinkRepository.cs b/src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationInviteLinkRepository.cs index fda27e450248..c03c05f74373 100644 --- a/src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationInviteLinkRepository.cs +++ b/src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationInviteLinkRepository.cs @@ -1,9 +1,11 @@ using System.Data; +using Bit.Core; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Repositories; using Bit.Core.Settings; using Bit.Infrastructure.Dapper.Repositories; using Dapper; +using Microsoft.AspNetCore.DataProtection; using Microsoft.Data.SqlClient; namespace Bit.Infrastructure.Dapper.AdminConsole.Repositories; @@ -11,22 +13,30 @@ namespace Bit.Infrastructure.Dapper.AdminConsole.Repositories; public class OrganizationInviteLinkRepository : Repository, IOrganizationInviteLinkRepository { - public OrganizationInviteLinkRepository(GlobalSettings globalSettings) - : this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString) + private readonly IDataProtector _dataProtector; + + public OrganizationInviteLinkRepository( + GlobalSettings globalSettings, + IDataProtectionProvider dataProtectionProvider) + : this(globalSettings.SqlServer.ConnectionString, + globalSettings.SqlServer.ReadOnlyConnectionString, + dataProtectionProvider) { } - public OrganizationInviteLinkRepository(string connectionString, string readOnlyConnectionString) + public OrganizationInviteLinkRepository( + string connectionString, + string readOnlyConnectionString, + IDataProtectionProvider dataProtectionProvider) : base(connectionString, readOnlyConnectionString) - { } + { + _dataProtector = dataProtectionProvider.CreateProtector(Constants.DatabaseFieldProtectorPurpose); + } - public async Task GetByCodeAsync(Guid code) + public override async Task GetByIdAsync(Guid id) { - using var connection = new SqlConnection(ConnectionString); - var results = await connection.QueryAsync( - $"[{Schema}].[{Table}_ReadByCode]", - new { Code = code }, - commandType: CommandType.StoredProcedure); - return results.SingleOrDefault(); + var link = await base.GetByIdAsync(id); + UnprotectData(link); + return link; } public async Task GetByOrganizationIdAsync(Guid organizationId) @@ -36,44 +46,98 @@ public OrganizationInviteLinkRepository(string connectionString, string readOnly $"[{Schema}].[{Table}_ReadByOrganizationId]", new { OrganizationId = organizationId }, commandType: CommandType.StoredProcedure); - return results.SingleOrDefault(); + var link = results.SingleOrDefault(); + UnprotectData(link); + return link; + } + + public override async Task CreateAsync(OrganizationInviteLink link) + { + await ProtectDataAndSaveAsync(link, () => base.CreateAsync(link)); + return link; + } + + public override async Task ReplaceAsync(OrganizationInviteLink link) + { + await ProtectDataAndSaveAsync(link, () => base.ReplaceAsync(link)); } public async Task RefreshAsync(OrganizationInviteLink oldLink, OrganizationInviteLink newLink) { - await using var connection = new SqlConnection(ConnectionString); - await connection.OpenAsync(); - await using var transaction = await connection.BeginTransactionAsync(); + var originalCode = newLink.Code; + ProtectData(newLink); try { - await connection.ExecuteAsync( - $"[{Schema}].[{Table}_DeleteById]", - new { Id = oldLink.Id }, - transaction: transaction, - commandType: CommandType.StoredProcedure); + await using var connection = new SqlConnection(ConnectionString); + await connection.OpenAsync(); + await using var transaction = await connection.BeginTransactionAsync(); + try + { + await connection.ExecuteAsync( + $"[{Schema}].[{Table}_DeleteById]", + new { Id = oldLink.Id }, + transaction: transaction, + commandType: CommandType.StoredProcedure); - await connection.ExecuteAsync( - $"[{Schema}].[{Table}_Create]", - new - { - newLink.Id, - newLink.Code, - newLink.OrganizationId, - newLink.AllowedDomains, - newLink.Invite, - newLink.SupportsConfirmation, - newLink.CreationDate, - newLink.RevisionDate, - }, - transaction: transaction, - commandType: CommandType.StoredProcedure); + await connection.ExecuteAsync( + $"[{Schema}].[{Table}_Create]", + new + { + newLink.Id, + newLink.Code, + newLink.OrganizationId, + newLink.AllowedDomains, + newLink.Invite, + newLink.SupportsConfirmation, + newLink.CreationDate, + newLink.RevisionDate, + }, + transaction: transaction, + commandType: CommandType.StoredProcedure); - await transaction.CommitAsync(); + await transaction.CommitAsync(); + } + catch + { + await transaction.RollbackAsync(); + throw; + } + } + finally + { + newLink.Code = originalCode; } - catch + } + + private async Task ProtectDataAndSaveAsync(OrganizationInviteLink link, Func saveTask) + { + var originalCode = link.Code; + ProtectData(link); + try + { + await saveTask(); + } + finally + { + link.Code = originalCode; + } + } + + private void ProtectData(OrganizationInviteLink link) + { + if (!link.Code?.StartsWith(Constants.DatabaseFieldProtectedPrefix) ?? false) + { + link.Code = string.Concat(Constants.DatabaseFieldProtectedPrefix, + _dataProtector.Protect(link.Code!)); + } + } + + private void UnprotectData(OrganizationInviteLink? link) + { + if (link?.Code?.StartsWith(Constants.DatabaseFieldProtectedPrefix) ?? false) { - await transaction.RollbackAsync(); - throw; + link.Code = _dataProtector.Unprotect( + link.Code.Substring(Constants.DatabaseFieldProtectedPrefix.Length)); } } } diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Configurations/OrganizationInviteLinkEntityTypeConfiguration.cs b/src/Infrastructure.EntityFramework/AdminConsole/Configurations/OrganizationInviteLinkEntityTypeConfiguration.cs index a56944141fa1..499d6df40a54 100644 --- a/src/Infrastructure.EntityFramework/AdminConsole/Configurations/OrganizationInviteLinkEntityTypeConfiguration.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Configurations/OrganizationInviteLinkEntityTypeConfiguration.cs @@ -17,11 +17,6 @@ public void Configure(EntityTypeBuilder builder) .IsUnique() .IsClustered(false); - builder - .HasIndex(e => e.Code) - .IsUnique() - .IsClustered(false); - builder.ToTable(nameof(OrganizationInviteLink)); } } diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationInviteLinkRepository.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationInviteLinkRepository.cs index addecea2f135..9c2bca74e7ef 100644 --- a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationInviteLinkRepository.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationInviteLinkRepository.cs @@ -1,7 +1,9 @@ using AutoMapper; +using Bit.Core; using Bit.Core.AdminConsole.Repositories; using Bit.Infrastructure.EntityFramework.AdminConsole.Models; using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.AspNetCore.DataProtection; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using AdminConsoleEntities = Bit.Core.AdminConsole.Entities; @@ -12,19 +14,22 @@ public class OrganizationInviteLinkRepository : Repository, IOrganizationInviteLinkRepository { + private readonly IDataProtector _dataProtector; + public OrganizationInviteLinkRepository( - IServiceScopeFactory serviceScopeFactory, IMapper mapper) + IServiceScopeFactory serviceScopeFactory, IMapper mapper, + IDataProtectionProvider dataProtectionProvider) : base(serviceScopeFactory, mapper, (DatabaseContext context) => context.OrganizationInviteLinks) - { } + { + _dataProtector = dataProtectionProvider.CreateProtector(Constants.DatabaseFieldProtectorPurpose); + } - public async Task GetByCodeAsync(Guid code) + public override async Task GetByIdAsync(Guid id) { - using var scope = ServiceScopeFactory.CreateScope(); - var dbContext = GetDatabaseContext(scope); - var result = await dbContext.OrganizationInviteLinks - .FirstOrDefaultAsync(e => e.Code == code); - return Mapper.Map(result); + var link = await base.GetByIdAsync(id); + UnprotectData(link); + return link; } public async Task GetByOrganizationIdAsync( @@ -34,24 +39,80 @@ public OrganizationInviteLinkRepository( var dbContext = GetDatabaseContext(scope); var result = await dbContext.OrganizationInviteLinks .FirstOrDefaultAsync(e => e.OrganizationId == organizationId); - return Mapper.Map(result); + var link = Mapper.Map(result); + UnprotectData(link); + return link; + } + + public override async Task CreateAsync( + AdminConsoleEntities.OrganizationInviteLink link) + { + await ProtectDataAndSaveAsync(link, () => base.CreateAsync(link)); + return link; + } + + public override async Task ReplaceAsync(AdminConsoleEntities.OrganizationInviteLink link) + { + await ProtectDataAndSaveAsync(link, () => base.ReplaceAsync(link)); } public async Task RefreshAsync( AdminConsoleEntities.OrganizationInviteLink oldLink, AdminConsoleEntities.OrganizationInviteLink newLink) { - using var scope = ServiceScopeFactory.CreateScope(); - var dbContext = GetDatabaseContext(scope); - await using var transaction = await dbContext.Database.BeginTransactionAsync(); + var originalCode = newLink.Code; + ProtectData(newLink); + try + { + using var scope = ServiceScopeFactory.CreateScope(); + var dbContext = GetDatabaseContext(scope); + await using var transaction = await dbContext.Database.BeginTransactionAsync(); + + await dbContext.OrganizationInviteLinks + .Where(e => e.Id == oldLink.Id) + .ExecuteDeleteAsync(); - await dbContext.OrganizationInviteLinks - .Where(e => e.Id == oldLink.Id) - .ExecuteDeleteAsync(); + var efNew = Mapper.Map(newLink); + await dbContext.OrganizationInviteLinks.AddAsync(efNew); + await dbContext.SaveChangesAsync(); + await transaction.CommitAsync(); + } + finally + { + newLink.Code = originalCode; + } + } - var efNew = Mapper.Map(newLink); - await dbContext.OrganizationInviteLinks.AddAsync(efNew); - await dbContext.SaveChangesAsync(); - await transaction.CommitAsync(); + private async Task ProtectDataAndSaveAsync( + AdminConsoleEntities.OrganizationInviteLink link, Func saveTask) + { + var originalCode = link.Code; + ProtectData(link); + try + { + await saveTask(); + } + finally + { + link.Code = originalCode; + } + } + + private void ProtectData(AdminConsoleEntities.OrganizationInviteLink link) + { + if (!link.Code?.StartsWith(Constants.DatabaseFieldProtectedPrefix) ?? false) + { + link.Code = string.Concat(Constants.DatabaseFieldProtectedPrefix, + _dataProtector.Protect(link.Code!)); + } + } + + private void UnprotectData(AdminConsoleEntities.OrganizationInviteLink? link) + { + if (link?.Code?.StartsWith(Constants.DatabaseFieldProtectedPrefix) ?? false) + { + link.Code = _dataProtector.Unprotect( + link.Code.Substring(Constants.DatabaseFieldProtectedPrefix.Length)); + } } } diff --git a/src/Sql/dbo/Stored Procedures/OrganizationInviteLink_Create.sql b/src/Sql/dbo/Stored Procedures/OrganizationInviteLink_Create.sql index 84bf5f5f5262..6e1a07fe4ab4 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationInviteLink_Create.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationInviteLink_Create.sql @@ -1,6 +1,6 @@ CREATE PROCEDURE [dbo].[OrganizationInviteLink_Create] @Id UNIQUEIDENTIFIER OUTPUT, - @Code UNIQUEIDENTIFIER, + @Code NVARCHAR(300), @OrganizationId UNIQUEIDENTIFIER, @AllowedDomains NVARCHAR(MAX), @Invite NVARCHAR(MAX), diff --git a/src/Sql/dbo/Stored Procedures/OrganizationInviteLink_ReadByCode.sql b/src/Sql/dbo/Stored Procedures/OrganizationInviteLink_ReadByCode.sql deleted file mode 100644 index 5715f6ebab62..000000000000 --- a/src/Sql/dbo/Stored Procedures/OrganizationInviteLink_ReadByCode.sql +++ /dev/null @@ -1,13 +0,0 @@ -CREATE PROCEDURE [dbo].[OrganizationInviteLink_ReadByCode] - @Code UNIQUEIDENTIFIER -AS -BEGIN - SET NOCOUNT ON - - SELECT - * - FROM - [dbo].[OrganizationInviteLinkView] - WHERE - [Code] = @Code -END diff --git a/src/Sql/dbo/Stored Procedures/OrganizationInviteLink_Update.sql b/src/Sql/dbo/Stored Procedures/OrganizationInviteLink_Update.sql index a270e8eb2be5..83ecb16496ec 100644 --- a/src/Sql/dbo/Stored Procedures/OrganizationInviteLink_Update.sql +++ b/src/Sql/dbo/Stored Procedures/OrganizationInviteLink_Update.sql @@ -1,6 +1,6 @@ CREATE PROCEDURE [dbo].[OrganizationInviteLink_Update] @Id UNIQUEIDENTIFIER, - @Code UNIQUEIDENTIFIER, + @Code NVARCHAR(300), @OrganizationId UNIQUEIDENTIFIER, @AllowedDomains NVARCHAR(MAX), @Invite NVARCHAR(MAX), diff --git a/src/Sql/dbo/Tables/OrganizationInviteLink.sql b/src/Sql/dbo/Tables/OrganizationInviteLink.sql index c42299f1c2cd..680cd6eb7620 100644 --- a/src/Sql/dbo/Tables/OrganizationInviteLink.sql +++ b/src/Sql/dbo/Tables/OrganizationInviteLink.sql @@ -1,7 +1,7 @@ CREATE TABLE [dbo].[OrganizationInviteLink] ( [Id] UNIQUEIDENTIFIER NOT NULL, - [Code] UNIQUEIDENTIFIER NOT NULL, + [Code] NVARCHAR(300) NOT NULL, [OrganizationId] UNIQUEIDENTIFIER NOT NULL, [AllowedDomains] NVARCHAR(MAX) NOT NULL, [Invite] NVARCHAR(MAX) NOT NULL, @@ -17,6 +17,3 @@ CREATE UNIQUE NONCLUSTERED INDEX [IX_OrganizationInviteLink_OrganizationId] ON [dbo].[OrganizationInviteLink]([OrganizationId] ASC); GO -CREATE UNIQUE NONCLUSTERED INDEX [IX_OrganizationInviteLink_Code] - ON [dbo].[OrganizationInviteLink]([Code] ASC); -GO diff --git a/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationInviteLinksControllerTests.cs b/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationInviteLinksControllerTests.cs index 6257b06bb5b3..f8e6534adab1 100644 --- a/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationInviteLinksControllerTests.cs +++ b/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationInviteLinksControllerTests.cs @@ -85,6 +85,7 @@ public async Task ValidateEmailDomain_WithAllowedEmail_ReturnsIsAllowedTrue() var validateRequest = new OrganizationInviteLinkValidateEmailDomainRequestModel { + OrganizationId = _organization.Id, Code = created.Code, Email = "user@acme.com", }; @@ -264,7 +265,7 @@ public async Task GetStatus_WithExistingLink_ReturnsData() var anonClient = _factory.CreateClient(); var statusResponse = await anonClient.PostAsJsonAsync( "/organizations/invite-link/status", - new GetOrganizationInviteLinkStatusRequestModel { Code = created.Code }); + new GetOrganizationInviteLinkStatusRequestModel { OrganizationId = _organization.Id, Code = created.Code }); Assert.Equal(HttpStatusCode.OK, statusResponse.StatusCode); var status = await statusResponse.Content.ReadFromJsonAsync(); @@ -293,7 +294,7 @@ public async Task GetPolicies_WithExistingLink_ReturnsListResponseModel() var anonClient = _factory.CreateClient(); var policiesResponse = await anonClient.PostAsJsonAsync( "/organizations/invite-link/policies", - new GetOrganizationInviteLinkPoliciesRequestModel { Code = created.Code }); + new GetOrganizationInviteLinkPoliciesRequestModel { OrganizationId = _organization.Id, Code = created.Code }); Assert.Equal(HttpStatusCode.OK, policiesResponse.StatusCode); var body = await policiesResponse.Content.ReadFromJsonAsync>(); diff --git a/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationUsersControllerAcceptInviteLinkTests.cs b/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationUsersControllerAcceptInviteLinkTests.cs index 915bf5769b5d..9b50a6e91cbd 100644 --- a/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationUsersControllerAcceptInviteLinkTests.cs +++ b/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationUsersControllerAcceptInviteLinkTests.cs @@ -84,7 +84,7 @@ public async Task AcceptInviteLink_WithValidRequest_ReturnsOk() var joinerLoginHelper = new LoginHelper(_factory, joinerClient); await joinerLoginHelper.LoginAsync(joinerEmail); - var acceptRequest = new AcceptOrganizationInviteLinkRequestModel { Code = created.Code }; + var acceptRequest = new AcceptOrganizationInviteLinkRequestModel { OrganizationId = created.OrganizationId, Code = created.Code }; var response = await joinerClient.PostAsJsonAsync( "/organizations/users/invite-link/accept", acceptRequest); diff --git a/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationUsersControllerConfirmInviteLinkTests.cs b/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationUsersControllerConfirmInviteLinkTests.cs index 947fba935e44..d08de46a2d11 100644 --- a/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationUsersControllerConfirmInviteLinkTests.cs +++ b/test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationUsersControllerConfirmInviteLinkTests.cs @@ -77,7 +77,7 @@ public async Task ConfirmInviteLink_WithValidRequest_ReturnsOkAndConfirmsMembers // Act var response = await joinerClient.PostAsJsonAsync( - "/organizations/users/invite-link/confirm", BuildConfirmRequest(code)); + "/organizations/users/invite-link/confirm", BuildConfirmRequest(_organization.Id, code)); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -101,7 +101,7 @@ public async Task ConfirmInviteLink_WithUnknownCode_ReturnsNotFound() // Act var response = await joinerClient.PostAsJsonAsync( - "/organizations/users/invite-link/confirm", BuildConfirmRequest(Guid.NewGuid())); + "/organizations/users/invite-link/confirm", BuildConfirmRequest(_organization.Id, Guid.NewGuid())); // Assert Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); @@ -115,13 +115,13 @@ public async Task ConfirmInviteLink_WhenAlreadyConfirmed_ReturnsValidationProble var (joinerClient, _) = await CreateJoinerClientAsync(); var firstResponse = await joinerClient.PostAsJsonAsync( - "/organizations/users/invite-link/confirm", BuildConfirmRequest(code)); + "/organizations/users/invite-link/confirm", BuildConfirmRequest(_organization.Id, code)); Assert.Equal(HttpStatusCode.OK, firstResponse.StatusCode); // Act // Confirming again finds the already-confirmed membership and fails with a 400 validation problem. var secondResponse = await joinerClient.PostAsJsonAsync( - "/organizations/users/invite-link/confirm", BuildConfirmRequest(code)); + "/organizations/users/invite-link/confirm", BuildConfirmRequest(_organization.Id, code)); // Assert Assert.Equal(HttpStatusCode.BadRequest, secondResponse.StatusCode); @@ -162,9 +162,10 @@ private async Task CreateInviteLinkAsync() return (joinerClient, joinerEmail); } - private static ConfirmOrganizationInviteLinkRequestModel BuildConfirmRequest(Guid code) => + private static ConfirmOrganizationInviteLinkRequestModel BuildConfirmRequest(Guid organizationId, Guid code) => new() { + OrganizationId = organizationId, Code = code, OrgUserKey = _validEncryptedKey, DefaultUserCollectionName = _validEncryptedKey, diff --git a/test/Api.Test/AdminConsole/Controllers/OrganizationInviteLinksControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationInviteLinksControllerTests.cs index 3ed917c06d91..6d179b0500e4 100644 --- a/test/Api.Test/AdminConsole/Controllers/OrganizationInviteLinksControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationInviteLinksControllerTests.cs @@ -22,12 +22,11 @@ public class OrganizationInviteLinksControllerTests { [Theory, BitAutoData] public async Task Create_WithValidInput_Success( - Guid orgId, OrganizationInviteLink inviteLink, SutProvider sutProvider) { - inviteLink.OrganizationId = orgId; inviteLink.AllowedDomains = "[\"acme.com\"]"; + inviteLink.Code = Guid.NewGuid().ToString(); var model = new CreateOrganizationInviteLinkRequestModel { @@ -40,19 +39,19 @@ public async Task Create_WithValidInput_Success( .CreateAsync(Arg.Any()) .Returns(new CommandResult(inviteLink)); - var result = await sutProvider.Sut.Create(orgId, model); + var result = await sutProvider.Sut.Create(inviteLink.OrganizationId, model); var createdResult = Assert.IsType>(result); - Assert.Equal($"organizations/{orgId}/invite-link", createdResult.Location); + Assert.Equal($"organizations/{inviteLink.OrganizationId}/invite-link", createdResult.Location); Assert.NotNull(createdResult.Value); Assert.Equal(inviteLink.Id, createdResult.Value.Id); - Assert.Equal(inviteLink.Code, createdResult.Value.Code); - Assert.Equal(orgId, createdResult.Value.OrganizationId); + Assert.Equal(Guid.Parse(inviteLink.Code), createdResult.Value.Code); + Assert.Equal(inviteLink.OrganizationId, createdResult.Value.OrganizationId); await sutProvider.GetDependency() .Received(1) .CreateAsync(Arg.Is(r => - r.OrganizationId == orgId && + r.OrganizationId == inviteLink.OrganizationId && r.Invite == "invite-blob")); } @@ -80,23 +79,22 @@ public async Task Create_WithExistingLink_Returns409( [Theory, BitAutoData] public async Task Get_WhenLinkExists_ReturnsOkWithModel( - Guid orgId, OrganizationInviteLink inviteLink, SutProvider sutProvider) { - inviteLink.OrganizationId = orgId; inviteLink.AllowedDomains = "[\"acme.com\"]"; + inviteLink.Code = Guid.NewGuid().ToString(); sutProvider.GetDependency() - .GetAsync(orgId) + .GetAsync(inviteLink.OrganizationId) .Returns(new CommandResult(inviteLink)); - var result = await sutProvider.Sut.Get(orgId); + var result = await sutProvider.Sut.Get(inviteLink.OrganizationId); var okResult = Assert.IsType>(result); Assert.NotNull(okResult.Value); Assert.Equal(inviteLink.Id, okResult.Value.Id); - Assert.Equal(orgId, okResult.Value.OrganizationId); + Assert.Equal(inviteLink.OrganizationId, okResult.Value.OrganizationId); } [Theory, BitAutoData] @@ -153,12 +151,11 @@ public async Task Create_WithValidationError_Returns400( [Theory, BitAutoData] public async Task Update_WithValidInput_ReturnsOk( - Guid orgId, OrganizationInviteLink inviteLink, SutProvider sutProvider) { - inviteLink.OrganizationId = orgId; inviteLink.AllowedDomains = "[\"acme.com\"]"; + inviteLink.Code = Guid.NewGuid().ToString(); var model = new UpdateOrganizationInviteLinkRequestModel { @@ -169,17 +166,17 @@ public async Task Update_WithValidInput_ReturnsOk( .UpdateAsync(Arg.Any()) .Returns(new CommandResult(inviteLink)); - var result = await sutProvider.Sut.Update(orgId, model); + var result = await sutProvider.Sut.Update(inviteLink.OrganizationId, model); var okResult = Assert.IsType>(result); Assert.NotNull(okResult.Value); Assert.Equal(inviteLink.Id, okResult.Value.Id); - Assert.Equal(orgId, okResult.Value.OrganizationId); + Assert.Equal(inviteLink.OrganizationId, okResult.Value.OrganizationId); await sutProvider.GetDependency() .Received(1) .UpdateAsync(Arg.Is(r => - r.OrganizationId == orgId)); + r.OrganizationId == inviteLink.OrganizationId)); } [Theory, BitAutoData] @@ -229,7 +226,7 @@ public async Task GetStatus_WithValidQuery_Success( SutProvider sutProvider) { sutProvider.GetDependency() - .GetStatusAsync(model.Code) + .GetStatusAsync(model.OrganizationId, model.Code) .Returns(new CommandResult(status)); var result = await sutProvider.Sut.GetStatus(model); @@ -247,7 +244,7 @@ public async Task GetStatus_WithNotFoundError_ReturnsNotFound( SutProvider sutProvider) { sutProvider.GetDependency() - .GetStatusAsync(model.Code) + .GetStatusAsync(model.OrganizationId, model.Code) .Returns(new CommandResult(new InviteLinkNotFound())); var result = await sutProvider.Sut.GetStatus(model); @@ -255,6 +252,20 @@ public async Task GetStatus_WithNotFoundError_ReturnsNotFound( Assert.IsType>(result); } + [Theory, BitAutoData] + public async Task GetStatus_WithNotAvailableError_ReturnsBadRequest( + GetOrganizationInviteLinkStatusRequestModel model, + SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetStatusAsync(model.OrganizationId, model.Code) + .Returns(new CommandResult(new InviteLinkNotAvailable())); + + var result = await sutProvider.Sut.GetStatus(model); + + Assert.IsType>(result); + } + [Theory, BitAutoData] public async Task GetPolicies_WithValidInput_ReturnsOkWithList( GetOrganizationInviteLinkPoliciesRequestModel model, @@ -267,7 +278,7 @@ public async Task GetPolicies_WithValidInput_ReturnsOkWithList( } sutProvider.GetDependency() - .GetPoliciesAsync(model.Code) + .GetPoliciesAsync(model.OrganizationId, model.Code) .Returns(new CommandResult>(policies)); var result = await sutProvider.Sut.GetPolicies(model); @@ -283,7 +294,7 @@ public async Task GetPolicies_WithNotFoundError_ReturnsNotFound( SutProvider sutProvider) { sutProvider.GetDependency() - .GetPoliciesAsync(model.Code) + .GetPoliciesAsync(model.OrganizationId, model.Code) .Returns(new CommandResult>(new InviteLinkNotFound())); var result = await sutProvider.Sut.GetPolicies(model); @@ -297,7 +308,7 @@ public async Task GetPolicies_WithNotAvailableError_ReturnsBadRequest( SutProvider sutProvider) { sutProvider.GetDependency() - .GetPoliciesAsync(model.Code) + .GetPoliciesAsync(model.OrganizationId, model.Code) .Returns(new CommandResult>(new InviteLinkNotAvailable())); var result = await sutProvider.Sut.GetPolicies(model); diff --git a/test/Core.Test/AdminConsole/Entities/OrganizationInviteLinkTests.cs b/test/Core.Test/AdminConsole/Entities/OrganizationInviteLinkTests.cs new file mode 100644 index 000000000000..cfe64d3b380c --- /dev/null +++ b/test/Core.Test/AdminConsole/Entities/OrganizationInviteLinkTests.cs @@ -0,0 +1,53 @@ +using Bit.Core.AdminConsole.Entities; +using Xunit; + +namespace Bit.Core.Test.AdminConsole.Entities; + +public class OrganizationInviteLinkTests +{ + [Fact] + public void CodeMatches_SameCode_ReturnsTrue() + { + var code = Guid.NewGuid().ToString(); + var link = new OrganizationInviteLink { Code = code }; + + Assert.True(link.CodeMatches(code)); + } + + [Fact] + public void CodeMatches_DifferentCodes_ReturnsFalse() + { + var link = new OrganizationInviteLink { Code = Guid.NewGuid().ToString() }; + + Assert.False(link.CodeMatches(Guid.NewGuid().ToString())); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void CodeMatches_NullOrEmptyProvidedCode_ReturnsFalse(string? providedCode) + { + var link = new OrganizationInviteLink { Code = Guid.NewGuid().ToString() }; + + Assert.False(link.CodeMatches(providedCode)); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void CodeMatches_NullOrEmptyStoredCode_ReturnsFalse(string? storedCode) + { + var link = new OrganizationInviteLink { Code = storedCode! }; + + Assert.False(link.CodeMatches("some-code")); + } + + [Fact] + public void CodeMatches_CaseSensitive_ReturnsFalse() + { + var code = Guid.NewGuid().ToString(); + var link = new OrganizationInviteLink { Code = code }; + + Assert.False(link.CodeMatches(code.ToUpperInvariant())); + } +} diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/AcceptOrganizationInviteLinkCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/AcceptOrganizationInviteLinkCommandTests.cs index f1d336371cb1..6166ccb7b6ca 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/AcceptOrganizationInviteLinkCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/AcceptOrganizationInviteLinkCommandTests.cs @@ -44,13 +44,17 @@ public async Task AcceptAsync_WithLinkNotFound_ReturnsInviteLinkNotFound( } [Theory, BitAutoData] - public async Task AcceptAsync_WithOrganizationNotFound_ReturnsInviteLinkNotFound( + public async Task AcceptAsync_WithCodeMismatch_ReturnsInviteLinkNotFound( AcceptOrganizationInviteLinkRequest request, OrganizationInviteLink inviteLink, SutProvider sutProvider) { + // Store a different code than what the request presents + inviteLink.OrganizationId = request.OrganizationId; + inviteLink.Code = Guid.NewGuid().ToString(); + sutProvider.GetDependency() - .GetByCodeAsync(inviteLink.Code) + .GetByOrganizationIdAsync(request.OrganizationId) .Returns(inviteLink); var result = await sutProvider.Sut.AcceptAsync(request); @@ -59,24 +63,60 @@ public async Task AcceptAsync_WithOrganizationNotFound_ReturnsInviteLinkNotFound Assert.IsType(result.AsError); } + [Theory, BitAutoData] + public async Task AcceptAsync_WithOrganizationNotFound_ReturnsInviteLinkNotFound( + Organization organization, + OrganizationInviteLink inviteLink, + User user, + SutProvider sutProvider) + { + var code = Guid.NewGuid(); + inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); + + sutProvider.GetDependency() + .GetByOrganizationIdAsync(organization.Id) + .Returns(inviteLink); + + // Organization repo returns null → not found + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = code, + User = user + }; + var result = await sutProvider.Sut.AcceptAsync(request); + + Assert.True(result.IsError); + Assert.IsType(result.AsError); + } + [Theory, BitAutoData] public async Task AcceptAsync_WithOrganizationDisabled_ReturnsInviteLinkNotFound( Organization organization, OrganizationInviteLink inviteLink, - AcceptOrganizationInviteLinkRequest request, + User user, SutProvider sutProvider) { organization.Enabled = false; inviteLink.OrganizationId = organization.Id; + var code = Guid.NewGuid(); + inviteLink.Code = code.ToString(); sutProvider.GetDependency() - .GetByCodeAsync(inviteLink.Code) + .GetByOrganizationIdAsync(organization.Id) .Returns(inviteLink); sutProvider.GetDependency() .GetByIdAsync(organization.Id) .Returns(organization); + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = code, + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -87,21 +127,22 @@ public async Task AcceptAsync_WithOrganizationDisabled_ReturnsInviteLinkNotFound public async Task AcceptAsync_WithOrganizationNotUsingInviteLinks_ReturnsInviteLinkNotAvailable( Organization organization, OrganizationInviteLink inviteLink, - AcceptOrganizationInviteLinkRequest request, + User user, SutProvider sutProvider) { organization.Enabled = true; organization.UseInviteLinks = false; inviteLink.OrganizationId = organization.Id; - sutProvider.GetDependency() - .GetByCodeAsync(request.Code) - .Returns(inviteLink); - - sutProvider.GetDependency() - .GetByIdAsync(organization.Id) - .Returns(organization); + SetupHappyPath(organization, inviteLink, user, sutProvider); + organization.UseInviteLinks = false; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -119,7 +160,12 @@ public async Task AcceptAsync_WithEmailDomainNotAllowed_ReturnsError( inviteLink.AllowedDomains = "[\"allowed.com\"]"; user.Email = "user@notallowed.com"; - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -136,7 +182,12 @@ public async Task AcceptAsync_WithUnverifiedEmail_ReturnsEmailNotVerified( SetupHappyPath(organization, inviteLink, user, sutProvider); user.EmailVerified = false; - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -166,7 +217,12 @@ public async Task AcceptAsync_WithRevokedMember_ReturnsOrganizationAccessRevoked .GetByOrganizationAsync(organization.Id, user.Id) .Returns(revokedOrganizationUser); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -190,7 +246,12 @@ public async Task AcceptAsync_WithRevokedEmailInvite_ReturnsOrganizationAccessRe .GetByOrganizationEmailAsync(organization.Id, user.Email) .Returns(revokedEmailInvite); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -218,7 +279,12 @@ public async Task AcceptAsync_WithAlreadyMember_ReturnsAlreadyOrganizationMember .GetByOrganizationAsync(organization.Id, user.Id) .Returns(existingOrganizationUser); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -243,7 +309,12 @@ public async Task AcceptAsync_WithExistingEmailInvite_UpdatesOrganizationUser( .GetByOrganizationEmailAsync(organization.Id, user.Email) .Returns(invitedOrganizationUser); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsSuccess); @@ -286,7 +357,12 @@ public async Task AcceptAsync_WithFreeAdmin_AdminLimitReached_ReturnsError( .GetCountByFreeOrganizationAdminUserAsync(user.Id) .Returns(1); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -319,7 +395,12 @@ public async Task AcceptAsync_WithFreeAdmin_NotAtLimit_Succeeds( .GetCountByFreeOrganizationAdminUserAsync(user.Id) .Returns(0); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsSuccess); @@ -345,7 +426,12 @@ public async Task AcceptAsync_WithSingleOrgPolicyViolation_ReturnsError( Invalid(new AcceptOrganizationMembershipValidationResult(), new UserIsAMemberOfAnotherOrganization()))); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -372,7 +458,12 @@ public async Task AcceptAsync_WithTwoFactorPolicy_UserLacks2FA_ReturnsError( Invalid(new AcceptOrganizationMembershipValidationResult(), new TwoFactorRequiredForMembership()))); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -407,7 +498,13 @@ public async Task AcceptAsync_WithAutoEnroll_MissingResetPasswordKey_ReturnsErro } ])); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user, ResetPasswordKey = null }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + ResetPasswordKey = null + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -432,7 +529,12 @@ public async Task AcceptAsync_WithNoSeatsAvailable_ReturnsError( .GetOccupiedSeatCountByOrganizationIdAsync(organization.Id) .Returns(new OrganizationSeatCounts { Users = 2, Sponsored = 0 }); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -461,7 +563,12 @@ public async Task AcceptAsync_WithAutoConfirmPolicy_AndNoSeatsAvailable_DoesNotD .GetOccupiedSeatCountByOrganizationIdAsync(organization.Id) .Returns(new OrganizationSeatCounts { Users = 2, Sponsored = 0 }); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -496,7 +603,12 @@ public async Task AcceptAsync_WithAutoAddSeats_BusinessFailure_ReturnsSeatAddFai .AutoAddSeatsAsync(organization, 1) .Throws(businessFailure); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -525,7 +637,12 @@ public async Task AcceptAsync_WithAutoAddSeats_UnhandledException_Propagates( .AutoAddSeatsAsync(organization, 1) .Throws(new InvalidOperationException("stripe outage")); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; await Assert.ThrowsAsync(() => sutProvider.Sut.AcceptAsync(request)); @@ -549,7 +666,12 @@ public async Task AcceptAsync_WithNewMember_CreatesOrganizationUser( .GetManyByMinimumRoleAsync(organization.Id, OrganizationUserType.Admin) .Returns(new List { adminDetails }); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsSuccess); @@ -613,7 +735,8 @@ public async Task AcceptAsync_WithAutoEnroll_AndValidKey_EnrollsUser( var resetPasswordKey = "valid-key-123"; var request = new AcceptOrganizationInviteLinkRequest { - Code = inviteLink.Code, + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), User = user, ResetPasswordKey = resetPasswordKey }; @@ -635,7 +758,12 @@ public async Task AcceptAsync_WithNoAutoEnroll_DoesNotEnrollUser( { SetupHappyPath(organization, inviteLink, user, sutProvider); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsSuccess); @@ -655,7 +783,12 @@ public async Task AcceptAsync_WithAutoConfirmPolicy_Enabled_DeletesEmergencyAcce SetupHappyPath(organization, inviteLink, user, sutProvider); SetupAutoConfirmPolicy(organization, user, sutProvider); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsSuccess); @@ -673,7 +806,12 @@ public async Task AcceptAsync_WithAutoConfirmPolicy_Disabled_DoesNotDeleteEmerge { SetupHappyPath(organization, inviteLink, user, sutProvider); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsSuccess); @@ -696,7 +834,12 @@ public async Task AcceptAsync_WithProviderUser_ReturnsProviderUsersCannotAcceptI .GetManyByUserAsync(user.Id) .Returns([providerUser]); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -724,7 +867,12 @@ public async Task AcceptAsync_WithAutoConfirmPolicy_AndMultiOrgUser_ReturnsError Invalid(new AcceptOrganizationMembershipValidationResult(), new UserCannotBelongToAnotherOrganization()))); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; var result = await sutProvider.Sut.AcceptAsync(request); Assert.True(result.IsError); @@ -751,7 +899,12 @@ public async Task AcceptAsync_WithAutoConfirmPolicy_EaDeleteThrows_ThrowsWithout .DeleteAllByUserIdAsync(user.Id) .Throws(new InvalidOperationException("db failure")); - var request = new AcceptOrganizationInviteLinkRequest { Code = inviteLink.Code, User = user }; + var request = new AcceptOrganizationInviteLinkRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user + }; await Assert.ThrowsAsync(() => sutProvider.Sut.AcceptAsync(request)); @@ -779,8 +932,9 @@ private static void SetupAutoConfirmPolicy( } /// - /// Configures the default "happy path" mocks so individual tests only need - /// to override the one thing they are testing. + /// Configures the default "happy path" mocks. Sets to + /// a fresh Guid string so tests can derive request.Code = Guid.Parse(inviteLink.Code) and + /// have succeed. /// private static void SetupHappyPath( Organization org, @@ -793,12 +947,13 @@ private static void SetupHappyPath( org.Seats = 10; org.MaxAutoscaleSeats = null; link.OrganizationId = org.Id; + link.Code = Guid.NewGuid().ToString(); link.AllowedDomains = "[\"example.com\"]"; user.Email = "user@example.com"; user.EmailVerified = true; sutProvider.GetDependency() - .GetByCodeAsync(link.Code) + .GetByOrganizationIdAsync(org.Id) .Returns(link); sutProvider.GetDependency() diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkCommandTests.cs index a7ce1c83a2a0..ae51dd52c44d 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkCommandTests.cs @@ -312,7 +312,8 @@ await sutProvider.GetDependency() private static ConfirmOrganizationInviteLinkRequest BuildRequest(OrganizationInviteLink inviteLink, User user) => new() { - Code = inviteLink.Code, + OrganizationId = inviteLink.OrganizationId, + Code = Guid.Parse(inviteLink.Code), User = user, OrgUserKey = "4.orgUserKey", DefaultUserCollectionName = "2.defaultCollectionName", @@ -326,6 +327,7 @@ private static void SetupHappyPath( SutProvider sutProvider) { inviteLink.OrganizationId = organization.Id; + inviteLink.Code = Guid.NewGuid().ToString(); if (existingOrganizationUser is not null) { existingOrganizationUser.OrganizationId = organization.Id; diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkValidatorTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkValidatorTests.cs index 5cd845693d4e..9d6447f1ee65 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkValidatorTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/ConfirmOrganizationInviteLinkValidatorTests.cs @@ -36,6 +36,35 @@ public async Task ValidateAsync_WithLinkNotFound_ReturnsInviteLinkNotFound( Assert.IsType(result.AsError); } + [Theory, BitAutoData] + public async Task ValidateAsync_WithCodeMismatch_ReturnsInviteLinkNotFound( + Organization organization, + OrganizationInviteLink inviteLink, + User user, + SutProvider sutProvider) + { + // Arrange + inviteLink.OrganizationId = organization.Id; + inviteLink.Code = Guid.NewGuid().ToString(); + + sutProvider.GetDependency() + .GetByOrganizationIdAsync(organization.Id) + .Returns(inviteLink); + + // Act — pass a different code than the one stored on the link + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.NewGuid(), + User = user, + }; + var result = await sutProvider.Sut.ValidateAsync(request); + + // Assert + Assert.True(result.IsError); + Assert.IsType(result.AsError); + } + [Theory, BitAutoData] public async Task ValidateAsync_WithOrganizationNotFound_ReturnsInviteLinkNotFound( OrganizationInviteLink inviteLink, @@ -43,12 +72,19 @@ public async Task ValidateAsync_WithOrganizationNotFound_ReturnsInviteLinkNotFou SutProvider sutProvider) { // Arrange + inviteLink.Code = Guid.NewGuid().ToString(); + sutProvider.GetDependency() - .GetByCodeAsync(inviteLink.Code) + .GetByOrganizationIdAsync(inviteLink.OrganizationId) .Returns(inviteLink); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = inviteLink.OrganizationId, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -66,9 +102,10 @@ public async Task ValidateAsync_WithOrganizationDisabled_ReturnsInviteLinkNotFou // Arrange organization.Enabled = false; inviteLink.OrganizationId = organization.Id; + inviteLink.Code = Guid.NewGuid().ToString(); sutProvider.GetDependency() - .GetByCodeAsync(inviteLink.Code) + .GetByOrganizationIdAsync(organization.Id) .Returns(inviteLink); sutProvider.GetDependency() @@ -76,7 +113,12 @@ public async Task ValidateAsync_WithOrganizationDisabled_ReturnsInviteLinkNotFou .Returns(organization); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -95,9 +137,10 @@ public async Task ValidateAsync_WithOrganizationNotUsingInviteLinks_ReturnsInvit organization.Enabled = true; organization.UseInviteLinks = false; inviteLink.OrganizationId = organization.Id; + inviteLink.Code = Guid.NewGuid().ToString(); sutProvider.GetDependency() - .GetByCodeAsync(inviteLink.Code) + .GetByOrganizationIdAsync(organization.Id) .Returns(inviteLink); sutProvider.GetDependency() @@ -105,7 +148,12 @@ public async Task ValidateAsync_WithOrganizationNotUsingInviteLinks_ReturnsInvit .Returns(organization); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -126,7 +174,12 @@ public async Task ValidateAsync_WithEmailDomainNotAllowed_ReturnsEmailDomainNotA user.Email = "user@notallowed.example.com"; // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -150,7 +203,12 @@ public async Task ValidateAsync_WithProviderUser_ReturnsProviderUsersCannotAccep .Returns([providerUser]); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -175,7 +233,12 @@ public async Task ValidateAsync_WithRevokedMember_ReturnsOrganizationAccessRevok .Returns(revokedOrganizationUser); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -202,7 +265,12 @@ public async Task ValidateAsync_WithRevokedEmailInvite_ReturnsOrganizationAccess .Returns(revokedEmailInvite); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -228,7 +296,12 @@ public async Task ValidateAsync_WithConfirmedMember_ReturnsAlreadyOrganizationMe .Returns(existingOrganizationUser); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -257,7 +330,12 @@ public async Task ValidateAsync_WithUnconfirmedExistingMember_IsAllowed( .Returns(existingOrganizationUser); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -287,7 +365,12 @@ public async Task ValidateAsync_WithNewUserAndNoSeatsAvailable_ReturnsOrganizati .Returns(new OrganizationSeatCounts { Users = 4, Sponsored = 0 }); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -317,7 +400,12 @@ public async Task ValidateAsync_WithSingleOrganizationPolicyViolation_ReturnsErr .Returns([membershipInAnotherOrganization]); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -351,7 +439,12 @@ public async Task ValidateAsync_WhenMemberOfAnotherOrganizationWithSingleOrgPoli ])); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -381,7 +474,12 @@ public async Task ValidateAsync_WithTwoFactorRequiredAndUserLacks2FA_ReturnsErro .Returns(false); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -409,7 +507,12 @@ public async Task ValidateAsync_WithTwoFactorRequiredAndUserHas2FA_IsAllowed( .Returns(true); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -427,7 +530,12 @@ public async Task ValidateAsync_WithNewUser_ReturnsValidatedContext( SetupHappyPath(organization, inviteLink, user, sutProvider); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -457,7 +565,12 @@ public async Task ValidateAsync_WithExistingInvitedUser_SkipsSeatCheckAndReturns .Returns(invitedOrganizationUser); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -494,7 +607,12 @@ public async Task ValidateAsync_WithFreeAdminMembership_AdminLimitReached_Return .Returns(1); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -526,7 +644,12 @@ public async Task ValidateAsync_WithFreeAdminMembership_NotAtLimit_IsAllowed( .Returns(0); // Act - var request = new ConfirmOrganizationInviteLinkValidationRequest { Code = inviteLink.Code, User = user }; + var request = new ConfirmOrganizationInviteLinkValidationRequest + { + OrganizationId = organization.Id, + Code = Guid.Parse(inviteLink.Code), + User = user, + }; var result = await sutProvider.Sut.ValidateAsync(request); // Assert @@ -546,11 +669,12 @@ private static void SetupHappyPath( org.MaxAutoscaleSeats = null; org.PlanType = PlanType.EnterpriseAnnually; link.OrganizationId = org.Id; + link.Code = Guid.NewGuid().ToString(); link.AllowedDomains = "[\"example.com\"]"; user.Email = "user@example.com"; sutProvider.GetDependency() - .GetByCodeAsync(link.Code) + .GetByOrganizationIdAsync(org.Id) .Returns(link); sutProvider.GetDependency() diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/CreateOrganizationInviteLinkCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/CreateOrganizationInviteLinkCommandTests.cs index 03f5d13f72e0..cf11d75abf5d 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/CreateOrganizationInviteLinkCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/CreateOrganizationInviteLinkCommandTests.cs @@ -45,7 +45,7 @@ public async Task CreateAsync_WithValidInput_Success( var link = result.AsSuccess; Assert.Equal(organization.Id, link.OrganizationId); Assert.NotEqual(Guid.Empty, link.Id); - Assert.NotEqual(Guid.Empty, link.Code); + Assert.True(Guid.TryParse(link.Code, out var parsedCode) && parsedCode != Guid.Empty); Assert.Equal(request.Invite, link.Invite); Assert.Equal(request.SupportsConfirmation, link.SupportsConfirmation); diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkPoliciesQueryTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkPoliciesQueryTests.cs index 2733cb1b2764..50e3ff15b6fc 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkPoliciesQueryTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkPoliciesQueryTests.cs @@ -16,13 +16,14 @@ public class GetOrganizationInviteLinkPoliciesQueryTests { [Theory, BitAutoData] public async Task GetPoliciesAsync_WithValidInput_ReturnsMasterPasswordAndResetPasswordOnly( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.UsePolicies = true; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); List policies = [ @@ -32,12 +33,12 @@ public async Task GetPoliciesAsync_WithValidInput_ReturnsMasterPasswordAndResetP new Policy { Type = PolicyType.SingleOrg, Enabled = true }, ]; - SetupMocks(sutProvider, code, inviteLink, organization); + SetupMocks(sutProvider, inviteLink, organization); sutProvider.GetDependency() - .GetManyByOrganizationIdAsync(organization.Id) + .GetManyByOrganizationIdAsync(inviteLink.OrganizationId) .Returns(policies); - var result = await sutProvider.Sut.GetPoliciesAsync(code); + var result = await sutProvider.Sut.GetPoliciesAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); var returned = result.AsSuccess.ToList(); @@ -48,13 +49,28 @@ public async Task GetPoliciesAsync_WithValidInput_ReturnsMasterPasswordAndResetP [Theory, BitAutoData] public async Task GetPoliciesAsync_InviteLinkNotFound_ReturnsNotFoundError( + Guid organizationId, Guid code, SutProvider sutProvider) { sutProvider.GetDependency() - .GetByCodeAsync(code).ReturnsNull(); + .GetByOrganizationIdAsync(organizationId).ReturnsNull(); + + var result = await sutProvider.Sut.GetPoliciesAsync(organizationId, code); + + Assert.True(result.IsError); + Assert.IsType(result.AsError); + } + + [Theory, BitAutoData] + public async Task GetPoliciesAsync_CodeMismatch_ReturnsNotFoundError( + OrganizationInviteLink inviteLink, + SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetByOrganizationIdAsync(inviteLink.OrganizationId).Returns(inviteLink); - var result = await sutProvider.Sut.GetPoliciesAsync(code); + var result = await sutProvider.Sut.GetPoliciesAsync(inviteLink.OrganizationId, Guid.NewGuid()); Assert.True(result.IsError); Assert.IsType(result.AsError); @@ -62,16 +78,18 @@ public async Task GetPoliciesAsync_InviteLinkNotFound_ReturnsNotFoundError( [Theory, BitAutoData] public async Task GetPoliciesAsync_OrganizationNotFound_ReturnsNotFoundError( - Guid code, OrganizationInviteLink inviteLink, SutProvider sutProvider) { + var code = Guid.NewGuid(); + inviteLink.Code = code.ToString(); + sutProvider.GetDependency() - .GetByCodeAsync(code).Returns(inviteLink); + .GetByOrganizationIdAsync(inviteLink.OrganizationId).Returns(inviteLink); sutProvider.GetDependency() .GetByIdAsync(inviteLink.OrganizationId).ReturnsNull(); - var result = await sutProvider.Sut.GetPoliciesAsync(code); + var result = await sutProvider.Sut.GetPoliciesAsync(inviteLink.OrganizationId, code); Assert.True(result.IsError); Assert.IsType(result.AsError); @@ -79,20 +97,21 @@ public async Task GetPoliciesAsync_OrganizationNotFound_ReturnsNotFoundError( [Theory, BitAutoData] public async Task GetPoliciesAsync_OrganizationDisabled_ReturnsNotFoundError( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Enabled = false; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); sutProvider.GetDependency() - .GetByCodeAsync(code).Returns(inviteLink); + .GetByOrganizationIdAsync(inviteLink.OrganizationId).Returns(inviteLink); sutProvider.GetDependency() - .GetByIdAsync(organization.Id).Returns(organization); + .GetByIdAsync(inviteLink.OrganizationId).Returns(organization); - var result = await sutProvider.Sut.GetPoliciesAsync(code); + var result = await sutProvider.Sut.GetPoliciesAsync(inviteLink.OrganizationId, code); Assert.True(result.IsError); Assert.IsType(result.AsError); @@ -100,17 +119,18 @@ public async Task GetPoliciesAsync_OrganizationDisabled_ReturnsNotFoundError( [Theory, BitAutoData] public async Task GetPoliciesAsync_UseInviteLinksFalse_ReturnsNotAvailableError( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SutProvider sutProvider) { - inviteLink.OrganizationId = organization.Id; + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); + SetupMocks(sutProvider, inviteLink, organization); organization.UseInviteLinks = false; - var result = await sutProvider.Sut.GetPoliciesAsync(code); + var result = await sutProvider.Sut.GetPoliciesAsync(inviteLink.OrganizationId, code); Assert.True(result.IsError); Assert.IsType(result.AsError); @@ -118,17 +138,18 @@ public async Task GetPoliciesAsync_UseInviteLinksFalse_ReturnsNotAvailableError( [Theory, BitAutoData] public async Task GetPoliciesAsync_UsePoliciesFalse_ReturnsNotFoundError( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SutProvider sutProvider) { - inviteLink.OrganizationId = organization.Id; + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); + SetupMocks(sutProvider, inviteLink, organization); organization.UsePolicies = false; - var result = await sutProvider.Sut.GetPoliciesAsync(code); + var result = await sutProvider.Sut.GetPoliciesAsync(inviteLink.OrganizationId, code); Assert.True(result.IsError); Assert.IsType(result.AsError); @@ -140,7 +161,6 @@ await sutProvider.GetDependency() private static void SetupMocks( SutProvider sutProvider, - Guid code, OrganizationInviteLink inviteLink, Organization organization) { @@ -148,7 +168,7 @@ private static void SetupMocks( organization.UseInviteLinks = true; organization.UsePolicies = true; sutProvider.GetDependency() - .GetByCodeAsync(code) + .GetByOrganizationIdAsync(inviteLink.OrganizationId) .Returns(inviteLink); sutProvider.GetDependency() .GetByIdAsync(organization.Id) diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkStatusQueryTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkStatusQueryTests.cs index 6f7b87182643..2373ea9288a8 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkStatusQueryTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/GetOrganizationInviteLinkStatusQueryTests.cs @@ -19,18 +19,19 @@ public class GetOrganizationInviteLinkStatusQueryTests { [Theory, BitAutoData] public async Task GetStatusAsync_WithValidInput_Success( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Seats = null; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); - SetupOccupiedSeats(sutProvider, organization.Id, 0); + SetupMocks(sutProvider, inviteLink, organization); + SetupOccupiedSeats(sutProvider, inviteLink.OrganizationId, 0); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); var status = result.AsSuccess; @@ -66,13 +67,28 @@ public async Task GetStatusAsync_SupportsConfirmation( [Theory, BitAutoData] public async Task GetStatusAsync_InviteLinkNotFound_ReturnsNotFoundError( + Guid organizationId, Guid code, SutProvider sutProvider) { sutProvider.GetDependency() - .GetByCodeAsync(code).ReturnsNull(); + .GetByOrganizationIdAsync(organizationId).ReturnsNull(); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(organizationId, code); + + Assert.True(result.IsError); + Assert.IsType(result.AsError); + } + + [Theory, BitAutoData] + public async Task GetStatusAsync_CodeMismatch_ReturnsNotFoundError( + OrganizationInviteLink inviteLink, + SutProvider sutProvider) + { + sutProvider.GetDependency() + .GetByOrganizationIdAsync(inviteLink.OrganizationId).Returns(inviteLink); + + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, Guid.NewGuid()); Assert.True(result.IsError); Assert.IsType(result.AsError); @@ -80,16 +96,18 @@ public async Task GetStatusAsync_InviteLinkNotFound_ReturnsNotFoundError( [Theory, BitAutoData] public async Task GetStatusAsync_OrganizationNotFound_ReturnsNotFoundError( - Guid code, OrganizationInviteLink inviteLink, SutProvider sutProvider) { + var code = Guid.NewGuid(); + inviteLink.Code = code.ToString(); + sutProvider.GetDependency() - .GetByCodeAsync(code).Returns(inviteLink); + .GetByOrganizationIdAsync(inviteLink.OrganizationId).Returns(inviteLink); sutProvider.GetDependency() .GetByIdAsync(inviteLink.OrganizationId).ReturnsNull(); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsError); Assert.IsType(result.AsError); @@ -97,20 +115,21 @@ public async Task GetStatusAsync_OrganizationNotFound_ReturnsNotFoundError( [Theory, BitAutoData] public async Task GetStatusAsync_OrganizationDisabled_ReturnsNotFoundError( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Enabled = false; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); sutProvider.GetDependency() - .GetByCodeAsync(code).Returns(inviteLink); + .GetByOrganizationIdAsync(inviteLink.OrganizationId).Returns(inviteLink); sutProvider.GetDependency() - .GetByIdAsync(organization.Id).Returns(organization); + .GetByIdAsync(inviteLink.OrganizationId).Returns(organization); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsError); Assert.IsType(result.AsError); @@ -118,17 +137,18 @@ public async Task GetStatusAsync_OrganizationDisabled_ReturnsNotFoundError( [Theory, BitAutoData] public async Task GetStatusAsync_UseInviteLinksFalse_ReturnsLinksDisabled( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SutProvider sutProvider) { - inviteLink.OrganizationId = organization.Id; + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); + SetupMocks(sutProvider, inviteLink, organization); organization.UseInviteLinks = false; - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); var status = result.AsSuccess; @@ -145,18 +165,19 @@ await sutProvider.GetDependency() [Theory, BitAutoData] public async Task GetStatusAsync_NoSeatLimit_ReturnsSeatsAvailableTrue( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Seats = null; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); - SetupOccupiedSeats(sutProvider, organization.Id, 50); + SetupMocks(sutProvider, inviteLink, organization); + SetupOccupiedSeats(sutProvider, inviteLink.OrganizationId, 50); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); Assert.True(result.AsSuccess.SeatsAvailable); @@ -164,19 +185,20 @@ public async Task GetStatusAsync_NoSeatLimit_ReturnsSeatsAvailableTrue( [Theory, BitAutoData] public async Task GetStatusAsync_UnusedSeats_ReturnsSeatsAvailableTrue( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Seats = 10; organization.MaxAutoscaleSeats = null; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); - SetupOccupiedSeats(sutProvider, organization.Id, 5); + SetupMocks(sutProvider, inviteLink, organization); + SetupOccupiedSeats(sutProvider, inviteLink.OrganizationId, 5); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); Assert.True(result.AsSuccess.SeatsAvailable); @@ -184,19 +206,20 @@ public async Task GetStatusAsync_UnusedSeats_ReturnsSeatsAvailableTrue( [Theory, BitAutoData] public async Task GetStatusAsync_AutoscaleHeadroom_ReturnsSeatsAvailableTrue( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Seats = 10; organization.MaxAutoscaleSeats = 20; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); - SetupOccupiedSeats(sutProvider, organization.Id, 10); + SetupMocks(sutProvider, inviteLink, organization); + SetupOccupiedSeats(sutProvider, inviteLink.OrganizationId, 10); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); Assert.True(result.AsSuccess.SeatsAvailable); @@ -204,19 +227,20 @@ public async Task GetStatusAsync_AutoscaleHeadroom_ReturnsSeatsAvailableTrue( [Theory, BitAutoData] public async Task GetStatusAsync_NoSeatsAndNoHeadroom_ReturnsSeatsAvailableFalseAndSsoNull( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Seats = 10; organization.MaxAutoscaleSeats = 10; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); - SetupOccupiedSeats(sutProvider, organization.Id, 10); + SetupMocks(sutProvider, inviteLink, organization); + SetupOccupiedSeats(sutProvider, inviteLink.OrganizationId, 10); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); Assert.False(result.AsSuccess.SeatsAvailable); @@ -224,25 +248,26 @@ public async Task GetStatusAsync_NoSeatsAndNoHeadroom_ReturnsSeatsAvailableFalse [Theory, BitAutoData] public async Task GetStatusAsync_SeatsUnavailable_ReturnsSsoNullWithoutCheckingSsoConfig( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SsoConfig ssoConfig, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Seats = 10; organization.MaxAutoscaleSeats = 10; organization.Identifier = "my-org"; organization.UseSso = true; ssoConfig.Enabled = true; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); - SetupOccupiedSeats(sutProvider, organization.Id, 10); + SetupMocks(sutProvider, inviteLink, organization); + SetupOccupiedSeats(sutProvider, inviteLink.OrganizationId, 10); sutProvider.GetDependency() - .GetByOrganizationIdAsync(organization.Id).Returns(ssoConfig); + .GetByOrganizationIdAsync(inviteLink.OrganizationId).Returns(ssoConfig); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); Assert.Null(result.AsSuccess.Sso); @@ -253,20 +278,20 @@ await sutProvider.GetDependency() [Theory, BitAutoData] public async Task GetStatusAsync_OccupancyAtAutoscaleCeiling_ReturnsSeatsAvailableFalse( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SutProvider sutProvider) { - // Occupancy has autoscaled past the base seats up to the ceiling. + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Seats = 5; organization.MaxAutoscaleSeats = 10; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); - SetupOccupiedSeats(sutProvider, organization.Id, 10); + SetupMocks(sutProvider, inviteLink, organization); + SetupOccupiedSeats(sutProvider, inviteLink.OrganizationId, 10); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); Assert.False(result.AsSuccess.SeatsAvailable); @@ -274,19 +299,20 @@ public async Task GetStatusAsync_OccupancyAtAutoscaleCeiling_ReturnsSeatsAvailab [Theory, BitAutoData] public async Task GetStatusAsync_NoAutoscaleCap_ReturnsSeatsAvailableTrue( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Seats = 10; organization.MaxAutoscaleSeats = null; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); - SetupOccupiedSeats(sutProvider, organization.Id, 10); + SetupMocks(sutProvider, inviteLink, organization); + SetupOccupiedSeats(sutProvider, inviteLink.OrganizationId, 10); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); Assert.True(result.AsSuccess.SeatsAvailable); @@ -294,20 +320,21 @@ public async Task GetStatusAsync_NoAutoscaleCap_ReturnsSeatsAvailableTrue( [Theory, BitAutoData] public async Task GetStatusAsync_NoSsoConfig_ReturnsSsoNull( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Seats = null; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); - SetupOccupiedSeats(sutProvider, organization.Id, 0); + SetupMocks(sutProvider, inviteLink, organization); + SetupOccupiedSeats(sutProvider, inviteLink.OrganizationId, 0); sutProvider.GetDependency() - .GetByOrganizationIdAsync(organization.Id).ReturnsNull(); + .GetByOrganizationIdAsync(inviteLink.OrganizationId).ReturnsNull(); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); Assert.Null(result.AsSuccess.Sso); @@ -315,22 +342,23 @@ public async Task GetStatusAsync_NoSsoConfig_ReturnsSsoNull( [Theory, BitAutoData] public async Task GetStatusAsync_SsoConfigDisabled_ReturnsSsoNull( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SsoConfig ssoConfig, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Seats = null; ssoConfig.Enabled = false; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); - SetupOccupiedSeats(sutProvider, organization.Id, 0); + SetupMocks(sutProvider, inviteLink, organization); + SetupOccupiedSeats(sutProvider, inviteLink.OrganizationId, 0); sutProvider.GetDependency() - .GetByOrganizationIdAsync(organization.Id).Returns(ssoConfig); + .GetByOrganizationIdAsync(inviteLink.OrganizationId).Returns(ssoConfig); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); Assert.Null(result.AsSuccess.Sso); @@ -338,23 +366,24 @@ public async Task GetStatusAsync_SsoConfigDisabled_ReturnsSsoNull( [Theory, BitAutoData] public async Task GetStatusAsync_NoOrganizationIdentifier_ReturnsSsoNull( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SsoConfig ssoConfig, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Seats = null; organization.Identifier = null; ssoConfig.Enabled = true; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); - SetupOccupiedSeats(sutProvider, organization.Id, 0); + SetupMocks(sutProvider, inviteLink, organization); + SetupOccupiedSeats(sutProvider, inviteLink.OrganizationId, 0); sutProvider.GetDependency() - .GetByOrganizationIdAsync(organization.Id).Returns(ssoConfig); + .GetByOrganizationIdAsync(inviteLink.OrganizationId).Returns(ssoConfig); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); Assert.Null(result.AsSuccess.Sso); @@ -362,29 +391,30 @@ public async Task GetStatusAsync_NoOrganizationIdentifier_ReturnsSsoNull( [Theory, BitAutoData] public async Task GetStatusAsync_WithSsoConfiguredAndRequireSsoPolicyEnabled_ReturnsSsoRequiredTrue( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SsoConfig ssoConfig, Policy requireSsoPolicy, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Seats = null; organization.Identifier = "my-org"; organization.UseSso = true; organization.UsePolicies = true; ssoConfig.Enabled = true; requireSsoPolicy.Enabled = true; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); - SetupOccupiedSeats(sutProvider, organization.Id, 0); + SetupMocks(sutProvider, inviteLink, organization); + SetupOccupiedSeats(sutProvider, inviteLink.OrganizationId, 0); sutProvider.GetDependency() - .GetByOrganizationIdAsync(organization.Id).Returns(ssoConfig); + .GetByOrganizationIdAsync(inviteLink.OrganizationId).Returns(ssoConfig); sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(organization.Id, PolicyType.RequireSso).Returns(requireSsoPolicy); + .GetByOrganizationIdTypeAsync(inviteLink.OrganizationId, PolicyType.RequireSso).Returns(requireSsoPolicy); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); Assert.NotNull(result.AsSuccess.Sso); @@ -394,26 +424,27 @@ public async Task GetStatusAsync_WithSsoConfiguredAndRequireSsoPolicyEnabled_Ret [Theory, BitAutoData] public async Task GetStatusAsync_WithSsoConfiguredAndNoRequireSsoPolicy_ReturnsSsoRequiredFalse( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SsoConfig ssoConfig, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Seats = null; organization.Identifier = "my-org"; organization.UseSso = true; ssoConfig.Enabled = true; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); - SetupOccupiedSeats(sutProvider, organization.Id, 0); + SetupMocks(sutProvider, inviteLink, organization); + SetupOccupiedSeats(sutProvider, inviteLink.OrganizationId, 0); sutProvider.GetDependency() - .GetByOrganizationIdAsync(organization.Id).Returns(ssoConfig); + .GetByOrganizationIdAsync(inviteLink.OrganizationId).Returns(ssoConfig); sutProvider.GetDependency() - .GetByOrganizationIdTypeAsync(organization.Id, PolicyType.RequireSso).ReturnsNull(); + .GetByOrganizationIdTypeAsync(inviteLink.OrganizationId, PolicyType.RequireSso).ReturnsNull(); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); Assert.NotNull(result.AsSuccess.Sso); @@ -423,21 +454,22 @@ public async Task GetStatusAsync_WithSsoConfiguredAndNoRequireSsoPolicy_ReturnsS [Theory, BitAutoData] public async Task GetStatusAsync_WithUseSsoFalse_ReturnsSsoNull( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SsoConfig ssoConfig, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Seats = null; organization.UseSso = false; ssoConfig.Enabled = true; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); - SetupOccupiedSeats(sutProvider, organization.Id, 0); + SetupMocks(sutProvider, inviteLink, organization); + SetupOccupiedSeats(sutProvider, inviteLink.OrganizationId, 0); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); Assert.Null(result.AsSuccess.Sso); @@ -448,27 +480,28 @@ await sutProvider.GetDependency() [Theory, BitAutoData] public async Task GetStatusAsync_WithSsoEnabledAndUsePoliciesFalse_ReturnsSsoRequiredFalse( - Guid code, OrganizationInviteLink inviteLink, Organization organization, SsoConfig ssoConfig, Policy requireSsoPolicy, SutProvider sutProvider) { + var code = Guid.NewGuid(); + organization.Id = inviteLink.OrganizationId; organization.Seats = null; organization.Identifier = "my-org"; organization.UseSso = true; organization.UsePolicies = false; ssoConfig.Enabled = true; requireSsoPolicy.Enabled = true; - inviteLink.OrganizationId = organization.Id; + inviteLink.Code = code.ToString(); - SetupMocks(sutProvider, code, inviteLink, organization); - SetupOccupiedSeats(sutProvider, organization.Id, 0); + SetupMocks(sutProvider, inviteLink, organization); + SetupOccupiedSeats(sutProvider, inviteLink.OrganizationId, 0); sutProvider.GetDependency() - .GetByOrganizationIdAsync(organization.Id).Returns(ssoConfig); + .GetByOrganizationIdAsync(inviteLink.OrganizationId).Returns(ssoConfig); - var result = await sutProvider.Sut.GetStatusAsync(code); + var result = await sutProvider.Sut.GetStatusAsync(inviteLink.OrganizationId, code); Assert.True(result.IsSuccess); Assert.NotNull(result.AsSuccess.Sso); @@ -480,14 +513,13 @@ await sutProvider.GetDependency() private static void SetupMocks( SutProvider sutProvider, - Guid code, OrganizationInviteLink inviteLink, Organization organization) { organization.Enabled = true; organization.UseInviteLinks = true; sutProvider.GetDependency() - .GetByCodeAsync(code) + .GetByOrganizationIdAsync(inviteLink.OrganizationId) .Returns(inviteLink); sutProvider.GetDependency() .GetByIdAsync(organization.Id) diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/UpdateOrganizationInviteLinkCommandTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/UpdateOrganizationInviteLinkCommandTests.cs index 05ebcfbe3396..9b9c8571906e 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/UpdateOrganizationInviteLinkCommandTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/UpdateOrganizationInviteLinkCommandTests.cs @@ -21,10 +21,15 @@ public class UpdateOrganizationInviteLinkCommandTests [Theory, BitAutoData] public async Task UpdateAsync_WithValidInput_Success( Organization organization, + OrganizationInviteLink existingLink, SutProvider sutProvider) { organization.Enabled = true; organization.UseEvents = true; + existingLink.OrganizationId = organization.Id; + existingLink.CreationDate = DateTime.UtcNow.AddDays(-5); + existingLink.RevisionDate = existingLink.CreationDate; + existingLink.SetAllowedDomains(["old.com"]); SetupAbility(sutProvider, organization.Id); @@ -32,19 +37,6 @@ public async Task UpdateAsync_WithValidInput_Success( .GetByIdAsync(organization.Id) .Returns(organization); - var originalCreationDate = DateTime.UtcNow.AddDays(-5); - var existingLink = new OrganizationInviteLink - { - Id = Guid.NewGuid(), - Code = Guid.NewGuid(), - OrganizationId = organization.Id, - Invite = "invite-blob", - SupportsConfirmation = true, - CreationDate = originalCreationDate, - RevisionDate = originalCreationDate, - }; - existingLink.SetAllowedDomains(["old.com"]); - sutProvider.GetDependency() .GetByOrganizationIdAsync(organization.Id) .Returns(existingLink); @@ -58,10 +50,10 @@ public async Task UpdateAsync_WithValidInput_Success( Assert.Same(existingLink, link); Assert.Equal(existingLink.Id, link.Id); Assert.Equal(existingLink.Code, link.Code); - Assert.Equal("invite-blob", link.Invite); - Assert.True(link.SupportsConfirmation); - Assert.Equal(originalCreationDate, link.CreationDate); - Assert.True(link.RevisionDate > originalCreationDate); + Assert.Equal(existingLink.Invite, link.Invite); + Assert.Equal(existingLink.SupportsConfirmation, link.SupportsConfirmation); + Assert.Equal(existingLink.CreationDate, link.CreationDate); + Assert.True(link.RevisionDate > existingLink.CreationDate); var deserializedDomains = JsonSerializer.Deserialize>(link.AllowedDomains); Assert.NotNull(deserializedDomains); diff --git a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/ValidateOrganizationInviteLinkEmailDomainQueryTests.cs b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/ValidateOrganizationInviteLinkEmailDomainQueryTests.cs index 2745938259ff..d80c7f2cdde7 100644 --- a/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/ValidateOrganizationInviteLinkEmailDomainQueryTests.cs +++ b/test/Core.Test/AdminConsole/OrganizationFeatures/InviteLinks/ValidateOrganizationInviteLinkEmailDomainQueryTests.cs @@ -13,15 +13,33 @@ public class ValidateOrganizationInviteLinkEmailDomainQueryTests { [Theory, BitAutoData] public async Task ValidateAsync_WhenLinkNotFound_ReturnsNotFoundError( + Guid organizationId, Guid code, string email, SutProvider sutProvider) { sutProvider.GetDependency() - .GetByCodeAsync(code) + .GetByOrganizationIdAsync(organizationId) .Returns((OrganizationInviteLink?)null); - var result = await sutProvider.Sut.ValidateAsync(code, email); + var result = await sutProvider.Sut.ValidateAsync(organizationId, code, email); + + Assert.True(result.IsError); + Assert.IsType(result.AsError); + } + + [Theory, BitAutoData] + public async Task ValidateAsync_WhenCodeMismatch_ReturnsNotFoundError( + OrganizationInviteLink link, + SutProvider sutProvider) + { + link.SetAllowedDomains(["acme.com"]); + + sutProvider.GetDependency() + .GetByOrganizationIdAsync(link.OrganizationId) + .Returns(link); + + var result = await sutProvider.Sut.ValidateAsync(link.OrganizationId, Guid.NewGuid(), "user@acme.com"); Assert.True(result.IsError); Assert.IsType(result.AsError); @@ -29,17 +47,18 @@ public async Task ValidateAsync_WhenLinkNotFound_ReturnsNotFoundError( [Theory, BitAutoData] public async Task ValidateAsync_WhenEmailDomainMatches_ReturnsTrue( - Guid code, + OrganizationInviteLink link, SutProvider sutProvider) { - var link = new OrganizationInviteLink(); + var code = Guid.NewGuid(); + link.Code = code.ToString(); link.SetAllowedDomains(["acme.com"]); sutProvider.GetDependency() - .GetByCodeAsync(code) + .GetByOrganizationIdAsync(link.OrganizationId) .Returns(link); - var result = await sutProvider.Sut.ValidateAsync(code, "user@acme.com"); + var result = await sutProvider.Sut.ValidateAsync(link.OrganizationId, code, "user@acme.com"); Assert.True(result.IsSuccess); Assert.True(result.AsSuccess); @@ -47,17 +66,18 @@ public async Task ValidateAsync_WhenEmailDomainMatches_ReturnsTrue( [Theory, BitAutoData] public async Task ValidateAsync_WhenEmailDomainDoesNotMatch_ReturnsFalse( - Guid code, + OrganizationInviteLink link, SutProvider sutProvider) { - var link = new OrganizationInviteLink(); + var code = Guid.NewGuid(); + link.Code = code.ToString(); link.SetAllowedDomains(["acme.com"]); sutProvider.GetDependency() - .GetByCodeAsync(code) + .GetByOrganizationIdAsync(link.OrganizationId) .Returns(link); - var result = await sutProvider.Sut.ValidateAsync(code, "user@other.com"); + var result = await sutProvider.Sut.ValidateAsync(link.OrganizationId, code, "user@other.com"); Assert.True(result.IsSuccess); Assert.False(result.AsSuccess); diff --git a/test/Infrastructure.IntegrationTest/AdminConsole/OrganizationTestHelpers.cs b/test/Infrastructure.IntegrationTest/AdminConsole/OrganizationTestHelpers.cs index 43b88f67522c..a5a64ab08950 100644 --- a/test/Infrastructure.IntegrationTest/AdminConsole/OrganizationTestHelpers.cs +++ b/test/Infrastructure.IntegrationTest/AdminConsole/OrganizationTestHelpers.cs @@ -205,7 +205,7 @@ public static Task CreateTestOrganizationInviteLinkAsync string identifier = "test") => repository.CreateAsync(new OrganizationInviteLink { - Code = Guid.NewGuid(), + Code = Guid.NewGuid().ToString(), OrganizationId = organization.Id, AllowedDomains = "[\"example.com\"]", Invite = $"invite-blob-{identifier}", diff --git a/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/OrganizationInviteLinkRepositoryTests.cs b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/OrganizationInviteLinkRepositoryTests.cs index 0b77196bbaaf..d5a9430bd1e1 100644 --- a/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/OrganizationInviteLinkRepositoryTests.cs +++ b/test/Infrastructure.IntegrationTest/AdminConsole/Repositories/OrganizationInviteLinkRepositoryTests.cs @@ -27,73 +27,41 @@ public async Task CreateAsync_Works( } [Theory, DatabaseData] - public async Task CreateAsync_DuplicateOrganizationId_Throws( + public async Task CreateAsync_RoundTrip_CodeIsUnprotected( IOrganizationInviteLinkRepository repository, IOrganizationRepository organizationRepository) { + var originalCode = Guid.NewGuid().ToString(); var organization = await organizationRepository.CreateTestOrganizationAsync(); - await repository.CreateTestOrganizationInviteLinkAsync(organization, "first"); - - await Assert.ThrowsAnyAsync( - () => repository.CreateTestOrganizationInviteLinkAsync(organization, "second")); - } - - [Theory, DatabaseData] - public async Task CreateAsync_DuplicateCode_Throws( - IOrganizationInviteLinkRepository repository, - IOrganizationRepository organizationRepository) - { - var organization1 = await organizationRepository.CreateTestOrganizationAsync(identifier: "org1"); - var organization2 = await organizationRepository.CreateTestOrganizationAsync(identifier: "org2"); - - var sharedCode = Guid.NewGuid(); await repository.CreateAsync(new OrganizationInviteLink { - Code = sharedCode, - OrganizationId = organization1.Id, + Code = originalCode, + OrganizationId = organization.Id, AllowedDomains = "[\"example.com\"]", - Invite = "invite-blob-1", + Invite = "invite-blob", SupportsConfirmation = true, CreationDate = DateTime.UtcNow, RevisionDate = DateTime.UtcNow, }); - await Assert.ThrowsAnyAsync(() => repository.CreateAsync(new OrganizationInviteLink - { - Code = sharedCode, - OrganizationId = organization2.Id, - AllowedDomains = "[\"example.com\"]", - Invite = "invite-blob-2", - SupportsConfirmation = true, - CreationDate = DateTime.UtcNow, - RevisionDate = DateTime.UtcNow, - })); + // The repository must unprotect on read — the entity's Code should match what was passed in + var result = await repository.GetByOrganizationIdAsync(organization.Id); + + Assert.NotNull(result); + Assert.Equal(originalCode, result.Code); } [Theory, DatabaseData] - public async Task GetByCodeAsync_ReturnsLink( + public async Task CreateAsync_DuplicateOrganizationId_Throws( IOrganizationInviteLinkRepository repository, IOrganizationRepository organizationRepository) { var organization = await organizationRepository.CreateTestOrganizationAsync(); - var link = await repository.CreateTestOrganizationInviteLinkAsync(organization); - - var result = await repository.GetByCodeAsync(link.Code); - - Assert.NotNull(result); - Assert.Equal(link.Id, result.Id); - Assert.Equal(link.Code, result.Code); - Assert.Equal(link.Invite, result.Invite); - } - - [Theory, DatabaseData] - public async Task GetByCodeAsync_NonExistentCode_ReturnsNull( - IOrganizationInviteLinkRepository repository) - { - var result = await repository.GetByCodeAsync(Guid.NewGuid()); + await repository.CreateTestOrganizationInviteLinkAsync(organization, "first"); - Assert.Null(result); + await Assert.ThrowsAnyAsync( + () => repository.CreateTestOrganizationInviteLinkAsync(organization, "second")); } [Theory, DatabaseData] @@ -165,7 +133,7 @@ public async Task RefreshAsync_ReplacesLink( var newLink = new OrganizationInviteLink { - Code = Guid.NewGuid(), + Code = Guid.NewGuid().ToString(), OrganizationId = organization.Id, AllowedDomains = oldLink.AllowedDomains, Invite = "new-invite-blob", @@ -186,7 +154,7 @@ public async Task RefreshAsync_ReplacesLink( } [Theory, DatabaseData] - public async Task RefreshAsync_WhenInsertViolatesUniqueCode_RollsBackDelete( + public async Task RefreshAsync_WhenInsertViolatesUniqueOrganizationId_RollsBackDelete( IOrganizationInviteLinkRepository repository, IOrganizationRepository organizationRepository) { @@ -194,12 +162,13 @@ public async Task RefreshAsync_WhenInsertViolatesUniqueCode_RollsBackDelete( var org2 = await organizationRepository.CreateTestOrganizationAsync(identifier: "org2"); var org1Link = await repository.CreateTestOrganizationInviteLinkAsync(org1); - var org2Link = await repository.CreateTestOrganizationInviteLinkAsync(org2); + await repository.CreateTestOrganizationInviteLinkAsync(org2); + // Try to refresh org1's link with a new link that claims org2's OrganizationId — violates unique index var conflictingLink = new OrganizationInviteLink { - Code = org2Link.Code, - OrganizationId = org1.Id, + Code = Guid.NewGuid().ToString(), + OrganizationId = org2.Id, AllowedDomains = org1Link.AllowedDomains, Invite = "new-invite-blob", SupportsConfirmation = true, diff --git a/util/Migrator/DbScripts/2026-07-14_01_OrganizationInviteLinkCodeProtection.sql b/util/Migrator/DbScripts/2026-07-14_01_OrganizationInviteLinkCodeProtection.sql new file mode 100644 index 000000000000..5951faa56d67 --- /dev/null +++ b/util/Migrator/DbScripts/2026-07-14_01_OrganizationInviteLinkCodeProtection.sql @@ -0,0 +1,105 @@ +-- Data-protect OrganizationInviteLink.Code at rest: change column from UNIQUEIDENTIFIER to +-- NVARCHAR(300), drop the Code unique index and ReadByCode sproc, refresh view and sproc metadata. +-- COORDINATED DEPLOY: run only after all server instances are on the new version. + +IF EXISTS (SELECT 1 FROM sys.indexes WHERE name = 'IX_OrganizationInviteLink_Code' AND object_id = OBJECT_ID('[dbo].[OrganizationInviteLink]')) +BEGIN + DROP INDEX [IX_OrganizationInviteLink_Code] ON [dbo].[OrganizationInviteLink]; +END +GO + +IF EXISTS ( + SELECT * + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = 'OrganizationInviteLink' + AND COLUMN_NAME = 'Code' + AND DATA_TYPE = 'uniqueidentifier') +BEGIN + ALTER TABLE [dbo].[OrganizationInviteLink] + ALTER COLUMN [Code] NVARCHAR(300) NOT NULL; +END +GO + +EXECUTE sp_refreshview N'[dbo].[OrganizationInviteLinkView]'; +GO + +CREATE OR ALTER PROCEDURE [dbo].[OrganizationInviteLink_Create] + @Id UNIQUEIDENTIFIER OUTPUT, + @Code NVARCHAR(300), + @OrganizationId UNIQUEIDENTIFIER, + @AllowedDomains NVARCHAR(MAX), + @Invite NVARCHAR(MAX), + @SupportsConfirmation BIT, + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + + INSERT INTO [dbo].[OrganizationInviteLink] + ( + [Id], + [Code], + [OrganizationId], + [AllowedDomains], + [Invite], + [SupportsConfirmation], + [CreationDate], + [RevisionDate] + ) + VALUES + ( + @Id, + @Code, + @OrganizationId, + @AllowedDomains, + @Invite, + @SupportsConfirmation, + @CreationDate, + @RevisionDate + ) +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[OrganizationInviteLink_Update] + @Id UNIQUEIDENTIFIER, + @Code NVARCHAR(300), + @OrganizationId UNIQUEIDENTIFIER, + @AllowedDomains NVARCHAR(MAX), + @Invite NVARCHAR(MAX), + @SupportsConfirmation BIT, + @CreationDate DATETIME2(7), + @RevisionDate DATETIME2(7) +AS +BEGIN + SET NOCOUNT ON + + UPDATE + [dbo].[OrganizationInviteLink] + SET + [Code] = @Code, + [OrganizationId] = @OrganizationId, + [AllowedDomains] = @AllowedDomains, + [Invite] = @Invite, + [SupportsConfirmation] = @SupportsConfirmation, + [CreationDate] = @CreationDate, + [RevisionDate] = @RevisionDate + WHERE + [Id] = @Id +END +GO + +DROP PROCEDURE IF EXISTS [dbo].[OrganizationInviteLink_ReadByCode]; +GO + +IF OBJECT_ID('[dbo].[OrganizationInviteLink_ReadById]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationInviteLink_ReadById]'; +END +GO + +IF OBJECT_ID('[dbo].[OrganizationInviteLink_ReadByOrganizationId]') IS NOT NULL +BEGIN + EXECUTE sp_refreshsqlmodule N'[dbo].[OrganizationInviteLink_ReadByOrganizationId]'; +END +GO diff --git a/util/MySqlMigrations/Migrations/20260710150151_OrganizationInviteLinkCodeProtection.Designer.cs b/util/MySqlMigrations/Migrations/20260710150151_OrganizationInviteLinkCodeProtection.Designer.cs new file mode 100644 index 000000000000..509895ad1383 --- /dev/null +++ b/util/MySqlMigrations/Migrations/20260710150151_OrganizationInviteLinkCodeProtection.Designer.cs @@ -0,0 +1,3769 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20260710150151_OrganizationInviteLinkCodeProtection")] + partial class OrganizationInviteLinkCodeProtection + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Bit.Core.Dirt.Reports.Models.Data.OrganizationMemberBaseDetail", b => + { + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("CollectionName") + .HasColumnType("longtext"); + + b.Property("Email") + .HasColumnType("longtext"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("GroupName") + .HasColumnType("longtext"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("Manage") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.Property("ResetPasswordKey") + .HasColumnType("longtext"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("UserGuid") + .HasColumnType("char(36)"); + + b.Property("UserName") + .HasColumnType("longtext"); + + b.Property("UsesKeyConnector") + .HasColumnType("tinyint(1)"); + + b.ToTable("OrganizationMemberBaseDetails"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DefaultUserCollectionEmail") + .HasColumnType("longtext"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("Manage") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("HidePasswords") + .HasColumnType("tinyint(1)"); + + b.Property("Manage") + .HasColumnType("tinyint(1)"); + + b.Property("ReadOnly") + .HasColumnType("tinyint(1)"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AllowAdminAccessToAllCollectionItems") + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("BillingEmail") + .IsRequired() + .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("ExemptFromBillingAutomation") + .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("LimitCollectionCreation") + .HasColumnType("tinyint(1)"); + + b.Property("LimitCollectionDeletion") + .HasColumnType("tinyint(1)"); + + b.Property("LimitItemDeletion") + .HasColumnType("tinyint(1)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("int"); + + b.Property("MaxAutoscaleSmSeats") + .HasColumnType("int"); + + b.Property("MaxAutoscaleSmServiceAccounts") + .HasColumnType("int"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("datetime(6)"); + + b.Property("Plan") + .IsRequired() + .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("SmSeats") + .HasColumnType("int"); + + b.Property("SmServiceAccounts") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("SyncSeats") + .HasColumnType("tinyint(1)"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("Use2fa") + .HasColumnType("tinyint(1)"); + + b.Property("UseAdminSponsoredFamilies") + .HasColumnType("tinyint(1)"); + + b.Property("UseApi") + .HasColumnType("tinyint(1)"); + + b.Property("UseAutomaticUserConfirmation") + .HasColumnType("tinyint(1)"); + + b.Property("UseCustomPermissions") + .HasColumnType("tinyint(1)"); + + b.Property("UseDirectory") + .HasColumnType("tinyint(1)"); + + b.Property("UseDisableSmAdsForUsers") + .HasColumnType("tinyint(1)"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.Property("UseGroups") + .HasColumnType("tinyint(1)"); + + b.Property("UseInviteLinks") + .HasColumnType("tinyint(1)"); + + b.Property("UseKeyConnector") + .HasColumnType("tinyint(1)"); + + b.Property("UseMyItems") + .HasColumnType("tinyint(1)"); + + b.Property("UseOrganizationDomains") + .HasColumnType("tinyint(1)"); + + b.Property("UsePam") + .HasColumnType("tinyint(1)"); + + b.Property("UsePasswordManager") + .HasColumnType("tinyint(1)"); + + b.Property("UsePhishingBlocker") + .HasColumnType("tinyint(1)"); + + b.Property("UsePolicies") + .HasColumnType("tinyint(1)"); + + b.Property("UseResetPassword") + .HasColumnType("tinyint(1)"); + + b.Property("UseRiskInsights") + .HasColumnType("tinyint(1)"); + + b.Property("UseScim") + .HasColumnType("tinyint(1)"); + + b.Property("UseSecretsManager") + .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.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.HasIndex("Id", "Enabled") + .HasAnnotation("Npgsql:IndexInclude", new[] { "UseTotp", "UsersGetPremium" }); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationInviteLink", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AllowedDomains") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Code") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Invite") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SupportsConfirmation") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationInviteLink", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.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") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "Type") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("BillingEmail") + .HasColumnType("longtext"); + + b.Property("BillingPhone") + .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("DiscountId") + .HasColumnType("longtext"); + + b.Property("Enabled") + .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("Name") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UseEvents") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.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", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.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", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("varchar(25)"); + + b.Property("Approved") + .HasColumnType("tinyint(1)"); + + b.Property("AuthenticationDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("MasterPasswordHash") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PublicKey") + .HasColumnType("longtext"); + + b.Property("RequestCountryName") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("tinyint unsigned"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ResponseDate") + .HasColumnType("datetime(6)"); + + b.Property("ResponseDeviceId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.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("smallint"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ConsumedDate") + .HasColumnType("datetime(6)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id") + .HasName("PK_Grant") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ExpirationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + 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", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId"); + + b.HasIndex("OrganizationId", "ExternalId") + .IsUnique() + .HasAnnotation("Npgsql:IndexInclude", new[] { "UserId" }) + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "UserId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.WebAuthnCredential", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AaGuid") + .HasColumnType("char(36)"); + + b.Property("Counter") + .HasColumnType("int"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("CredentialId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EncryptedPrivateKey") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("EncryptedPublicKey") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("EncryptedUserKey") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PublicKey") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SupportsPrf") + .HasColumnType("tinyint(1)"); + + b.Property("Type") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("WebAuthnCredential", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ClientOrganizationMigrationRecord", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("GatewayCustomerId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("GatewaySubscriptionId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("int"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PlanType") + .HasColumnType("tinyint unsigned"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("Seats") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId", "OrganizationId") + .IsUnique(); + + b.ToTable("ClientOrganizationMigrationRecord", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("InstallationId") + .HasColumnType("char(36)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("InstallationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationInstallation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohort", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ChurnDiscountCouponCode") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("MigrationPathId") + .HasColumnType("tinyint unsigned"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ProactiveDiscountCouponCode") + .HasMaxLength(64) + .HasColumnType("varchar(64)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("Name") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationPlanMigrationCohort", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohortAssignment", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ChurnDiscountAppliedDate") + .HasColumnType("datetime(6)"); + + b.Property("CohortId") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("MigratedDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("ScheduledDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("CohortId", "CreationDate", "Id") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("CohortId", "ScheduledDate", "MigratedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationPlanMigrationCohortAssignment", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AssignedSeats") + .HasColumnType("int"); + + b.Property("ClientId") + .HasColumnType("char(36)"); + + b.Property("ClientName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("InvoiceId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("InvoiceNumber") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PlanName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("Total") + .HasColumnType("decimal(65,30)"); + + b.Property("UsedSeats") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderInvoiceItem", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderPlan", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AllocatedSeats") + .HasColumnType("int"); + + b.Property("PlanType") + .HasColumnType("tinyint unsigned"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("PurchasedSeats") + .HasColumnType("int"); + + b.Property("SeatMinimum") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("Id", "PlanType") + .IsUnique(); + + b.ToTable("ProviderPlan", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.SubscriptionDiscount", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AmountOff") + .HasColumnType("bigint"); + + b.Property("AudienceType") + .HasColumnType("int"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Currency") + .HasMaxLength(10) + .HasColumnType("varchar(10)"); + + b.Property("Duration") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("DurationInMonths") + .HasColumnType("int"); + + b.Property("EndDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("PercentOff") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("StartDate") + .HasColumnType("datetime(6)"); + + b.Property("StripeCouponId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("StripeProductIds") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("StripeCouponId") + .IsUnique(); + + b.HasIndex("StartDate", "EndDate") + .HasDatabaseName("IX_SubscriptionDiscount_DateRange") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SubscriptionDiscount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationApplication", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Applications") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ContentEncryptionKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationApplication", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Configuration") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Type") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "Type") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationIntegration", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegrationConfiguration", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Configuration") + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .HasColumnType("int"); + + b.Property("Filters") + .HasColumnType("longtext"); + + b.Property("OrganizationIntegrationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Template") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationIntegrationId"); + + b.ToTable("OrganizationIntegrationConfiguration", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationReport", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ApplicationAtRiskCount") + .HasColumnType("int"); + + b.Property("ApplicationCount") + .HasColumnType("int"); + + b.Property("ApplicationData") + .HasColumnType("longtext"); + + b.Property("ContentEncryptionKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("CriticalApplicationAtRiskCount") + .HasColumnType("int"); + + b.Property("CriticalApplicationCount") + .HasColumnType("int"); + + b.Property("CriticalMemberAtRiskCount") + .HasColumnType("int"); + + b.Property("CriticalMemberCount") + .HasColumnType("int"); + + b.Property("CriticalPasswordAtRiskCount") + .HasColumnType("int"); + + b.Property("CriticalPasswordCount") + .HasColumnType("int"); + + b.Property("MemberAtRiskCount") + .HasColumnType("int"); + + b.Property("MemberCount") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PasswordAtRiskCount") + .HasColumnType("int"); + + b.Property("PasswordCount") + .HasColumnType("int"); + + b.Property("ReportData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ReportFile") + .HasColumnType("longtext"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SummaryData") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationReport", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Uri") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("PasswordHealthReportApplication", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cache", b => + { + b.Property("Id") + .HasMaxLength(449) + .HasColumnType("varchar(449)"); + + b.Property("AbsoluteExpiration") + .HasColumnType("datetime(6)"); + + b.Property("ExpiresAtTime") + .HasColumnType("datetime(6)"); + + b.Property("SlidingExpirationInSeconds") + .HasColumnType("bigint"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longblob"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ExpiresAtTime") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Cache", (string)null); + }); + + 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", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Active") + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("ClientVersion") + .HasMaxLength(43) + .HasColumnType("varchar(43)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("EncryptedPrivateKey") + .HasColumnType("longtext"); + + b.Property("EncryptedPublicKey") + .HasColumnType("longtext"); + + b.Property("EncryptedUserKey") + .HasColumnType("longtext"); + + b.Property("Identifier") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("LastActivityDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .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("Identifier") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "Identifier") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Device", (string)null); + }); + + 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("DomainName") + .HasColumnType("longtext"); + + b.Property("GrantedServiceAccountId") + .HasColumnType("char(36)"); + + 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("ProjectId") + .HasColumnType("char(36)"); + + b.Property("ProviderId") + .HasColumnType("char(36)"); + + b.Property("ProviderOrganizationId") + .HasColumnType("char(36)"); + + b.Property("ProviderUserId") + .HasColumnType("char(36)"); + + b.Property("SecretId") + .HasColumnType("char(36)"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)"); + + b.Property("SystemUser") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("Date", "OrganizationId", "ActingUserId", "CipherId") + .HasDatabaseName("IX_Event_DateOrganizationIdUserId") + .HasAnnotation("SqlServer:Clustered", false) + .HasAnnotation("SqlServer:Include", new[] { "ServiceAccountId", "GrantedServiceAccountId" }); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("varchar(300)"); + + b.Property("Name") + .IsRequired() + .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", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("char(36)"); + + b.Property("OrganizationUserId") + .HasColumnType("char(36)"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ApiKey") + .IsRequired() + .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", (string)null); + }); + + 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", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DomainName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("JobRunCount") + .HasColumnType("int"); + + b.Property("LastCheckedDate") + .HasColumnType("datetime(6)"); + + b.Property("NextRunDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Txt") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("VerifiedDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsAdminInitiated") + .HasColumnType("tinyint(1)"); + + b.Property("LastSyncDate") + .HasColumnType("datetime(6)"); + + b.Property("Notes") + .HasColumnType("longtext"); + + 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.HasIndex("SponsoringOrganizationUserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessSecretsManager") + .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("RevocationReason") + .HasColumnType("tinyint unsigned"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("StatusNew") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.PlayItem", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("PlayId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("PlayId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("PlayItem", null, t => + { + t.HasCheckConstraint("CK_PlayItem_UserOrOrganization", "(\"UserId\" IS NOT NULL AND \"OrganizationId\" IS NULL) OR (\"UserId\" IS NULL AND \"OrganizationId\" IS NOT NULL)"); + }); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("AuthType") + .HasColumnType("tinyint unsigned"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DeletionDate") + .HasColumnType("datetime(6)"); + + b.Property("Disabled") + .HasColumnType("tinyint(1)"); + + b.Property("Emails") + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("HideEmail") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .IsRequired() + .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("DeletionDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Send", (string)null); + }); + + 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") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PostalCode") + .IsRequired() + .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", (string)null); + }); + + 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("ProviderId") + .HasColumnType("char(36)"); + + 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("ProviderId"); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "OrganizationId", "CreationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Transaction", (string)null); + }); + + 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("AvatarColor") + .HasMaxLength(7) + .HasColumnType("varchar(7)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Culture") + .IsRequired() + .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("KdfMemory") + .HasColumnType("int"); + + b.Property("KdfParallelism") + .HasColumnType("int"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("LastApiKeyRotationDate") + .HasColumnType("datetime(6)"); + + b.Property("LastEmailChangeDate") + .HasColumnType("datetime(6)"); + + b.Property("LastFailedLoginDate") + .HasColumnType("datetime(6)"); + + b.Property("LastKdfChangeDate") + .HasColumnType("datetime(6)"); + + b.Property("LastKeyRotationDate") + .HasColumnType("datetime(6)"); + + b.Property("LastPasswordChangeDate") + .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("MasterPasswordSalt") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + 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("SecurityState") + .HasColumnType("longtext"); + + b.Property("SecurityVersion") + .HasColumnType("int"); + + b.Property("SignedPublicKey") + .HasColumnType("longtext"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("longtext"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("varchar(32)"); + + b.Property("UsesKeyConnector") + .HasColumnType("tinyint(1)"); + + b.Property("V2UpgradeToken") + .HasColumnType("longtext"); + + b.Property("VerifyDevices") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.HasIndex("Premium", "PremiumExpirationDate", "RenewalReminderDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("SignatureAlgorithm") + .HasColumnType("tinyint unsigned"); + + b.Property("SigningKey") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("VerifyingKey") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("UserSignatureKeyPair", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Body") + .HasMaxLength(3000) + .HasColumnType("varchar(3000)"); + + b.Property("ClientType") + .HasColumnType("tinyint unsigned"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Global") + .HasColumnType("tinyint(1)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("Priority") + .HasColumnType("tinyint unsigned"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("TaskId") + .HasColumnType("char(36)"); + + b.Property("Title") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("TaskId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("ClientType", "Global", "UserId", "OrganizationId", "Priority", "CreationDate") + .IsDescending(false, false, false, false, true, true) + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Notification", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.NotificationStatus", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("NotificationId") + .HasColumnType("char(36)"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("ReadDate") + .HasColumnType("datetime(6)"); + + b.HasKey("UserId", "NotificationId") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("NotificationId"); + + b.ToTable("NotificationStatus", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Platform.Installation", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Enabled") + .HasColumnType("tinyint(1)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("varchar(150)"); + + b.Property("LastActivityDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(34) + .HasColumnType("varchar(34)"); + + b.Property("Read") + .HasColumnType("tinyint(1)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Write") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator().HasValue("AccessPolicy"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("ClientSecretHash") + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("EncryptedPayload") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("varchar(200)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Scope") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("varchar(4000)"); + + b.Property("ServiceAccountId") + .HasColumnType("char(36)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("DeletedDate") + .HasColumnType("datetime(6)"); + + b.Property("Key") + .HasColumnType("longtext"); + + b.Property("Note") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.SecretVersion", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("EditorOrganizationUserId") + .HasColumnType("char(36)"); + + b.Property("EditorServiceAccountId") + .HasColumnType("char(36)"); + + b.Property("SecretId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("VersionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("EditorOrganizationUserId") + .HasDatabaseName("IX_SecretVersion_EditorOrganizationUserId"); + + b.HasIndex("EditorServiceAccountId") + .HasDatabaseName("IX_SecretVersion_EditorServiceAccountId"); + + b.HasIndex("SecretId") + .HasDatabaseName("IX_SecretVersion_SecretId"); + + b.ToTable("SecretVersion"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Archives") + .HasColumnType("longtext"); + + 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("Key") + .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", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.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", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", b => + { + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("CipherId") + .HasColumnType("char(36)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("OrganizationId") + .HasColumnType("char(36)"); + + b.Property("RevisionDate") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("tinyint unsigned"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("CipherId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SecurityTask", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("char(36)"); + + b.Property("SecretsId") + .HasColumnType("char(36)"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedSecretId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedSecretId"); + + b.Property("ServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedSecretId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("char(36)") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Collections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.AdminConsole.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationInviteLink", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.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.Auth.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.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.Auth.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.Auth.Models.WebAuthnCredential", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Platform.Installation", "Installation") + .WithMany() + .HasForeignKey("InstallationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Installation"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohortAssignment", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohort", "Cohort") + .WithMany() + .HasForeignKey("CohortId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cohort"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderPlan", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationApplication", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegrationConfiguration", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", "OrganizationIntegration") + .WithMany() + .HasForeignKey("OrganizationIntegrationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganizationIntegration"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationReport", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.Vault.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + 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.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.AdminConsole.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.AdminConsole.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.PlayItem", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", "Task") + .WithMany() + .HasForeignKey("TaskId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("Task"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.NotificationStatus", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", "Notification") + .WithMany() + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany("ApiKeys") + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.SecretVersion", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "EditorOrganizationUser") + .WithMany() + .HasForeignKey("EditorOrganizationUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "EditorServiceAccount") + .WithMany() + .HasForeignKey("EditorServiceAccountId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "Secret") + .WithMany("SecretVersions") + .HasForeignKey("SecretId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EditorOrganizationUser"); + + b.Navigation("EditorServiceAccount"); + + b.Navigation("Secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.Vault.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.Vault.Models.SecurityTask", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") + .WithMany() + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedSecret"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany("ProjectAccessPolicies") + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedSecret"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedSecret"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Collections"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("SecretVersions"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ProjectAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/MySqlMigrations/Migrations/20260710150151_OrganizationInviteLinkCodeProtection.cs b/util/MySqlMigrations/Migrations/20260710150151_OrganizationInviteLinkCodeProtection.cs new file mode 100644 index 000000000000..4e21c4d4b04d --- /dev/null +++ b/util/MySqlMigrations/Migrations/20260710150151_OrganizationInviteLinkCodeProtection.cs @@ -0,0 +1,47 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.MySqlMigrations.Migrations; + +/// +public partial class OrganizationInviteLinkCodeProtection : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_OrganizationInviteLink_Code", + table: "OrganizationInviteLink"); + + migrationBuilder.AlterColumn( + name: "Code", + table: "OrganizationInviteLink", + type: "longtext", + nullable: false, + oldClrType: typeof(Guid), + oldType: "char(36)") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("Relational:Collation", "ascii_general_ci"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Code", + table: "OrganizationInviteLink", + type: "char(36)", + nullable: false, + collation: "ascii_general_ci", + oldClrType: typeof(string), + oldType: "longtext") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_OrganizationInviteLink_Code", + table: "OrganizationInviteLink", + column: "Code", + unique: true); + } +} diff --git a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs index 376e4d10e61d..d72c385ee803 100644 --- a/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -390,8 +390,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("longtext"); - b.Property("Code") - .HasColumnType("char(36)"); + b.Property("Code") + .IsRequired() + .HasColumnType("longtext"); b.Property("CreationDate") .HasColumnType("datetime(6)"); @@ -411,10 +412,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.HasIndex("Code") - .IsUnique() - .HasAnnotation("SqlServer:Clustered", false); - b.HasIndex("OrganizationId") .IsUnique() .HasAnnotation("SqlServer:Clustered", false); diff --git a/util/PostgresMigrations/Migrations/20260710150135_OrganizationInviteLinkCodeProtection.Designer.cs b/util/PostgresMigrations/Migrations/20260710150135_OrganizationInviteLinkCodeProtection.Designer.cs new file mode 100644 index 000000000000..89b325e2cfe3 --- /dev/null +++ b/util/PostgresMigrations/Migrations/20260710150135_OrganizationInviteLinkCodeProtection.Designer.cs @@ -0,0 +1,3775 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20260710150135_OrganizationInviteLinkCodeProtection")] + partial class OrganizationInviteLinkCodeProtection + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:CollationDefinition:postgresIndetermanisticCollation", "en-u-ks-primary,en-u-ks-primary,icu,False") + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Bit.Core.Dirt.Reports.Models.Data.OrganizationMemberBaseDetail", b => + { + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CollectionName") + .HasColumnType("text"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("GroupName") + .HasColumnType("text"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("Manage") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.Property("ResetPasswordKey") + .HasColumnType("text"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("UserGuid") + .HasColumnType("uuid"); + + b.Property("UserName") + .HasColumnType("text"); + + b.Property("UsesKeyConnector") + .HasColumnType("boolean"); + + b.ToTable("OrganizationMemberBaseDetails"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DefaultUserCollectionEmail") + .HasColumnType("text"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("Manage") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("HidePasswords") + .HasColumnType("boolean"); + + b.Property("Manage") + .HasColumnType("boolean"); + + b.Property("ReadOnly") + .HasColumnType("boolean"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllowAdminAccessToAllCollectionItems") + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("BillingEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExemptFromBillingAutomation") + .HasColumnType("boolean"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("LimitCollectionCreation") + .HasColumnType("boolean"); + + b.Property("LimitCollectionDeletion") + .HasColumnType("boolean"); + + b.Property("LimitItemDeletion") + .HasColumnType("boolean"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("integer"); + + b.Property("MaxAutoscaleSmSeats") + .HasColumnType("integer"); + + b.Property("MaxAutoscaleSmServiceAccounts") + .HasColumnType("integer"); + + b.Property("MaxCollections") + .HasColumnType("smallint"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("timestamp with time zone"); + + b.Property("Plan") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Seats") + .HasColumnType("integer"); + + b.Property("SelfHost") + .HasColumnType("boolean"); + + b.Property("SmSeats") + .HasColumnType("integer"); + + b.Property("SmServiceAccounts") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("SyncSeats") + .HasColumnType("boolean"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("Use2fa") + .HasColumnType("boolean"); + + b.Property("UseAdminSponsoredFamilies") + .HasColumnType("boolean"); + + b.Property("UseApi") + .HasColumnType("boolean"); + + b.Property("UseAutomaticUserConfirmation") + .HasColumnType("boolean"); + + b.Property("UseCustomPermissions") + .HasColumnType("boolean"); + + b.Property("UseDirectory") + .HasColumnType("boolean"); + + b.Property("UseDisableSmAdsForUsers") + .HasColumnType("boolean"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.Property("UseGroups") + .HasColumnType("boolean"); + + b.Property("UseInviteLinks") + .HasColumnType("boolean"); + + b.Property("UseKeyConnector") + .HasColumnType("boolean"); + + b.Property("UseMyItems") + .HasColumnType("boolean"); + + b.Property("UseOrganizationDomains") + .HasColumnType("boolean"); + + b.Property("UsePam") + .HasColumnType("boolean"); + + b.Property("UsePasswordManager") + .HasColumnType("boolean"); + + b.Property("UsePhishingBlocker") + .HasColumnType("boolean"); + + b.Property("UsePolicies") + .HasColumnType("boolean"); + + b.Property("UseResetPassword") + .HasColumnType("boolean"); + + b.Property("UseRiskInsights") + .HasColumnType("boolean"); + + b.Property("UseScim") + .HasColumnType("boolean"); + + b.Property("UseSecretsManager") + .HasColumnType("boolean"); + + b.Property("UseSso") + .HasColumnType("boolean"); + + b.Property("UseTotp") + .HasColumnType("boolean"); + + b.Property("UsersGetPremium") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.HasIndex("Id", "Enabled"); + + NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Id", "Enabled"), new[] { "UseTotp", "UsersGetPremium" }); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationInviteLink", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllowedDomains") + .IsRequired() + .HasColumnType("text"); + + b.Property("Code") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Invite") + .IsRequired() + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SupportsConfirmation") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationInviteLink", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "Type") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("BillingEmail") + .HasColumnType("text"); + + b.Property("BillingPhone") + .HasColumnType("text"); + + b.Property("BusinessAddress1") + .HasColumnType("text"); + + b.Property("BusinessAddress2") + .HasColumnType("text"); + + b.Property("BusinessAddress3") + .HasColumnType("text"); + + b.Property("BusinessCountry") + .HasColumnType("text"); + + b.Property("BusinessName") + .HasColumnType("text"); + + b.Property("BusinessTaxNumber") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DiscountId") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UseEvents") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.Property("Approved") + .HasColumnType("boolean"); + + b.Property("AuthenticationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("MasterPasswordHash") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("RequestCountryName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("RequestDeviceType") + .HasColumnType("smallint"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ResponseDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ResponseDeviceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("GranteeId") + .HasColumnType("uuid"); + + b.Property("GrantorId") + .HasColumnType("uuid"); + + b.Property("KeyEncrypted") + .HasColumnType("text"); + + b.Property("LastNotificationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("WaitTimeDays") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConsumedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id") + .HasName("PK_Grant") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ExpirationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId"); + + b.HasIndex("OrganizationId", "ExternalId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("OrganizationId", "ExternalId"), new[] { "UserId" }); + + b.HasIndex("OrganizationId", "UserId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.WebAuthnCredential", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AaGuid") + .HasColumnType("uuid"); + + b.Property("Counter") + .HasColumnType("integer"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CredentialId") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EncryptedPrivateKey") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("EncryptedPublicKey") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("EncryptedUserKey") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PublicKey") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SupportsPrf") + .HasColumnType("boolean"); + + b.Property("Type") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("WebAuthnCredential", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ClientOrganizationMigrationRecord", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("GatewayCustomerId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("integer"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("Seats") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId", "OrganizationId") + .IsUnique(); + + b.ToTable("ClientOrganizationMigrationRecord", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("InstallationId") + .HasColumnType("uuid"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("InstallationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationInstallation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohort", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ChurnDiscountCouponCode") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("MigrationPathId") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ProactiveDiscountCouponCode") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("Name") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationPlanMigrationCohort", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohortAssignment", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ChurnDiscountAppliedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CohortId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("MigratedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ScheduledDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("CohortId", "CreationDate", "Id") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("CohortId", "ScheduledDate", "MigratedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationPlanMigrationCohortAssignment", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AssignedSeats") + .HasColumnType("integer"); + + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("ClientName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("InvoiceId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("InvoiceNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PlanName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("Total") + .HasColumnType("numeric"); + + b.Property("UsedSeats") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderInvoiceItem", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderPlan", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AllocatedSeats") + .HasColumnType("integer"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("PurchasedSeats") + .HasColumnType("integer"); + + b.Property("SeatMinimum") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("Id", "PlanType") + .IsUnique(); + + b.ToTable("ProviderPlan", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.SubscriptionDiscount", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AmountOff") + .HasColumnType("bigint"); + + b.Property("AudienceType") + .HasColumnType("integer"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Currency") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Duration") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("DurationInMonths") + .HasColumnType("integer"); + + b.Property("EndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("PercentOff") + .HasPrecision(5, 2) + .HasColumnType("numeric(5,2)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone"); + + b.Property("StripeCouponId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("StripeProductIds") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("StripeCouponId") + .IsUnique(); + + b.HasIndex("StartDate", "EndDate") + .HasDatabaseName("IX_SubscriptionDiscount_DateRange") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SubscriptionDiscount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationApplication", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Applications") + .IsRequired() + .HasColumnType("text"); + + b.Property("ContentEncryptionKey") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationApplication", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Configuration") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "Type") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationIntegration", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegrationConfiguration", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Configuration") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("EventType") + .HasColumnType("integer"); + + b.Property("Filters") + .HasColumnType("text"); + + b.Property("OrganizationIntegrationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Template") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationIntegrationId"); + + b.ToTable("OrganizationIntegrationConfiguration", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationReport", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationAtRiskCount") + .HasColumnType("integer"); + + b.Property("ApplicationCount") + .HasColumnType("integer"); + + b.Property("ApplicationData") + .HasColumnType("text"); + + b.Property("ContentEncryptionKey") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CriticalApplicationAtRiskCount") + .HasColumnType("integer"); + + b.Property("CriticalApplicationCount") + .HasColumnType("integer"); + + b.Property("CriticalMemberAtRiskCount") + .HasColumnType("integer"); + + b.Property("CriticalMemberCount") + .HasColumnType("integer"); + + b.Property("CriticalPasswordAtRiskCount") + .HasColumnType("integer"); + + b.Property("CriticalPasswordCount") + .HasColumnType("integer"); + + b.Property("MemberAtRiskCount") + .HasColumnType("integer"); + + b.Property("MemberCount") + .HasColumnType("integer"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PasswordAtRiskCount") + .HasColumnType("integer"); + + b.Property("PasswordCount") + .HasColumnType("integer"); + + b.Property("ReportData") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReportFile") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SummaryData") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationReport", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Uri") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("PasswordHealthReportApplication", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cache", b => + { + b.Property("Id") + .HasMaxLength(449) + .HasColumnType("character varying(449)"); + + b.Property("AbsoluteExpiration") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAtTime") + .HasColumnType("timestamp with time zone"); + + b.Property("SlidingExpirationInSeconds") + .HasColumnType("bigint"); + + b.Property("Value") + .IsRequired() + .HasColumnType("bytea"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ExpiresAtTime") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("ClientVersion") + .HasMaxLength(43) + .HasColumnType("character varying(43)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("EncryptedPrivateKey") + .HasColumnType("text"); + + b.Property("EncryptedPublicKey") + .HasColumnType("text"); + + b.Property("EncryptedUserKey") + .HasColumnType("text"); + + b.Property("Identifier") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("LastActivityDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("Identifier") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "Identifier") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ActingUserId") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CollectionId") + .HasColumnType("uuid"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("DeviceType") + .HasColumnType("smallint"); + + b.Property("DomainName") + .HasColumnType("text"); + + b.Property("GrantedServiceAccountId") + .HasColumnType("uuid"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("InstallationId") + .HasColumnType("uuid"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.Property("PolicyId") + .HasColumnType("uuid"); + + b.Property("ProjectId") + .HasColumnType("uuid"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("ProviderOrganizationId") + .HasColumnType("uuid"); + + b.Property("ProviderUserId") + .HasColumnType("uuid"); + + b.Property("SecretId") + .HasColumnType("uuid"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid"); + + b.Property("SystemUser") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("Date", "OrganizationId", "ActingUserId", "CipherId") + .HasDatabaseName("IX_Event_DateOrganizationIdUserId") + .HasAnnotation("SqlServer:Clustered", false) + .HasAnnotation("SqlServer:Include", new[] { "ServiceAccountId", "GrantedServiceAccountId" }); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("OrganizationUserId") + .HasColumnType("uuid"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Config") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DomainName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("JobRunCount") + .HasColumnType("integer"); + + b.Property("LastCheckedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("NextRunDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Txt") + .IsRequired() + .HasColumnType("text"); + + b.Property("VerifiedDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("IsAdminInitiated") + .HasColumnType("boolean"); + + b.Property("LastSyncDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Notes") + .HasColumnType("text"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PlanSponsorshipType") + .HasColumnType("smallint"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("uuid"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("uuid"); + + b.Property("ToDelete") + .HasColumnType("boolean"); + + b.Property("ValidUntil") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.HasIndex("SponsoringOrganizationUserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessSecretsManager") + .HasColumnType("boolean"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("ResetPasswordKey") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevocationReason") + .HasColumnType("smallint"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("StatusNew") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.PlayItem", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PlayId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("PlayId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("PlayItem", null, t => + { + t.HasCheckConstraint("CK_PlayItem_UserOrOrganization", "(\"UserId\" IS NOT NULL AND \"OrganizationId\" IS NULL) OR (\"UserId\" IS NULL AND \"OrganizationId\" IS NOT NULL)"); + }); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessCount") + .HasColumnType("integer"); + + b.Property("AuthType") + .HasColumnType("smallint"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .IsRequired() + .HasColumnType("text"); + + b.Property("DeletionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Disabled") + .HasColumnType("boolean"); + + b.Property("Emails") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("HideEmail") + .HasColumnType("boolean"); + + b.Property("Key") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaxAccessCount") + .HasColumnType("integer"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DeletionDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Country") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("PostalCode") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Rate") + .HasColumnType("numeric"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("character varying(2)"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("PaymentMethodType") + .HasColumnType("smallint"); + + b.Property("ProviderId") + .HasColumnType("uuid"); + + b.Property("Refunded") + .HasColumnType("boolean"); + + b.Property("RefundedAmount") + .HasColumnType("numeric"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "OrganizationId", "CreationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccountRevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("character varying(7)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Culture") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .UseCollation("postgresIndetermanisticCollation"); + + b.Property("EmailVerified") + .HasColumnType("boolean"); + + b.Property("EquivalentDomains") + .HasColumnType("text"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("text"); + + b.Property("FailedLoginCount") + .HasColumnType("integer"); + + b.Property("ForcePasswordReset") + .HasColumnType("boolean"); + + b.Property("Gateway") + .HasColumnType("smallint"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Kdf") + .HasColumnType("smallint"); + + b.Property("KdfIterations") + .HasColumnType("integer"); + + b.Property("KdfMemory") + .HasColumnType("integer"); + + b.Property("KdfParallelism") + .HasColumnType("integer"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("LastApiKeyRotationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastEmailChangeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastFailedLoginDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastKdfChangeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastKeyRotationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LastPasswordChangeDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("MasterPasswordSalt") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("MaxStorageGb") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Premium") + .HasColumnType("boolean"); + + b.Property("PremiumExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PrivateKey") + .HasColumnType("text"); + + b.Property("PublicKey") + .HasColumnType("text"); + + b.Property("ReferenceData") + .HasColumnType("text"); + + b.Property("RenewalReminderDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("SecurityState") + .HasColumnType("text"); + + b.Property("SecurityVersion") + .HasColumnType("integer"); + + b.Property("SignedPublicKey") + .HasColumnType("text"); + + b.Property("Storage") + .HasColumnType("bigint"); + + b.Property("TwoFactorProviders") + .HasColumnType("text"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("UsesKeyConnector") + .HasColumnType("boolean"); + + b.Property("V2UpgradeToken") + .HasColumnType("text"); + + b.Property("VerifyDevices") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.HasIndex("Premium", "PremiumExpirationDate", "RenewalReminderDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SignatureAlgorithm") + .HasColumnType("smallint"); + + b.Property("SigningKey") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("VerifyingKey") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("UserSignatureKeyPair", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Body") + .HasMaxLength(3000) + .HasColumnType("character varying(3000)"); + + b.Property("ClientType") + .HasColumnType("smallint"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Global") + .HasColumnType("boolean"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Priority") + .HasColumnType("smallint"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("TaskId") + .HasColumnType("uuid"); + + b.Property("Title") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("TaskId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("ClientType", "Global", "UserId", "OrganizationId", "Priority", "CreationDate") + .IsDescending(false, false, false, false, true, true) + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Notification", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.NotificationStatus", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("NotificationId") + .HasColumnType("uuid"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReadDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("UserId", "NotificationId") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("NotificationId"); + + b.ToTable("NotificationStatus", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Platform.Installation", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.Property("LastActivityDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(34) + .HasColumnType("character varying(34)"); + + b.Property("Read") + .HasColumnType("boolean"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Write") + .HasColumnType("boolean"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator().HasValue("AccessPolicy"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClientSecretHash") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("EncryptedPayload") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ExpireAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Scope") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("ServiceAccountId") + .HasColumnType("uuid"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Note") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.SecretVersion", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("EditorOrganizationUserId") + .HasColumnType("uuid"); + + b.Property("EditorServiceAccountId") + .HasColumnType("uuid"); + + b.Property("SecretId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.Property("VersionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("EditorOrganizationUserId") + .HasDatabaseName("IX_SecretVersion_EditorOrganizationUserId"); + + b.HasIndex("EditorServiceAccountId") + .HasDatabaseName("IX_SecretVersion_EditorServiceAccountId"); + + b.HasIndex("SecretId") + .HasDatabaseName("IX_SecretVersion_SecretId"); + + b.ToTable("SecretVersion"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Archives") + .HasColumnType("text"); + + b.Property("Attachments") + .HasColumnType("text"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .HasColumnType("text"); + + b.Property("DeletedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Favorites") + .HasColumnType("text"); + + b.Property("Folders") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("Reprompt") + .HasColumnType("smallint"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CipherId") + .HasColumnType("uuid"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("RevisionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("CipherId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SecurityTask", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("uuid"); + + b.Property("SecretsId") + .HasColumnType("uuid"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedSecretId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedSecretId"); + + b.Property("ServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedSecretId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("uuid") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Collections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.AdminConsole.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationInviteLink", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.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.Auth.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.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.Auth.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.Auth.Models.WebAuthnCredential", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Platform.Installation", "Installation") + .WithMany() + .HasForeignKey("InstallationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Installation"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohortAssignment", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohort", "Cohort") + .WithMany() + .HasForeignKey("CohortId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cohort"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderPlan", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationApplication", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegrationConfiguration", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", "OrganizationIntegration") + .WithMany() + .HasForeignKey("OrganizationIntegrationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganizationIntegration"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationReport", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.Vault.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + 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.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.AdminConsole.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.AdminConsole.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.PlayItem", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", "Task") + .WithMany() + .HasForeignKey("TaskId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("Task"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.NotificationStatus", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", "Notification") + .WithMany() + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany("ApiKeys") + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.SecretVersion", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "EditorOrganizationUser") + .WithMany() + .HasForeignKey("EditorOrganizationUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "EditorServiceAccount") + .WithMany() + .HasForeignKey("EditorServiceAccountId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "Secret") + .WithMany("SecretVersions") + .HasForeignKey("SecretId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EditorOrganizationUser"); + + b.Navigation("EditorServiceAccount"); + + b.Navigation("Secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.Vault.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.Vault.Models.SecurityTask", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") + .WithMany() + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedSecret"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany("ProjectAccessPolicies") + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedSecret"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedSecret"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Collections"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("SecretVersions"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ProjectAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/PostgresMigrations/Migrations/20260710150135_OrganizationInviteLinkCodeProtection.cs b/util/PostgresMigrations/Migrations/20260710150135_OrganizationInviteLinkCodeProtection.cs new file mode 100644 index 000000000000..168590c1980b --- /dev/null +++ b/util/PostgresMigrations/Migrations/20260710150135_OrganizationInviteLinkCodeProtection.cs @@ -0,0 +1,43 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.PostgresMigrations.Migrations; + +/// +public partial class OrganizationInviteLinkCodeProtection : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_OrganizationInviteLink_Code", + table: "OrganizationInviteLink"); + + migrationBuilder.AlterColumn( + name: "Code", + table: "OrganizationInviteLink", + type: "text", + nullable: false, + oldClrType: typeof(Guid), + oldType: "uuid"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Code", + table: "OrganizationInviteLink", + type: "uuid", + nullable: false, + oldClrType: typeof(string), + oldType: "text"); + + migrationBuilder.CreateIndex( + name: "IX_OrganizationInviteLink_Code", + table: "OrganizationInviteLink", + column: "Code", + unique: true); + } +} diff --git a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs index 7467281e6fd0..13eec7a2e0a6 100644 --- a/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -393,8 +393,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b.Property("Code") - .HasColumnType("uuid"); + b.Property("Code") + .IsRequired() + .HasColumnType("text"); b.Property("CreationDate") .HasColumnType("timestamp with time zone"); @@ -414,10 +415,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.HasIndex("Code") - .IsUnique() - .HasAnnotation("SqlServer:Clustered", false); - b.HasIndex("OrganizationId") .IsUnique() .HasAnnotation("SqlServer:Clustered", false); diff --git a/util/SqliteMigrations/Migrations/20260710150143_OrganizationInviteLinkCodeProtection.Designer.cs b/util/SqliteMigrations/Migrations/20260710150143_OrganizationInviteLinkCodeProtection.Designer.cs new file mode 100644 index 000000000000..a9b290452fa1 --- /dev/null +++ b/util/SqliteMigrations/Migrations/20260710150143_OrganizationInviteLinkCodeProtection.Designer.cs @@ -0,0 +1,3758 @@ +// +using System; +using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations +{ + [DbContext(typeof(DatabaseContext))] + [Migration("20260710150143_OrganizationInviteLinkCodeProtection")] + partial class OrganizationInviteLinkCodeProtection + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.8"); + + modelBuilder.Entity("Bit.Core.Dirt.Reports.Models.Data.OrganizationMemberBaseDetail", b => + { + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("CollectionName") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("GroupName") + .HasColumnType("TEXT"); + + b.Property("HidePasswords") + .HasColumnType("INTEGER"); + + b.Property("Manage") + .HasColumnType("INTEGER"); + + b.Property("ReadOnly") + .HasColumnType("INTEGER"); + + b.Property("ResetPasswordKey") + .HasColumnType("TEXT"); + + b.Property("TwoFactorProviders") + .HasColumnType("TEXT"); + + b.Property("UserGuid") + .HasColumnType("TEXT"); + + b.Property("UserName") + .HasColumnType("TEXT"); + + b.Property("UsesKeyConnector") + .HasColumnType("INTEGER"); + + b.ToTable("OrganizationMemberBaseDetails"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DefaultUserCollectionEmail") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Collection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionGroup", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("HidePasswords") + .HasColumnType("INTEGER"); + + b.Property("Manage") + .HasColumnType("INTEGER"); + + b.Property("ReadOnly") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "GroupId"); + + b.HasIndex("GroupId"); + + b.ToTable("CollectionGroups"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionUser", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("HidePasswords") + .HasColumnType("INTEGER"); + + b.Property("Manage") + .HasColumnType("INTEGER"); + + b.Property("ReadOnly") + .HasColumnType("INTEGER"); + + b.HasKey("CollectionId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AllowAdminAccessToAllCollectionItems") + .HasColumnType("INTEGER") + .HasDefaultValue(true); + + b.Property("BillingEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress1") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress2") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessAddress3") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessCountry") + .HasMaxLength(2) + .HasColumnType("TEXT"); + + b.Property("BusinessName") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("BusinessTaxNumber") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("ExemptFromBillingAutomation") + .HasColumnType("INTEGER"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Identifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("LimitCollectionCreation") + .HasColumnType("INTEGER"); + + b.Property("LimitCollectionDeletion") + .HasColumnType("INTEGER"); + + b.Property("LimitItemDeletion") + .HasColumnType("INTEGER"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("INTEGER"); + + b.Property("MaxAutoscaleSmSeats") + .HasColumnType("INTEGER"); + + b.Property("MaxAutoscaleSmServiceAccounts") + .HasColumnType("INTEGER"); + + b.Property("MaxCollections") + .HasColumnType("INTEGER"); + + b.Property("MaxStorageGb") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OwnersNotifiedOfAutoscaling") + .HasColumnType("TEXT"); + + b.Property("Plan") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PlanType") + .HasColumnType("INTEGER"); + + b.Property("PrivateKey") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("ReferenceData") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Seats") + .HasColumnType("INTEGER"); + + b.Property("SelfHost") + .HasColumnType("INTEGER"); + + b.Property("SmSeats") + .HasColumnType("INTEGER"); + + b.Property("SmServiceAccounts") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Storage") + .HasColumnType("INTEGER"); + + b.Property("SyncSeats") + .HasColumnType("INTEGER"); + + b.Property("TwoFactorProviders") + .HasColumnType("TEXT"); + + b.Property("Use2fa") + .HasColumnType("INTEGER"); + + b.Property("UseAdminSponsoredFamilies") + .HasColumnType("INTEGER"); + + b.Property("UseApi") + .HasColumnType("INTEGER"); + + b.Property("UseAutomaticUserConfirmation") + .HasColumnType("INTEGER"); + + b.Property("UseCustomPermissions") + .HasColumnType("INTEGER"); + + b.Property("UseDirectory") + .HasColumnType("INTEGER"); + + b.Property("UseDisableSmAdsForUsers") + .HasColumnType("INTEGER"); + + b.Property("UseEvents") + .HasColumnType("INTEGER"); + + b.Property("UseGroups") + .HasColumnType("INTEGER"); + + b.Property("UseInviteLinks") + .HasColumnType("INTEGER"); + + b.Property("UseKeyConnector") + .HasColumnType("INTEGER"); + + b.Property("UseMyItems") + .HasColumnType("INTEGER"); + + b.Property("UseOrganizationDomains") + .HasColumnType("INTEGER"); + + b.Property("UsePam") + .HasColumnType("INTEGER"); + + b.Property("UsePasswordManager") + .HasColumnType("INTEGER"); + + b.Property("UsePhishingBlocker") + .HasColumnType("INTEGER"); + + b.Property("UsePolicies") + .HasColumnType("INTEGER"); + + b.Property("UseResetPassword") + .HasColumnType("INTEGER"); + + b.Property("UseRiskInsights") + .HasColumnType("INTEGER"); + + b.Property("UseScim") + .HasColumnType("INTEGER"); + + b.Property("UseSecretsManager") + .HasColumnType("INTEGER"); + + b.Property("UseSso") + .HasColumnType("INTEGER"); + + b.Property("UseTotp") + .HasColumnType("INTEGER"); + + b.Property("UsersGetPremium") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.HasIndex("Id", "Enabled") + .HasAnnotation("Npgsql:IndexInclude", new[] { "UseTotp", "UsersGetPremium" }); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationInviteLink", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AllowedDomains") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Invite") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SupportsConfirmation") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationInviteLink", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Policy", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "Type") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Policy", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("BillingEmail") + .HasColumnType("TEXT"); + + b.Property("BillingPhone") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress1") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress2") + .HasColumnType("TEXT"); + + b.Property("BusinessAddress3") + .HasColumnType("TEXT"); + + b.Property("BusinessCountry") + .HasColumnType("TEXT"); + + b.Property("BusinessName") + .HasColumnType("TEXT"); + + b.Property("BusinessTaxNumber") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DiscountId") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UseEvents") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.ToTable("Provider", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Settings") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderOrganization", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Permissions") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId"); + + b.ToTable("ProviderUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessCode") + .HasMaxLength(25) + .HasColumnType("TEXT"); + + b.Property("Approved") + .HasColumnType("INTEGER"); + + b.Property("AuthenticationDate") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("MasterPasswordHash") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("RequestCountryName") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("RequestDeviceIdentifier") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("RequestDeviceType") + .HasColumnType("INTEGER"); + + b.Property("RequestIpAddress") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ResponseDate") + .HasColumnType("TEXT"); + + b.Property("ResponseDeviceId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ResponseDeviceId"); + + b.HasIndex("UserId"); + + b.ToTable("AuthRequest", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("GranteeId") + .HasColumnType("TEXT"); + + b.Property("GrantorId") + .HasColumnType("TEXT"); + + b.Property("KeyEncrypted") + .HasColumnType("TEXT"); + + b.Property("LastNotificationDate") + .HasColumnType("TEXT"); + + b.Property("RecoveryInitiatedDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("WaitTimeDays") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("GranteeId"); + + b.HasIndex("GrantorId"); + + b.ToTable("EmergencyAccess", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ConsumedDate") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasName("PK_Grant") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ExpirationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("Grant", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SsoConfig", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId"); + + b.HasIndex("OrganizationId", "ExternalId") + .IsUnique() + .HasAnnotation("Npgsql:IndexInclude", new[] { "UserId" }) + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "UserId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SsoUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.WebAuthnCredential", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AaGuid") + .HasColumnType("TEXT"); + + b.Property("Counter") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("CredentialId") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EncryptedPrivateKey") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("EncryptedPublicKey") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("EncryptedUserKey") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SupportsPrf") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("WebAuthnCredential", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ClientOrganizationMigrationRecord", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("GatewayCustomerId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("MaxAutoscaleSeats") + .HasColumnType("INTEGER"); + + b.Property("MaxStorageGb") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PlanType") + .HasColumnType("INTEGER"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("Seats") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId", "OrganizationId") + .IsUnique(); + + b.ToTable("ClientOrganizationMigrationRecord", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("InstallationId") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("InstallationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationInstallation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohort", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ChurnDiscountCouponCode") + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("IsActive") + .HasColumnType("INTEGER"); + + b.Property("MigrationPathId") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("ProactiveDiscountCouponCode") + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("Name") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationPlanMigrationCohort", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohortAssignment", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ChurnDiscountAppliedDate") + .HasColumnType("TEXT"); + + b.Property("CohortId") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("MigratedDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("ScheduledDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("CohortId", "CreationDate", "Id") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("CohortId", "ScheduledDate", "MigratedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationPlanMigrationCohortAssignment", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AssignedSeats") + .HasColumnType("INTEGER"); + + b.Property("ClientId") + .HasColumnType("TEXT"); + + b.Property("ClientName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Created") + .HasColumnType("TEXT"); + + b.Property("InvoiceId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("InvoiceNumber") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PlanName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("Total") + .HasColumnType("TEXT"); + + b.Property("UsedSeats") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.ToTable("ProviderInvoiceItem", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderPlan", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AllocatedSeats") + .HasColumnType("INTEGER"); + + b.Property("PlanType") + .HasColumnType("INTEGER"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("PurchasedSeats") + .HasColumnType("INTEGER"); + + b.Property("SeatMinimum") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ProviderId"); + + b.HasIndex("Id", "PlanType") + .IsUnique(); + + b.ToTable("ProviderPlan", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.SubscriptionDiscount", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AmountOff") + .HasColumnType("INTEGER"); + + b.Property("AudienceType") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Currency") + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("Duration") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.Property("DurationInMonths") + .HasColumnType("INTEGER"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("PercentOff") + .HasPrecision(5, 2) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("StripeCouponId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("StripeProductIds") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("StripeCouponId") + .IsUnique(); + + b.HasIndex("StartDate", "EndDate") + .HasDatabaseName("IX_SubscriptionDiscount_DateRange") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SubscriptionDiscount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationApplication", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Applications") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ContentEncryptionKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationApplication", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Configuration") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId", "Type") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationIntegration", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegrationConfiguration", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Configuration") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("EventType") + .HasColumnType("INTEGER"); + + b.Property("Filters") + .HasColumnType("TEXT"); + + b.Property("OrganizationIntegrationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Template") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationIntegrationId"); + + b.ToTable("OrganizationIntegrationConfiguration", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationReport", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ApplicationAtRiskCount") + .HasColumnType("INTEGER"); + + b.Property("ApplicationCount") + .HasColumnType("INTEGER"); + + b.Property("ApplicationData") + .HasColumnType("TEXT"); + + b.Property("ContentEncryptionKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("CriticalApplicationAtRiskCount") + .HasColumnType("INTEGER"); + + b.Property("CriticalApplicationCount") + .HasColumnType("INTEGER"); + + b.Property("CriticalMemberAtRiskCount") + .HasColumnType("INTEGER"); + + b.Property("CriticalMemberCount") + .HasColumnType("INTEGER"); + + b.Property("CriticalPasswordAtRiskCount") + .HasColumnType("INTEGER"); + + b.Property("CriticalPasswordCount") + .HasColumnType("INTEGER"); + + b.Property("MemberAtRiskCount") + .HasColumnType("INTEGER"); + + b.Property("MemberCount") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PasswordAtRiskCount") + .HasColumnType("INTEGER"); + + b.Property("PasswordCount") + .HasColumnType("INTEGER"); + + b.Property("ReportData") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ReportFile") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SummaryData") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationReport", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Uri") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("PasswordHealthReportApplication", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cache", b => + { + b.Property("Id") + .HasMaxLength(449) + .HasColumnType("TEXT"); + + b.Property("AbsoluteExpiration") + .HasColumnType("TEXT"); + + b.Property("ExpiresAtTime") + .HasColumnType("TEXT"); + + b.Property("SlidingExpirationInSeconds") + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("BLOB"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ExpiresAtTime") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.CollectionCipher", b => + { + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.HasKey("CollectionId", "CipherId"); + + b.HasIndex("CipherId"); + + b.ToTable("CollectionCipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Device", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Active") + .HasColumnType("INTEGER") + .HasDefaultValue(true); + + b.Property("ClientVersion") + .HasMaxLength(43) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("EncryptedPrivateKey") + .HasColumnType("TEXT"); + + b.Property("EncryptedPublicKey") + .HasColumnType("TEXT"); + + b.Property("EncryptedUserKey") + .HasColumnType("TEXT"); + + b.Property("Identifier") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("LastActivityDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PushToken") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Identifier") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "Identifier") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Device", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Event", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ActingUserId") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CollectionId") + .HasColumnType("TEXT"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("DeviceType") + .HasColumnType("INTEGER"); + + b.Property("DomainName") + .HasColumnType("TEXT"); + + b.Property("GrantedServiceAccountId") + .HasColumnType("TEXT"); + + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("InstallationId") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("PolicyId") + .HasColumnType("TEXT"); + + b.Property("ProjectId") + .HasColumnType("TEXT"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("ProviderOrganizationId") + .HasColumnType("TEXT"); + + b.Property("ProviderUserId") + .HasColumnType("TEXT"); + + b.Property("SecretId") + .HasColumnType("TEXT"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT"); + + b.Property("SystemUser") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("Date", "OrganizationId", "ActingUserId", "CipherId") + .HasDatabaseName("IX_Event_DateOrganizationIdUserId") + .HasAnnotation("SqlServer:Clustered", false) + .HasAnnotation("SqlServer:Include", new[] { "ServiceAccountId", "GrantedServiceAccountId" }); + + b.ToTable("Event", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Group", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.GroupUser", b => + { + b.Property("GroupId") + .HasColumnType("TEXT"); + + b.Property("OrganizationUserId") + .HasColumnType("TEXT"); + + b.HasKey("GroupId", "OrganizationUserId"); + + b.HasIndex("OrganizationUserId"); + + b.ToTable("GroupUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationConnection", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Config") + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationConnection", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DomainName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("JobRunCount") + .HasColumnType("INTEGER"); + + b.Property("LastCheckedDate") + .HasColumnType("TEXT"); + + b.Property("NextRunDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Txt") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("VerifiedDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationDomain", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("FriendlyName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("IsAdminInitiated") + .HasColumnType("INTEGER"); + + b.Property("LastSyncDate") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasColumnType("TEXT"); + + b.Property("OfferedToEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PlanSponsorshipType") + .HasColumnType("INTEGER"); + + b.Property("SponsoredOrganizationId") + .HasColumnType("TEXT"); + + b.Property("SponsoringOrganizationId") + .HasColumnType("TEXT"); + + b.Property("SponsoringOrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("ToDelete") + .HasColumnType("INTEGER"); + + b.Property("ValidUntil") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SponsoredOrganizationId"); + + b.HasIndex("SponsoringOrganizationId"); + + b.HasIndex("SponsoringOrganizationUserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationSponsorship", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessSecretsManager") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("ExternalId") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Permissions") + .HasColumnType("TEXT"); + + b.Property("ResetPasswordKey") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("RevocationReason") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("StatusNew") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("OrganizationUser", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.PlayItem", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PlayId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("PlayId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("PlayItem", null, t => + { + t.HasCheckConstraint("CK_PlayItem_UserOrOrganization", "(\"UserId\" IS NOT NULL AND \"OrganizationId\" IS NULL) OR (\"UserId\" IS NULL AND \"OrganizationId\" IS NOT NULL)"); + }); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessCount") + .HasColumnType("INTEGER"); + + b.Property("AuthType") + .HasColumnType("INTEGER"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("DeletionDate") + .HasColumnType("TEXT"); + + b.Property("Disabled") + .HasColumnType("INTEGER"); + + b.Property("Emails") + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("ExpirationDate") + .HasColumnType("TEXT"); + + b.Property("HideEmail") + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MaxAccessCount") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Password") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("DeletionDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Send", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.TaxRate", b => + { + b.Property("Id") + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("Active") + .HasColumnType("INTEGER"); + + b.Property("Country") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("PostalCode") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("Rate") + .HasColumnType("TEXT"); + + b.Property("State") + .HasMaxLength(2) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("TaxRate", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Transaction", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Amount") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Details") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("PaymentMethodType") + .HasColumnType("INTEGER"); + + b.Property("ProviderId") + .HasColumnType("TEXT"); + + b.Property("Refunded") + .HasColumnType("INTEGER"); + + b.Property("RefundedAmount") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("ProviderId"); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId", "OrganizationId", "CreationDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Transaction", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccountRevisionDate") + .HasColumnType("TEXT"); + + b.Property("ApiKey") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("AvatarColor") + .HasMaxLength(7) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Culture") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailVerified") + .HasColumnType("INTEGER"); + + b.Property("EquivalentDomains") + .HasColumnType("TEXT"); + + b.Property("ExcludedGlobalEquivalentDomains") + .HasColumnType("TEXT"); + + b.Property("FailedLoginCount") + .HasColumnType("INTEGER"); + + b.Property("ForcePasswordReset") + .HasColumnType("INTEGER"); + + b.Property("Gateway") + .HasColumnType("INTEGER"); + + b.Property("GatewayCustomerId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("GatewaySubscriptionId") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Kdf") + .HasColumnType("INTEGER"); + + b.Property("KdfIterations") + .HasColumnType("INTEGER"); + + b.Property("KdfMemory") + .HasColumnType("INTEGER"); + + b.Property("KdfParallelism") + .HasColumnType("INTEGER"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("LastApiKeyRotationDate") + .HasColumnType("TEXT"); + + b.Property("LastEmailChangeDate") + .HasColumnType("TEXT"); + + b.Property("LastFailedLoginDate") + .HasColumnType("TEXT"); + + b.Property("LastKdfChangeDate") + .HasColumnType("TEXT"); + + b.Property("LastKeyRotationDate") + .HasColumnType("TEXT"); + + b.Property("LastPasswordChangeDate") + .HasColumnType("TEXT"); + + b.Property("LicenseKey") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("MasterPassword") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("MasterPasswordHint") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("MasterPasswordSalt") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("MaxStorageGb") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Premium") + .HasColumnType("INTEGER"); + + b.Property("PremiumExpirationDate") + .HasColumnType("TEXT"); + + b.Property("PrivateKey") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .HasColumnType("TEXT"); + + b.Property("ReferenceData") + .HasColumnType("TEXT"); + + b.Property("RenewalReminderDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("SecurityState") + .HasColumnType("TEXT"); + + b.Property("SecurityVersion") + .HasColumnType("INTEGER"); + + b.Property("SignedPublicKey") + .HasColumnType("TEXT"); + + b.Property("Storage") + .HasColumnType("INTEGER"); + + b.Property("TwoFactorProviders") + .HasColumnType("TEXT"); + + b.Property("TwoFactorRecoveryCode") + .HasMaxLength(32) + .HasColumnType("TEXT"); + + b.Property("UsesKeyConnector") + .HasColumnType("INTEGER"); + + b.Property("V2UpgradeToken") + .HasColumnType("TEXT"); + + b.Property("VerifyDevices") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("GatewayCustomerId"); + + b.HasIndex("GatewaySubscriptionId"); + + b.HasIndex("Premium", "PremiumExpirationDate", "RenewalReminderDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("User", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("SignatureAlgorithm") + .HasColumnType("INTEGER"); + + b.Property("SigningKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("VerifyingKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("UserSignatureKeyPair", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Body") + .HasMaxLength(3000) + .HasColumnType("TEXT"); + + b.Property("ClientType") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Global") + .HasColumnType("INTEGER"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Priority") + .HasColumnType("INTEGER"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("TaskId") + .HasColumnType("TEXT"); + + b.Property("Title") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("TaskId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("UserId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("ClientType", "Global", "UserId", "OrganizationId", "Priority", "CreationDate") + .IsDescending(false, false, false, false, true, true) + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Notification", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.NotificationStatus", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("NotificationId") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("ReadDate") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "NotificationId") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("NotificationId"); + + b.ToTable("NotificationStatus", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Platform.Installation", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("Enabled") + .HasColumnType("INTEGER"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(150) + .HasColumnType("TEXT"); + + b.Property("LastActivityDate") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Installation", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(34) + .HasColumnType("TEXT"); + + b.Property("Read") + .HasColumnType("INTEGER"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Write") + .HasColumnType("INTEGER"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("AccessPolicy", (string)null); + + b.HasDiscriminator().HasValue("AccessPolicy"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ClientSecretHash") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("EncryptedPayload") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("ExpireAt") + .HasColumnType("TEXT"); + + b.Property("Key") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Scope") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("TEXT"); + + b.Property("ServiceAccountId") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("ServiceAccountId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ApiKey", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Project", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Note") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("DeletedDate") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("Secret", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.SecretVersion", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("EditorOrganizationUserId") + .HasColumnType("TEXT"); + + b.Property("EditorServiceAccountId") + .HasColumnType("TEXT"); + + b.Property("SecretId") + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("VersionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("EditorOrganizationUserId") + .HasDatabaseName("IX_SecretVersion_EditorOrganizationUserId"); + + b.HasIndex("EditorServiceAccountId") + .HasDatabaseName("IX_SecretVersion_EditorServiceAccountId"); + + b.HasIndex("SecretId") + .HasDatabaseName("IX_SecretVersion_SecretId"); + + b.ToTable("SecretVersion"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("ServiceAccount", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Archives") + .HasColumnType("TEXT"); + + b.Property("Attachments") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Data") + .HasColumnType("TEXT"); + + b.Property("DeletedDate") + .HasColumnType("TEXT"); + + b.Property("Favorites") + .HasColumnType("TEXT"); + + b.Property("Folders") + .HasColumnType("TEXT"); + + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("Reprompt") + .HasColumnType("INTEGER"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("UserId"); + + b.ToTable("Cipher", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Folder", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Folder", (string)null); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("CipherId") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("OrganizationId") + .HasColumnType("TEXT"); + + b.Property("RevisionDate") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("Id") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("CipherId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("OrganizationId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("SecurityTask", (string)null); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.Property("ProjectsId") + .HasColumnType("TEXT"); + + b.Property("SecretsId") + .HasColumnType("TEXT"); + + b.HasKey("ProjectsId", "SecretsId"); + + b.HasIndex("SecretsId"); + + b.ToTable("ProjectSecret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedSecretId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("GroupId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GroupId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("GroupId"); + + b.HasDiscriminator().HasValue("group_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("ServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedSecretId"); + + b.Property("ServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("ServiceAccountId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("ServiceAccountId"); + + b.HasDiscriminator().HasValue("service_account_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedProjectId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedProjectId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedProjectId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_project"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserSecretAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedSecretId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedSecretId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedSecretId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasBaseType("Bit.Infrastructure.EntityFramework.SecretsManager.Models.AccessPolicy"); + + b.Property("GrantedServiceAccountId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("GrantedServiceAccountId"); + + b.Property("OrganizationUserId") + .ValueGeneratedOnUpdateSometimes() + .HasColumnType("TEXT") + .HasColumnName("OrganizationUserId"); + + b.HasIndex("GrantedServiceAccountId"); + + b.HasIndex("OrganizationUserId"); + + b.HasDiscriminator().HasValue("user_service_account"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Collections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.CollectionGroup", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.AdminConsole.Models.CollectionUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.Navigation("Collection"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationInviteLink", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Policy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Policies") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.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.Auth.Models.AuthRequest", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice") + .WithMany() + .HasForeignKey("ResponseDeviceId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("ResponseDevice"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.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.Auth.Models.SsoConfig", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("SsoConfigs") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.SsoUser", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.Auth.Models.WebAuthnCredential", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationInstallation", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Platform.Installation", "Installation") + .WithMany() + .HasForeignKey("InstallationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Installation"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohortAssignment", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Billing.Models.OrganizationPlanMigrationCohort", "Cohort") + .WithMany() + .HasForeignKey("CohortId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cohort"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderInvoiceItem", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Billing.Models.ProviderPlan", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Provider"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationApplication", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegrationConfiguration", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationIntegration", "OrganizationIntegration") + .WithMany() + .HasForeignKey("OrganizationIntegrationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganizationIntegration"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.OrganizationReport", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Dirt.Models.PasswordHealthReportApplication", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.Vault.Models.Cipher", "Cipher") + .WithMany("CollectionCiphers") + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", "Collection") + .WithMany("CollectionCiphers") + .HasForeignKey("CollectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Collection"); + }); + + 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.Group", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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("GroupUsers") + .HasForeignKey("OrganizationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Group"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.AdminConsole.Models.Organization", "Organization") + .WithMany("Connections") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationDomain", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Domains") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationSponsorship", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "SponsoredOrganization") + .WithMany() + .HasForeignKey("SponsoredOrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.AdminConsole.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.PlayItem", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Organization"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Send", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.Transaction", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany("Transactions") + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", "Provider") + .WithMany() + .HasForeignKey("ProviderId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany("Transactions") + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("Provider"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.SecurityTask", "Task") + .WithMany() + .HasForeignKey("TaskId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Organization"); + + b.Navigation("Task"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.NotificationStatus", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", "Notification") + .WithMany() + .HasForeignKey("NotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notification"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ApiKey", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany("ApiKeys") + .HasForeignKey("ServiceAccountId"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.SecretVersion", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "EditorOrganizationUser") + .WithMany() + .HasForeignKey("EditorOrganizationUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "EditorServiceAccount") + .WithMany() + .HasForeignKey("EditorServiceAccountId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "Secret") + .WithMany("SecretVersions") + .HasForeignKey("SecretId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EditorOrganizationUser"); + + b.Navigation("EditorServiceAccount"); + + b.Navigation("Secret"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.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.Vault.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.Vault.Models.SecurityTask", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", "Cipher") + .WithMany() + .HasForeignKey("CipherId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cipher"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("ProjectSecret", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", null) + .WithMany() + .HasForeignKey("ProjectsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", null) + .WithMany() + .HasForeignKey("SecretsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedProject"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedSecret"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.GroupServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("GroupAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.Group", "Group") + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("Group"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany("ProjectAccessPolicies") + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedProject"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccountSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("ServiceAccountAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "ServiceAccount") + .WithMany() + .HasForeignKey("ServiceAccountId"); + + b.Navigation("GrantedSecret"); + + b.Navigation("ServiceAccount"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserProjectAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", "GrantedProject") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedProjectId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedProject"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserSecretAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", "GrantedSecret") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedSecretId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedSecret"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.UserServiceAccountAccessPolicy", b => + { + b.HasOne("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", "GrantedServiceAccount") + .WithMany("UserAccessPolicies") + .HasForeignKey("GrantedServiceAccountId"); + + b.HasOne("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", "OrganizationUser") + .WithMany() + .HasForeignKey("OrganizationUserId"); + + b.Navigation("GrantedServiceAccount"); + + b.Navigation("OrganizationUser"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Collection", b => + { + b.Navigation("CollectionCiphers"); + + b.Navigation("CollectionGroups"); + + b.Navigation("CollectionUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("Ciphers"); + + b.Navigation("Collections"); + + b.Navigation("Connections"); + + b.Navigation("Domains"); + + b.Navigation("Groups"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("Policies"); + + b.Navigation("SsoConfigs"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Group", b => + { + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.OrganizationUser", b => + { + b.Navigation("CollectionUsers"); + + b.Navigation("GroupUsers"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.User", b => + { + b.Navigation("Ciphers"); + + b.Navigation("Folders"); + + b.Navigation("OrganizationUsers"); + + b.Navigation("SsoUsers"); + + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Project", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.Secret", b => + { + b.Navigation("GroupAccessPolicies"); + + b.Navigation("SecretVersions"); + + b.Navigation("ServiceAccountAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.SecretsManager.Models.ServiceAccount", b => + { + b.Navigation("ApiKeys"); + + b.Navigation("GroupAccessPolicies"); + + b.Navigation("ProjectAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b => + { + b.Navigation("CollectionCiphers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/util/SqliteMigrations/Migrations/20260710150143_OrganizationInviteLinkCodeProtection.cs b/util/SqliteMigrations/Migrations/20260710150143_OrganizationInviteLinkCodeProtection.cs new file mode 100644 index 000000000000..62ad6b6d9bbd --- /dev/null +++ b/util/SqliteMigrations/Migrations/20260710150143_OrganizationInviteLinkCodeProtection.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Bit.SqliteMigrations.Migrations; + +/// +public partial class OrganizationInviteLinkCodeProtection : Migration +{ + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_OrganizationInviteLink_Code", + table: "OrganizationInviteLink"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateIndex( + name: "IX_OrganizationInviteLink_Code", + table: "OrganizationInviteLink", + column: "Code", + unique: true); + } +} diff --git a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs index d9e7b0c73707..d6f4baaca92b 100644 --- a/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs +++ b/util/SqliteMigrations/Migrations/DatabaseContextModelSnapshot.cs @@ -385,7 +385,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("TEXT"); - b.Property("Code") + b.Property("Code") + .IsRequired() .HasColumnType("TEXT"); b.Property("CreationDate") @@ -406,10 +407,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.HasIndex("Code") - .IsUnique() - .HasAnnotation("SqlServer:Clustered", false); - b.HasIndex("OrganizationId") .IsUnique() .HasAnnotation("SqlServer:Clustered", false);