1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-04 09:32:48 -05:00

[AC-1283] AC Team code ownership moves: Organization (pt 2) (#3486)

* move remaining Organization domain files

* namespaces will be updated in a separate commit
This commit is contained in:
Thomas Rittson
2023-11-30 07:31:15 +10:00
committed by GitHub
parent 09d07d864e
commit a4ddb4b212
13 changed files with 0 additions and 0 deletions

View File

@ -0,0 +1,28 @@
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Utilities;
namespace Bit.Core.Context;
public class CurrentContextOrganization
{
public CurrentContextOrganization() { }
public CurrentContextOrganization(OrganizationUserOrganizationDetails orgUser)
{
Id = orgUser.OrganizationId;
Type = orgUser.Type;
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(orgUser.Permissions);
AccessSecretsManager = orgUser.AccessSecretsManager && orgUser.UseSecretsManager && orgUser.Enabled;
LimitCollectionCreationDeletion = orgUser.LimitCollectionCreationDeletion;
AllowAdminAccessToAllCollectionItems = orgUser.AllowAdminAccessToAllCollectionItems;
}
public Guid Id { get; set; }
public OrganizationUserType Type { get; set; }
public Permissions Permissions { get; set; } = new();
public bool AccessSecretsManager { get; set; }
public bool LimitCollectionCreationDeletion { get; set; }
public bool AllowAdminAccessToAllCollectionItems { get; set; }
}

View File

@ -0,0 +1,7 @@
namespace Bit.Core.Enums;
public enum OrganizationStatusType : byte
{
Pending = 0,
Created = 1
}

View File

@ -0,0 +1,38 @@
using Bit.Core.AdminConsole.Entities;
namespace Bit.Core.Models.Data.Organizations;
public class OrganizationAbility
{
public OrganizationAbility() { }
public OrganizationAbility(Organization organization)
{
Id = organization.Id;
UseEvents = organization.UseEvents;
Use2fa = organization.Use2fa;
Using2fa = organization.Use2fa && organization.TwoFactorProviders != null &&
organization.TwoFactorProviders != "{}";
UsersGetPremium = organization.UsersGetPremium;
Enabled = organization.Enabled;
UseSso = organization.UseSso;
UseKeyConnector = organization.UseKeyConnector;
UseScim = organization.UseScim;
UseResetPassword = organization.UseResetPassword;
UseCustomPermissions = organization.UseCustomPermissions;
UsePolicies = organization.UsePolicies;
}
public Guid Id { get; set; }
public bool UseEvents { get; set; }
public bool Use2fa { get; set; }
public bool Using2fa { get; set; }
public bool UsersGetPremium { get; set; }
public bool Enabled { get; set; }
public bool UseSso { get; set; }
public bool UseKeyConnector { get; set; }
public bool UseScim { get; set; }
public bool UseResetPassword { get; set; }
public bool UseCustomPermissions { get; set; }
public bool UsePolicies { get; set; }
}

View File

@ -0,0 +1,151 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Models.OrganizationConnectionConfigs;
using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Enums;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Business;
namespace Bit.Core.Models.Data.Organizations;
public class SelfHostedOrganizationDetails : Organization
{
public int OccupiedSeatCount { get; set; }
public int CollectionCount { get; set; }
public int GroupCount { get; set; }
public IEnumerable<OrganizationUser> OrganizationUsers { get; set; }
public IEnumerable<Policy> Policies { get; set; }
public SsoConfig SsoConfig { get; set; }
public IEnumerable<OrganizationConnection> ScimConnections { get; set; }
public bool CanUseLicense(OrganizationLicense license, out string exception)
{
if (license.Seats.HasValue && OccupiedSeatCount > license.Seats.Value)
{
exception = $"Your organization currently has {OccupiedSeatCount} seats filled. " +
$"Your new license only has ({license.Seats.Value}) seats. Remove some users.";
return false;
}
if (license.MaxCollections.HasValue && CollectionCount > license.MaxCollections.Value)
{
exception = $"Your organization currently has {CollectionCount} collections. " +
$"Your new license allows for a maximum of ({license.MaxCollections.Value}) collections. " +
"Remove some collections.";
return false;
}
if (!license.UseGroups && UseGroups && GroupCount > 1)
{
exception = $"Your organization currently has {GroupCount} groups. " +
$"Your new license does not allow for the use of groups. Remove all groups.";
return false;
}
var enabledPolicyCount = Policies.Count(p => p.Enabled);
if (!license.UsePolicies && UsePolicies && enabledPolicyCount > 0)
{
exception = $"Your organization currently has {enabledPolicyCount} enabled " +
$"policies. Your new license does not allow for the use of policies. Disable all policies.";
return false;
}
if (!license.UseSso && UseSso && SsoConfig is { Enabled: true })
{
exception = $"Your organization currently has a SSO configuration. " +
$"Your new license does not allow for the use of SSO. Disable your SSO configuration.";
return false;
}
if (!license.UseKeyConnector && UseKeyConnector && SsoConfig?.Data != null &&
SsoConfig.GetData().MemberDecryptionType == MemberDecryptionType.KeyConnector)
{
exception = $"Your organization currently has Key Connector enabled. " +
$"Your new license does not allow for the use of Key Connector. Disable your Key Connector.";
return false;
}
if (!license.UseScim && UseScim && ScimConnections != null &&
ScimConnections.Any(c => c.GetConfig<ScimConfig>() is { Enabled: true }))
{
exception = "Your new plan does not allow the SCIM feature. " +
"Disable your SCIM configuration.";
return false;
}
if (!license.UseCustomPermissions && UseCustomPermissions &&
OrganizationUsers.Any(ou => ou.Type == OrganizationUserType.Custom))
{
exception = "Your new plan does not allow the Custom Permissions feature. " +
"Disable your Custom Permissions configuration.";
return false;
}
if (!license.UseResetPassword && UseResetPassword &&
Policies.Any(p => p.Type == PolicyType.ResetPassword && p.Enabled))
{
exception = "Your new license does not allow the Password Reset feature. "
+ "Disable your Password Reset policy.";
return false;
}
exception = "";
return true;
}
public Organization ToOrganization()
{
// Any new Organization properties must be added here for them to flow through to self-hosted organizations
return new Organization
{
Id = Id,
Identifier = Identifier,
Name = Name,
BusinessName = BusinessName,
BusinessAddress1 = BusinessAddress1,
BusinessAddress2 = BusinessAddress2,
BusinessAddress3 = BusinessAddress3,
BusinessCountry = BusinessCountry,
BusinessTaxNumber = BusinessTaxNumber,
BillingEmail = BillingEmail,
Plan = Plan,
PlanType = PlanType,
Seats = Seats,
MaxCollections = MaxCollections,
UsePolicies = UsePolicies,
UseSso = UseSso,
UseKeyConnector = UseKeyConnector,
UseScim = UseScim,
UseGroups = UseGroups,
UseDirectory = UseDirectory,
UseEvents = UseEvents,
UseTotp = UseTotp,
Use2fa = Use2fa,
UseApi = UseApi,
UseResetPassword = UseResetPassword,
UseSecretsManager = UseSecretsManager,
SelfHost = SelfHost,
UsersGetPremium = UsersGetPremium,
UseCustomPermissions = UseCustomPermissions,
Storage = Storage,
MaxStorageGb = MaxStorageGb,
Gateway = Gateway,
GatewayCustomerId = GatewayCustomerId,
GatewaySubscriptionId = GatewaySubscriptionId,
ReferenceData = ReferenceData,
Enabled = Enabled,
LicenseKey = LicenseKey,
PublicKey = PublicKey,
PrivateKey = PrivateKey,
TwoFactorProviders = TwoFactorProviders,
ExpirationDate = ExpirationDate,
CreationDate = CreationDate,
RevisionDate = RevisionDate,
MaxAutoscaleSeats = MaxAutoscaleSeats,
OwnersNotifiedOfAutoscaling = OwnersNotifiedOfAutoscaling,
LimitCollectionCreationDeletion = LimitCollectionCreationDeletion,
AllowAdminAccessToAllCollectionItems = AllowAdminAccessToAllCollectionItems
};
}
}

View File

@ -0,0 +1,18 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.Models.Data.Organizations;
namespace Bit.Core.Repositories;
public interface IOrganizationRepository : IRepository<Organization, Guid>
{
Task<Organization> GetByIdentifierAsync(string identifier);
Task<ICollection<Organization>> GetManyByEnabledAsync();
Task<ICollection<Organization>> GetManyByUserIdAsync(Guid userId);
Task<ICollection<Organization>> SearchAsync(string name, string userEmail, bool? paid, int skip, int take);
Task UpdateStorageAsync(Guid id);
Task<ICollection<OrganizationAbility>> GetManyAbilitiesAsync();
Task<Organization> GetByLicenseKeyAsync(string licenseKey);
Task<SelfHostedOrganizationDetails> GetSelfHostedOrganizationDetailsById(Guid id);
Task<ICollection<Organization>> SearchUnassignedToProviderAsync(string name, string ownerEmail, int skip, int take);
Task<IEnumerable<string>> GetOwnerEmailAddressesById(Guid organizationId);
}

View File

@ -0,0 +1,85 @@
using System.Security.Claims;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.Auth.Enums;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Business;
using Bit.Core.Models.Data;
namespace Bit.Core.Services;
public interface IOrganizationService
{
Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken, PaymentMethodType paymentMethodType,
TaxInfo taxInfo);
Task CancelSubscriptionAsync(Guid organizationId, bool? endOfPeriod = null);
Task ReinstateSubscriptionAsync(Guid organizationId);
Task<string> AdjustStorageAsync(Guid organizationId, short storageAdjustmentGb);
Task UpdateSubscription(Guid organizationId, int seatAdjustment, int? maxAutoscaleSeats);
Task AutoAddSeatsAsync(Organization organization, int seatsToAdd, DateTime? prorationDate = null);
Task<string> AdjustSeatsAsync(Guid organizationId, int seatAdjustment, DateTime? prorationDate = null);
Task VerifyBankAsync(Guid organizationId, int amount1, int amount2);
Task<Tuple<Organization, OrganizationUser>> SignUpAsync(OrganizationSignup organizationSignup, bool provider = false);
Task<Tuple<Organization, OrganizationUser>> SignUpAsync(OrganizationLicense license, User owner,
string ownerKey, string collectionName, string publicKey, string privateKey);
Task DeleteAsync(Organization organization);
Task EnableAsync(Guid organizationId, DateTime? expirationDate);
Task DisableAsync(Guid organizationId, DateTime? expirationDate);
Task UpdateExpirationDateAsync(Guid organizationId, DateTime? expirationDate);
Task EnableAsync(Guid organizationId);
Task UpdateAsync(Organization organization, bool updateBilling = false, EventType eventType = EventType.Organization_Updated);
Task UpdateTwoFactorProviderAsync(Organization organization, TwoFactorProviderType type);
Task DisableTwoFactorProviderAsync(Organization organization, TwoFactorProviderType type);
Task<List<OrganizationUser>> InviteUsersAsync(Guid organizationId, Guid? invitingUserId,
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites);
Task<List<OrganizationUser>> InviteUsersAsync(Guid organizationId, EventSystemUser systemUser,
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites);
Task<OrganizationUser> InviteUserAsync(Guid organizationId, Guid? invitingUserId, string email,
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<CollectionAccessSelection> collections, IEnumerable<Guid> groups);
Task<OrganizationUser> InviteUserAsync(Guid organizationId, EventSystemUser systemUser, string email,
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<CollectionAccessSelection> collections, IEnumerable<Guid> groups);
Task<IEnumerable<Tuple<OrganizationUser, string>>> ResendInvitesAsync(Guid organizationId, Guid? invitingUserId, IEnumerable<Guid> organizationUsersId);
Task ResendInviteAsync(Guid organizationId, Guid? invitingUserId, Guid organizationUserId, bool initOrganization = false);
Task<OrganizationUser> ConfirmUserAsync(Guid organizationId, Guid organizationUserId, string key,
Guid confirmingUserId, IUserService userService);
Task<List<Tuple<OrganizationUser, string>>> ConfirmUsersAsync(Guid organizationId, Dictionary<Guid, string> keys,
Guid confirmingUserId, IUserService userService);
Task SaveUserAsync(OrganizationUser user, Guid? savingUserId, IEnumerable<CollectionAccessSelection> collections, IEnumerable<Guid> groups);
[Obsolete("IDeleteOrganizationUserCommand should be used instead. To be removed by EC-607.")]
Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId);
[Obsolete("IDeleteOrganizationUserCommand should be used instead. To be removed by EC-607.")]
Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, EventSystemUser systemUser);
Task DeleteUserAsync(Guid organizationId, Guid userId);
Task<List<Tuple<OrganizationUser, string>>> DeleteUsersAsync(Guid organizationId,
IEnumerable<Guid> organizationUserIds, Guid? deletingUserId);
Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId);
Task ImportAsync(Guid organizationId, Guid? importingUserId, IEnumerable<ImportedGroup> groups,
IEnumerable<ImportedOrganizationUser> newUsers, IEnumerable<string> removeUserExternalIds,
bool overwriteExisting);
Task DeleteSsoUserAsync(Guid userId, Guid? organizationId);
Task<Organization> UpdateOrganizationKeysAsync(Guid orgId, string publicKey, string privateKey);
Task<bool> HasConfirmedOwnersExceptAsync(Guid organizationId, IEnumerable<Guid> organizationUsersId, bool includeProvider = true);
Task RevokeUserAsync(OrganizationUser organizationUser, Guid? revokingUserId);
Task RevokeUserAsync(OrganizationUser organizationUser, EventSystemUser systemUser);
Task<List<Tuple<OrganizationUser, string>>> RevokeUsersAsync(Guid organizationId,
IEnumerable<Guid> organizationUserIds, Guid? revokingUserId);
Task RestoreUserAsync(OrganizationUser organizationUser, Guid? restoringUserId, IUserService userService);
Task RestoreUserAsync(OrganizationUser organizationUser, EventSystemUser systemUser, IUserService userService);
Task<List<Tuple<OrganizationUser, string>>> RestoreUsersAsync(Guid organizationId,
IEnumerable<Guid> organizationUserIds, Guid? restoringUserId, IUserService userService);
Task CreatePendingOrganization(Organization organization, string ownerEmail, ClaimsPrincipal user, IUserService userService, bool salesAssistedTrialStarted);
/// <summary>
/// Update an Organization entry by setting the public/private keys, set it as 'Enabled' and move the Status from 'Pending' to 'Created'.
/// </summary>
/// <remarks>
/// This method must target a disabled Organization that has null keys and status as 'Pending'.
/// </remarks>
Task InitPendingOrganization(Guid userId, Guid organizationId, string publicKey, string privateKey, string collectionName);
Task ReplaceAndUpdateCacheAsync(Organization org, EventType? orgEvent = null);
void ValidatePasswordManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade);
void ValidateSecretsManagerPlan(Models.StaticStore.Plan plan, OrganizationUpgrade upgrade);
Task ValidateOrganizationUserUpdatePermissions(Guid organizationId, OrganizationUserType newType,
OrganizationUserType? oldType, Permissions permissions);
}

File diff suppressed because it is too large Load Diff