mirror of
https://github.com/bitwarden/server.git
synced 2025-07-02 16:42:50 -05:00
[Provider] Create and access child organizations (#1427)
This commit is contained in:
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Table;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@ -11,6 +12,7 @@ namespace Bit.Core.Services
|
||||
Task<IDictionary<Guid, OrganizationAbility>> GetOrganizationAbilitiesAsync();
|
||||
Task<IDictionary<Guid, ProviderAbility>> GetProviderAbilitiesAsync();
|
||||
Task UpsertOrganizationAbilityAsync(Organization organization);
|
||||
Task UpsertProviderAbilityAsync(Provider provider);
|
||||
Task DeleteOrganizationAbilityAsync(Guid organizationId);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ namespace Bit.Core.Services
|
||||
Task<string> AdjustStorageAsync(Guid organizationId, short storageAdjustmentGb);
|
||||
Task<string> AdjustSeatsAsync(Guid organizationId, int seatAdjustment);
|
||||
Task VerifyBankAsync(Guid organizationId, int amount1, int amount2);
|
||||
Task<Tuple<Organization, OrganizationUser>> SignUpAsync(OrganizationSignup organizationSignup);
|
||||
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 UpdateLicenseAsync(Guid organizationId, OrganizationLicense license);
|
||||
|
@ -2,6 +2,7 @@
|
||||
using Bit.Core.Models.Table;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Models.Business.Provider;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
|
||||
@ -24,8 +25,7 @@ namespace Bit.Core.Services
|
||||
Guid deletingUserId);
|
||||
|
||||
Task AddOrganization(Guid providerId, Guid organizationId, Guid addingUserId, string key);
|
||||
Task<ProviderOrganization> CreateOrganizationAsync(Guid providerId, OrganizationSignup organizationSignup, User user);
|
||||
Task RemoveOrganization(Guid providerOrganizationId, Guid removingUserId);
|
||||
|
||||
// TODO: Figure out how ProviderOrganizationProviderUsers should be managed
|
||||
}
|
||||
}
|
||||
|
@ -297,7 +297,7 @@ namespace Bit.Core.Services
|
||||
grantor.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>());
|
||||
await _userRepository.ReplaceAsync(grantor);
|
||||
|
||||
// Remove grantor from all organisations unless Owner
|
||||
// Remove grantor from all organizations unless Owner
|
||||
var orgUser = await _organizationUserRepository.GetManyByUserAsync(grantor.Id);
|
||||
foreach (var o in orgUser)
|
||||
{
|
||||
|
@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Table;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
using Bit.Core.Repositories;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
@ -36,6 +37,21 @@ namespace Bit.Core.Services
|
||||
await InitProviderAbilitiesAsync();
|
||||
return _providerAbilities;
|
||||
}
|
||||
|
||||
public virtual async Task UpsertProviderAbilityAsync(Provider provider)
|
||||
{
|
||||
await InitProviderAbilitiesAsync();
|
||||
var newAbility = new ProviderAbility(provider);
|
||||
|
||||
if (_providerAbilities.ContainsKey(provider.Id))
|
||||
{
|
||||
_providerAbilities[provider.Id] = newAbility;
|
||||
}
|
||||
else
|
||||
{
|
||||
_providerAbilities.Add(provider.Id, newAbility);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual async Task UpsertOrganizationAbilityAsync(Organization organization)
|
||||
{
|
||||
|
@ -552,15 +552,25 @@ namespace Bit.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Tuple<Organization, OrganizationUser>> SignUpAsync(OrganizationSignup signup)
|
||||
public async Task<Tuple<Organization, OrganizationUser>> SignUpAsync(OrganizationSignup signup,
|
||||
bool provider = false)
|
||||
{
|
||||
var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == signup.Plan && !p.Disabled);
|
||||
if (plan == null)
|
||||
var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == signup.Plan);
|
||||
if (!(plan is {LegacyYear: null}))
|
||||
{
|
||||
throw new BadRequestException("Invalid plan selected.");
|
||||
}
|
||||
|
||||
if (plan.Disabled)
|
||||
{
|
||||
throw new BadRequestException("Plan not found.");
|
||||
}
|
||||
|
||||
await ValidateSignUpPoliciesAsync(signup.Owner.Id);
|
||||
if (!provider)
|
||||
{
|
||||
await ValidateSignUpPoliciesAsync(signup.Owner.Id);
|
||||
}
|
||||
|
||||
ValidateOrganizationUpgradeParameters(plan, signup);
|
||||
|
||||
var organization = new Organization
|
||||
@ -598,7 +608,7 @@ namespace Bit.Core.Services
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
if (plan.Type == PlanType.Free)
|
||||
if (plan.Type == PlanType.Free && !provider)
|
||||
{
|
||||
var adminCount =
|
||||
await _organizationUserRepository.GetCountByFreeOrganizationAdminUserAsync(signup.Owner.Id);
|
||||
@ -607,14 +617,15 @@ namespace Bit.Core.Services
|
||||
throw new BadRequestException("You can only be an admin of one free organization.");
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (plan.Type != PlanType.Free)
|
||||
{
|
||||
await _paymentService.PurchaseOrganizationAsync(organization, signup.PaymentMethodType.Value,
|
||||
signup.PaymentToken, plan, signup.AdditionalStorageGb, signup.AdditionalSeats,
|
||||
signup.PremiumAccessAddon, signup.TaxInfo);
|
||||
}
|
||||
|
||||
var returnValue = await SignUpAsync(organization, signup.Owner.Id, signup.OwnerKey, signup.CollectionName, true);
|
||||
var ownerId = provider ? default : signup.Owner.Id;
|
||||
var returnValue = await SignUpAsync(organization, ownerId, signup.OwnerKey, signup.CollectionName, true);
|
||||
await _referenceEventService.RaiseEventAsync(
|
||||
new ReferenceEvent(ReferenceEventType.Signup, organization)
|
||||
{
|
||||
@ -725,20 +736,6 @@ namespace Bit.Core.Services
|
||||
await _organizationRepository.CreateAsync(organization);
|
||||
await _applicationCacheService.UpsertOrganizationAbilityAsync(organization);
|
||||
|
||||
var orgUser = new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = ownerId,
|
||||
Key = ownerKey,
|
||||
Type = OrganizationUserType.Owner,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
AccessAll = true,
|
||||
CreationDate = organization.CreationDate,
|
||||
RevisionDate = organization.CreationDate
|
||||
};
|
||||
|
||||
await _organizationUserRepository.CreateAsync(orgUser);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(collectionName))
|
||||
{
|
||||
var defaultCollection = new Collection
|
||||
@ -751,11 +748,28 @@ namespace Bit.Core.Services
|
||||
await _collectionRepository.CreateAsync(defaultCollection);
|
||||
}
|
||||
|
||||
// push
|
||||
var deviceIds = await GetUserDeviceIdsAsync(orgUser.UserId.Value);
|
||||
await _pushRegistrationService.AddUserRegistrationOrganizationAsync(deviceIds,
|
||||
organization.Id.ToString());
|
||||
await _pushNotificationService.PushSyncOrgKeysAsync(ownerId);
|
||||
OrganizationUser orgUser = null;
|
||||
if (ownerId != default)
|
||||
{
|
||||
orgUser = new OrganizationUser
|
||||
{
|
||||
OrganizationId = organization.Id,
|
||||
UserId = ownerId,
|
||||
Key = ownerKey,
|
||||
Type = OrganizationUserType.Owner,
|
||||
Status = OrganizationUserStatusType.Confirmed,
|
||||
AccessAll = true,
|
||||
CreationDate = organization.CreationDate,
|
||||
RevisionDate = organization.CreationDate
|
||||
};
|
||||
|
||||
await _organizationUserRepository.CreateAsync(orgUser);
|
||||
|
||||
var deviceIds = await GetUserDeviceIdsAsync(orgUser.UserId.Value);
|
||||
await _pushRegistrationService.AddUserRegistrationOrganizationAsync(deviceIds,
|
||||
organization.Id.ToString());
|
||||
await _pushNotificationService.PushSyncOrgKeysAsync(ownerId);
|
||||
}
|
||||
|
||||
return new Tuple<Organization, OrganizationUser>(organization, orgUser);
|
||||
}
|
||||
@ -1051,7 +1065,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
foreach (var type in inviteTypes)
|
||||
{
|
||||
ValidateOrganizationUserUpdatePermissions(organizationId, type, null);
|
||||
await ValidateOrganizationUserUpdatePermissions(organizationId, type, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1158,7 +1172,7 @@ namespace Bit.Core.Services
|
||||
|
||||
if (invitingUserId.HasValue && invite.Type.HasValue)
|
||||
{
|
||||
ValidateOrganizationUserUpdatePermissions(organizationId, invite.Type.Value, null);
|
||||
await ValidateOrganizationUserUpdatePermissions(organizationId, invite.Type.Value, null);
|
||||
}
|
||||
|
||||
if (organization.Seats.HasValue)
|
||||
@ -1172,6 +1186,12 @@ namespace Bit.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
var invitedIsOwner = invite.Type is OrganizationUserType.Owner;
|
||||
if (!invitedIsOwner && !await HasConfirmedOwnersExceptAsync(organizationId, new Guid[] {}))
|
||||
{
|
||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||
}
|
||||
|
||||
var orgUsers = new List<OrganizationUser>();
|
||||
var orgUserInvitedCount = 0;
|
||||
foreach (var email in invite.Emails)
|
||||
@ -1532,7 +1552,7 @@ namespace Bit.Core.Services
|
||||
|
||||
if (savingUserId.HasValue)
|
||||
{
|
||||
ValidateOrganizationUserUpdatePermissions(user.OrganizationId, user.Type, originalUser.Type);
|
||||
await ValidateOrganizationUserUpdatePermissions(user.OrganizationId, user.Type, originalUser.Type);
|
||||
}
|
||||
|
||||
if (user.Type != OrganizationUserType.Owner &&
|
||||
@ -1564,7 +1584,7 @@ namespace Bit.Core.Services
|
||||
}
|
||||
|
||||
if (orgUser.Type == OrganizationUserType.Owner && deletingUserId.HasValue &&
|
||||
!_currentContext.OrganizationOwner(organizationId))
|
||||
!await _currentContext.OrganizationOwner(organizationId))
|
||||
{
|
||||
throw new BadRequestException("Only owners can delete other owners.");
|
||||
}
|
||||
@ -1626,7 +1646,7 @@ namespace Bit.Core.Services
|
||||
var deletingUserIsOwner = false;
|
||||
if (deletingUserId.HasValue)
|
||||
{
|
||||
deletingUserIsOwner = _currentContext.OrganizationOwner(organizationId);
|
||||
deletingUserIsOwner = await _currentContext.OrganizationOwner(organizationId);
|
||||
}
|
||||
|
||||
var result = new List<Tuple<OrganizationUser, string>>();
|
||||
@ -1676,7 +1696,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
if (loggedInUserId.HasValue)
|
||||
{
|
||||
ValidateOrganizationUserUpdatePermissions(organizationUser.OrganizationId, organizationUser.Type, null);
|
||||
await ValidateOrganizationUserUpdatePermissions(organizationUser.OrganizationId, organizationUser.Type, null);
|
||||
}
|
||||
await _organizationUserRepository.UpdateGroupsAsync(organizationUser.Id, groupIds);
|
||||
await _eventService.LogOrganizationUserEventAsync(organizationUser,
|
||||
@ -1961,7 +1981,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<Organization> UpdateOrganizationKeysAsync(Guid orgId, string publicKey, string privateKey)
|
||||
{
|
||||
if (!_currentContext.ManageResetPassword(orgId))
|
||||
if (!await _currentContext.ManageResetPassword(orgId))
|
||||
{
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
@ -1972,7 +1992,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
throw new BadRequestException("Organization Keys already exist");
|
||||
}
|
||||
|
||||
|
||||
// Update org with generated public/private key
|
||||
org.PublicKey = publicKey;
|
||||
org.PrivateKey = privateKey;
|
||||
@ -2072,10 +2092,10 @@ namespace Bit.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateOrganizationUserUpdatePermissions(Guid organizationId, OrganizationUserType newType,
|
||||
private async Task ValidateOrganizationUserUpdatePermissions(Guid organizationId, OrganizationUserType newType,
|
||||
OrganizationUserType? oldType)
|
||||
{
|
||||
if (_currentContext.OrganizationOwner(organizationId))
|
||||
if (await _currentContext.OrganizationOwner(organizationId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -2085,7 +2105,7 @@ namespace Bit.Core.Services
|
||||
throw new BadRequestException("Only an Owner can configure another Owner's account.");
|
||||
}
|
||||
|
||||
if (_currentContext.OrganizationAdmin(organizationId))
|
||||
if (await _currentContext.OrganizationAdmin(organizationId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -2095,7 +2115,7 @@ namespace Bit.Core.Services
|
||||
throw new BadRequestException("Only Owners and Admins can configure Custom accounts.");
|
||||
}
|
||||
|
||||
if (!_currentContext.ManageUsers(organizationId))
|
||||
if (!await _currentContext.ManageUsers(organizationId))
|
||||
{
|
||||
throw new BadRequestException("Your account does not have permission to manage users.");
|
||||
}
|
||||
|
@ -284,7 +284,7 @@ namespace Bit.Core.Services
|
||||
|
||||
foreach (var policy in policies.Where(p => p.Enabled && p.Type == PolicyType.DisableSend))
|
||||
{
|
||||
if (!_currentContext.ManagePolicies(policy.OrganizationId))
|
||||
if (!await _currentContext.ManagePolicies(policy.OrganizationId))
|
||||
{
|
||||
throw new BadRequestException("Due to an Enterprise Policy, you are only able to delete an existing Send.");
|
||||
}
|
||||
@ -292,8 +292,13 @@ namespace Bit.Core.Services
|
||||
|
||||
if (send.HideEmail.GetValueOrDefault())
|
||||
{
|
||||
foreach (var policy in policies.Where(p => p.Enabled && p.Type == PolicyType.SendOptions && !_currentContext.ManagePolicies(p.OrganizationId)))
|
||||
foreach (var policy in policies.Where(p => p.Enabled && p.Type == PolicyType.SendOptions))
|
||||
{
|
||||
if (await _currentContext.ManagePolicies(policy.OrganizationId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SendOptionsPolicyData data = null;
|
||||
if (policy.Data != null)
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Models.Business.Provider;
|
||||
using Bit.Core.Models.Table;
|
||||
using Bit.Core.Models.Table.Provider;
|
||||
@ -28,6 +29,7 @@ namespace Bit.Core.Services
|
||||
public Task<List<Tuple<ProviderUser, string>>> DeleteUsersAsync(Guid providerId, IEnumerable<Guid> providerUserIds, Guid deletingUserId) => throw new NotImplementedException();
|
||||
|
||||
public Task AddOrganization(Guid providerId, Guid organizationId, Guid addingUserId, string key) => throw new NotImplementedException();
|
||||
public Task<ProviderOrganization> CreateOrganizationAsync(Guid providerId, OrganizationSignup organizationSignup, User user) => throw new NotImplementedException();
|
||||
|
||||
public Task RemoveOrganization(Guid providerOrganizationId, Guid removingUserId) => throw new NotImplementedException();
|
||||
}
|
||||
|
Reference in New Issue
Block a user