1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-01 08:02:49 -05:00

[Provider] Create and access child organizations (#1427)

This commit is contained in:
Oscar Hinton
2021-07-08 17:05:32 +02:00
committed by GitHub
parent a6128c781a
commit feb3106f37
51 changed files with 756 additions and 232 deletions

View File

@ -10,14 +10,17 @@ using System.Security.Claims;
using Bit.Core.Enums.Provider;
using Bit.Core.Utilities;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table.Provider;
using Bit.Core.Settings;
namespace Bit.Core.Context
{
public class CurrentContext : ICurrentContext
{
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
private bool _builtHttpContext;
private bool _builtClaimsPrincipal;
private ICollection<ProviderOrganization> _providerOrganizations;
public virtual HttpContext HttpContext { get; set; }
public virtual Guid? UserId { get; set; }
@ -34,6 +37,11 @@ namespace Bit.Core.Context
public virtual bool MaybeBot { get; set; }
public virtual int? BotScore { get; set; }
public CurrentContext(IProviderOrganizationRepository providerOrganizationRepository)
{
_providerOrganizationRepository = providerOrganizationRepository;
}
public async virtual Task BuildAsync(HttpContext httpContext, GlobalSettings globalSettings)
{
if (_builtHttpContext)
@ -197,7 +205,7 @@ namespace Bit.Core.Context
Permissions = SetOrganizationPermissionsFromClaims(c.Value, claimsDict)
}));
}
return organizations;
}
@ -227,97 +235,106 @@ namespace Bit.Core.Context
return providers;
}
public bool OrganizationUser(Guid orgId)
public async Task<bool> OrganizationUser(Guid orgId)
{
return Organizations?.Any(o => o.Id == orgId) ?? false;
return (Organizations?.Any(o => o.Id == orgId) ?? false) || await OrganizationOwner(orgId);
}
public bool OrganizationManager(Guid orgId)
public async Task<bool> OrganizationManager(Guid orgId)
{
return Organizations?.Any(o => o.Id == orgId &&
(o.Type == OrganizationUserType.Owner || o.Type == OrganizationUserType.Admin ||
o.Type == OrganizationUserType.Manager)) ?? false;
return await OrganizationAdmin(orgId) ||
(Organizations?.Any(o => o.Id == orgId && o.Type == OrganizationUserType.Manager) ?? false);
}
public bool OrganizationAdmin(Guid orgId)
public async Task<bool> OrganizationAdmin(Guid orgId)
{
return Organizations?.Any(o => o.Id == orgId &&
(o.Type == OrganizationUserType.Owner || o.Type == OrganizationUserType.Admin)) ?? false;
return await OrganizationOwner(orgId) ||
(Organizations?.Any(o => o.Id == orgId && o.Type == OrganizationUserType.Admin) ?? false);
}
public bool OrganizationOwner(Guid orgId)
public async Task<bool> OrganizationOwner(Guid orgId)
{
return Organizations?.Any(o => o.Id == orgId && o.Type == OrganizationUserType.Owner) ?? false;
if (Organizations?.Any(o => o.Id == orgId && o.Type == OrganizationUserType.Owner) ?? false)
{
return true;
}
if (Providers.Any())
{
return (await GetProviderOrganizations()).Any(po => po.OrganizationId == orgId);
}
return false;
}
public bool OrganizationCustom(Guid orgId)
public Task<bool> OrganizationCustom(Guid orgId)
{
return Organizations?.Any(o => o.Id == orgId && o.Type == OrganizationUserType.Custom) ?? false;
return Task.FromResult(Organizations?.Any(o => o.Id == orgId && o.Type == OrganizationUserType.Custom) ?? false);
}
public bool AccessBusinessPortal(Guid orgId)
public async Task<bool> AccessBusinessPortal(Guid orgId)
{
return OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
return await OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
&& (o.Permissions?.AccessBusinessPortal ?? false)) ?? false);
}
public bool AccessEventLogs(Guid orgId)
public async Task<bool> AccessEventLogs(Guid orgId)
{
return OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
return await OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
&& (o.Permissions?.AccessEventLogs ?? false)) ?? false);
}
public bool AccessImportExport(Guid orgId)
public async Task<bool> AccessImportExport(Guid orgId)
{
return OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
return await OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
&& (o.Permissions?.AccessImportExport ?? false)) ?? false);
}
public bool AccessReports(Guid orgId)
public async Task<bool> AccessReports(Guid orgId)
{
return OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
return await OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
&& (o.Permissions?.AccessReports ?? false)) ?? false);
}
public bool ManageAllCollections(Guid orgId)
public async Task<bool> ManageAllCollections(Guid orgId)
{
return OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
return await OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
&& (o.Permissions?.ManageAllCollections ?? false)) ?? false);
}
public bool ManageAssignedCollections(Guid orgId)
public async Task<bool> ManageAssignedCollections(Guid orgId)
{
return OrganizationManager(orgId) || (Organizations?.Any(o => o.Id == orgId
return await OrganizationManager(orgId) || (Organizations?.Any(o => o.Id == orgId
&& (o.Permissions?.ManageAssignedCollections ?? false)) ?? false);
}
public bool ManageGroups(Guid orgId)
public async Task<bool> ManageGroups(Guid orgId)
{
return OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
return await OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
&& (o.Permissions?.ManageGroups ?? false)) ?? false);
}
public bool ManagePolicies(Guid orgId)
public async Task<bool> ManagePolicies(Guid orgId)
{
return OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
return await OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
&& (o.Permissions?.ManagePolicies ?? false)) ?? false);
}
public bool ManageSso(Guid orgId)
public async Task<bool> ManageSso(Guid orgId)
{
return OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
return await OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
&& (o.Permissions?.ManageSso ?? false)) ?? false);
}
public bool ManageUsers(Guid orgId)
public async Task<bool> ManageUsers(Guid orgId)
{
return OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
return await OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
&& (o.Permissions?.ManageUsers ?? false)) ?? false);
}
public bool ManageResetPassword(Guid orgId)
public async Task<bool> ManageResetPassword(Guid orgId)
{
return OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
return await OrganizationAdmin(orgId) || (Organizations?.Any(o => o.Id == orgId
&& (o.Permissions?.ManageResetPassword ?? false)) ?? false);
}
@ -403,5 +420,15 @@ namespace Bit.Core.Context
ManageResetPassword = hasClaim("manageresetpassword")
};
}
private async Task<ICollection<ProviderOrganization>> GetProviderOrganizations()
{
if (_providerOrganizations == null)
{
_providerOrganizations = await _providerOrganizationRepository.GetManyByUserIdAsync(UserId.Value);
}
return _providerOrganizations;
}
}
}

View File

@ -31,22 +31,22 @@ namespace Bit.Core.Context
Task SetContextAsync(ClaimsPrincipal user);
bool OrganizationUser(Guid orgId);
bool OrganizationManager(Guid orgId);
bool OrganizationAdmin(Guid orgId);
bool OrganizationOwner(Guid orgId);
bool OrganizationCustom(Guid orgId);
bool AccessBusinessPortal(Guid orgId);
bool AccessEventLogs(Guid orgId);
bool AccessImportExport(Guid orgId);
bool AccessReports(Guid orgId);
bool ManageAllCollections(Guid orgId);
bool ManageAssignedCollections(Guid orgId);
bool ManageGroups(Guid orgId);
bool ManagePolicies(Guid orgId);
bool ManageSso(Guid orgId);
bool ManageUsers(Guid orgId);
bool ManageResetPassword(Guid orgId);
Task<bool> OrganizationUser(Guid orgId);
Task<bool> OrganizationManager(Guid orgId);
Task<bool> OrganizationAdmin(Guid orgId);
Task<bool> OrganizationOwner(Guid orgId);
Task<bool> OrganizationCustom(Guid orgId);
Task<bool> AccessBusinessPortal(Guid orgId);
Task<bool> AccessEventLogs(Guid orgId);
Task<bool> AccessImportExport(Guid orgId);
Task<bool> AccessReports(Guid orgId);
Task<bool> ManageAllCollections(Guid orgId);
Task<bool> ManageAssignedCollections(Guid orgId);
Task<bool> ManageGroups(Guid orgId);
Task<bool> ManagePolicies(Guid orgId);
Task<bool> ManageSso(Guid orgId);
Task<bool> ManageUsers(Guid orgId);
Task<bool> ManageResetPassword(Guid orgId);
bool ProviderProviderAdmin(Guid providerId);
bool ProviderUser(Guid providerId);
bool ManageProviderUsers(Guid providerId);

View File

@ -26,6 +26,7 @@ namespace Bit.Core.IdentityServer
private readonly ICurrentContext _currentContext;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IProviderUserRepository _providerUserRepository;
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
public ClientStore(
IInstallationRepository installationRepository,
@ -36,7 +37,8 @@ namespace Bit.Core.IdentityServer
ILicensingService licensingService,
ICurrentContext currentContext,
IOrganizationUserRepository organizationUserRepository,
IProviderUserRepository providerUserRepository)
IProviderUserRepository providerUserRepository,
IProviderOrganizationRepository providerOrganizationRepository)
{
_installationRepository = installationRepository;
_organizationRepository = organizationRepository;
@ -47,6 +49,7 @@ namespace Bit.Core.IdentityServer
_currentContext = currentContext;
_organizationUserRepository = organizationUserRepository;
_providerUserRepository = providerUserRepository;
_providerOrganizationRepository = providerOrganizationRepository;
}
public async Task<Client> FindClientByIdAsync(string clientId)

View File

@ -19,6 +19,7 @@ namespace Bit.Core.IdentityServer
private readonly IUserService _userService;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IProviderUserRepository _providerUserRepository;
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
private readonly ILicensingService _licensingService;
private readonly ICurrentContext _currentContext;
@ -26,12 +27,14 @@ namespace Bit.Core.IdentityServer
IUserService userService,
IOrganizationUserRepository organizationUserRepository,
IProviderUserRepository providerUserRepository,
IProviderOrganizationRepository providerOrganizationRepository,
ILicensingService licensingService,
ICurrentContext currentContext)
{
_userService = userService;
_organizationUserRepository = organizationUserRepository;
_providerUserRepository = providerUserRepository;
_providerOrganizationRepository = providerOrganizationRepository;
_licensingService = licensingService;
_currentContext = currentContext;
}

View File

@ -0,0 +1,72 @@
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Api
{
public class ProfileProviderOrganizationResponseModel : ResponseModel
{
public ProfileProviderOrganizationResponseModel(ProviderUserOrganizationDetails organization)
: base("profileProviderOrganization")
{
Id = organization.OrganizationId.ToString();
Name = organization.Name;
UsePolicies = organization.UsePolicies;
UseSso = organization.UseSso;
UseGroups = organization.UseGroups;
UseDirectory = organization.UseDirectory;
UseEvents = organization.UseEvents;
UseTotp = organization.UseTotp;
Use2fa = organization.Use2fa;
UseApi = organization.UseApi;
UseResetPassword = organization.UseResetPassword;
UsersGetPremium = organization.UsersGetPremium;
SelfHost = organization.SelfHost;
Seats = organization.Seats;
MaxCollections = organization.MaxCollections;
MaxStorageGb = organization.MaxStorageGb;
Key = organization.Key;
HasPublicAndPrivateKeys = organization.PublicKey != null && organization.PrivateKey != null;
Status = organization.Status;
Type = organization.Type;
Enabled = organization.Enabled;
SsoBound = !string.IsNullOrWhiteSpace(organization.SsoExternalId);
Identifier = organization.Identifier;
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(organization.Permissions);
ResetPasswordEnrolled = organization.ResetPasswordKey != null;
UserId = organization.UserId?.ToString();
ProviderId = organization.ProviderId?.ToString();
ProviderName = organization.ProviderName;
}
public string Id { get; set; }
public string Name { get; set; }
public bool UsePolicies { get; set; }
public bool UseSso { get; set; }
public bool UseGroups { get; set; }
public bool UseDirectory { get; set; }
public bool UseEvents { get; set; }
public bool UseTotp { get; set; }
public bool Use2fa { get; set; }
public bool UseApi { get; set; }
public bool UseResetPassword { get; set; }
public bool UseBusinessPortal => UsePolicies || UseSso; // TODO add events if needed
public bool UsersGetPremium { get; set; }
public bool SelfHost { get; set; }
public int Seats { get; set; }
public int MaxCollections { get; set; }
public short? MaxStorageGb { get; set; }
public string Key { get; set; }
public OrganizationUserStatusType Status { get; set; }
public OrganizationUserType Type { get; set; }
public bool Enabled { get; set; }
public bool SsoBound { get; set; }
public string Identifier { get; set; }
public Permissions Permissions { get; set; }
public bool ResetPasswordEnrolled { get; set; }
public string UserId { get; set; }
public bool HasPublicAndPrivateKeys { get; set; }
public string ProviderId { get; set; }
public string ProviderName { get; set; }
}
}

View File

@ -3,7 +3,6 @@ using Bit.Core.Models.Table;
using System.Collections.Generic;
using System.Linq;
using Bit.Core.Models.Data;
using Bit.Core.Services;
namespace Bit.Core.Models.Api
{
@ -11,7 +10,9 @@ namespace Bit.Core.Models.Api
{
public ProfileResponseModel(User user,
IEnumerable<OrganizationUserOrganizationDetails> organizationsUserDetails,
IEnumerable<ProviderUserProviderDetails> providerUserDetails, bool twoFactorEnabled) : base("profile")
IEnumerable<ProviderUserProviderDetails> providerUserDetails,
IEnumerable<ProviderUserOrganizationDetails> providerUserOrganizationDetails,
bool twoFactorEnabled) : base("profile")
{
if (user == null)
{
@ -30,7 +31,9 @@ namespace Bit.Core.Models.Api
PrivateKey = user.PrivateKey;
SecurityStamp = user.SecurityStamp;
Organizations = organizationsUserDetails?.Select(o => new ProfileOrganizationResponseModel(o));
Providers = providerUserDetails?.Select(p => new ProfileProviderResponseModel(p));
Providers = providerUserDetails?.Where(p => p.Enabled).Select(p => new ProfileProviderResponseModel(p));
ProviderOrganizations =
providerUserOrganizationDetails?.Select(po => new ProfileProviderOrganizationResponseModel(po));
}
public string Id { get; set; }
@ -46,5 +49,6 @@ namespace Bit.Core.Models.Api
public string SecurityStamp { get; set; }
public IEnumerable<ProfileOrganizationResponseModel> Organizations { get; set; }
public IEnumerable<ProfileProviderResponseModel> Providers { get; set; }
public IEnumerable<ProfileProviderOrganizationResponseModel> ProviderOrganizations { get; set; }
}
}

View File

@ -17,6 +17,7 @@ namespace Bit.Core.Models.Api
Enabled = provider.Enabled;
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(provider.Permissions);
UserId = provider.UserId?.ToString();
UseEvents = provider.UseEvents;
}
public string Id { get; set; }
@ -27,5 +28,6 @@ namespace Bit.Core.Models.Api
public bool Enabled { get; set; }
public Permissions Permissions { get; set; }
public string UserId { get; set; }
public bool UseEvents { get; set; }
}
}

View File

@ -1,22 +1,39 @@
using System;
using Bit.Core.Models.Data;
using Bit.Core.Models.Table.Provider;
namespace Bit.Core.Models.Api
{
public class ProviderOrganizationOrganizationDetailsResponseModel : ResponseModel
public class ProviderOrganizationResponseModel : ResponseModel
{
public ProviderOrganizationOrganizationDetailsResponseModel(ProviderOrganizationOrganizationDetails providerOrganization,
public ProviderOrganizationResponseModel(ProviderOrganization providerOrganization,
string obj = "providerOrganization") : base(obj)
{
if (providerOrganization == null)
{
throw new ArgumentNullException(nameof(providerOrganization));
}
Id = providerOrganization.Id;
ProviderId = providerOrganization.ProviderId;
OrganizationId = providerOrganization.OrganizationId;
Key = providerOrganization.Key;
Settings = providerOrganization.Settings;
CreationDate = providerOrganization.CreationDate;
RevisionDate = providerOrganization.RevisionDate;
}
public ProviderOrganizationResponseModel(ProviderOrganizationOrganizationDetails providerOrganization,
string obj = "providerOrganization") : base(obj)
{
if (providerOrganization == null)
{
throw new ArgumentNullException(nameof(providerOrganization));
}
Id = providerOrganization.Id;
ProviderId = providerOrganization.ProviderId;
OrganizationId = providerOrganization.OrganizationId;
OrganizationName = providerOrganization.OrganizationName;
Key = providerOrganization.Key;
Settings = providerOrganization.Settings;
CreationDate = providerOrganization.CreationDate;
@ -26,10 +43,25 @@ namespace Bit.Core.Models.Api
public Guid Id { get; set; }
public Guid ProviderId { get; set; }
public Guid OrganizationId { get; set; }
public string OrganizationName { get; set; }
public string Key { get; set; }
public string Settings { get; set; }
public DateTime CreationDate { get; set; }
public DateTime RevisionDate { get; set; }
}
public class ProviderOrganizationOrganizationDetailsResponseModel : ProviderOrganizationResponseModel
{
public ProviderOrganizationOrganizationDetailsResponseModel(ProviderOrganizationOrganizationDetails providerOrganization,
string obj = "providerOrganizationOrganizationDetail") : base(providerOrganization, obj)
{
if (providerOrganization == null)
{
throw new ArgumentNullException(nameof(providerOrganization));
}
OrganizationName = providerOrganization.OrganizationName;
}
public string OrganizationName { get; set; }
}
}

View File

@ -16,6 +16,7 @@ namespace Bit.Core.Models.Api
bool userTwoFactorEnabled,
IEnumerable<OrganizationUserOrganizationDetails> organizationUserDetails,
IEnumerable<ProviderUserProviderDetails> providerUserDetails,
IEnumerable<ProviderUserOrganizationDetails> providerUserOrganizationDetails,
IEnumerable<Folder> folders,
IEnumerable<CollectionDetails> collections,
IEnumerable<CipherDetails> ciphers,
@ -25,7 +26,8 @@ namespace Bit.Core.Models.Api
IEnumerable<Send> sends)
: base("sync")
{
Profile = new ProfileResponseModel(user, organizationUserDetails, providerUserDetails, userTwoFactorEnabled);
Profile = new ProfileResponseModel(user, organizationUserDetails, providerUserDetails,
providerUserOrganizationDetails, userTwoFactorEnabled);
Folders = folders.Select(f => new FolderResponseModel(f));
Ciphers = ciphers.Select(c => new CipherDetailsResponseModel(c, globalSettings, collectionCiphersDict));
Collections = collections?.Select(

View File

@ -0,0 +1,38 @@
using System;
namespace Bit.Core.Models.Data
{
public class ProviderUserOrganizationDetails
{
public Guid OrganizationId { get; set; }
public Guid? UserId { get; set; }
public string Name { get; set; }
public bool UsePolicies { get; set; }
public bool UseSso { get; set; }
public bool UseGroups { get; set; }
public bool UseDirectory { get; set; }
public bool UseEvents { get; set; }
public bool UseTotp { get; set; }
public bool Use2fa { get; set; }
public bool UseApi{ get; set; }
public bool UseResetPassword { get; set; }
public bool UseBusinessPortal => UsePolicies || UseSso;
public bool SelfHost { get; set; }
public bool UsersGetPremium { get; set; }
public int Seats { get; set; }
public int MaxCollections { get; set; }
public short? MaxStorageGb { get; set; }
public string Key { get; set; }
public Enums.OrganizationUserStatusType Status { get; set; }
public Enums.OrganizationUserType Type { get; set; }
public bool Enabled { get; set; }
public string SsoExternalId { get; set; }
public string Identifier { get; set; }
public string Permissions { get; set; }
public string ResetPasswordKey { get; set; }
public string PublicKey { get; set; }
public string PrivateKey { get; set; }
public Guid? ProviderId { get; set; }
public string ProviderName { get; set; }
}
}

View File

@ -13,5 +13,6 @@ namespace Bit.Core.Models.Data
public ProviderUserType Type { get; set; }
public bool Enabled { get; set; }
public string Permissions { get; set; }
public bool UseEvents { get; set; }
}
}

View File

@ -9,5 +9,6 @@ namespace Bit.Core.Repositories
public interface IProviderOrganizationRepository : IRepository<ProviderOrganization, Guid>
{
Task<ICollection<ProviderOrganizationOrganizationDetails>> GetManyDetailsByProviderAsync(Guid providerId);
Task<ICollection<ProviderOrganization>> GetManyByUserIdAsync(Guid userId);
}
}

View File

@ -17,6 +17,7 @@ namespace Bit.Core.Repositories
Task<ICollection<ProviderUserUserDetails>> GetManyDetailsByProviderAsync(Guid providerId);
Task<ICollection<ProviderUserProviderDetails>> GetManyDetailsByUserAsync(Guid userId,
ProviderUserStatusType? status = null);
Task<IEnumerable<ProviderUserOrganizationDetails>> GetManyOrganizationDetailsByUserAsync(Guid userId, ProviderUserStatusType? status = null);
Task DeleteManyAsync(IEnumerable<Guid> userIds);
Task<IEnumerable<ProviderUserPublicKey>> GetManyPublicKeysByProviderUserAsync(Guid providerId, IEnumerable<Guid> Ids);
}

View File

@ -33,5 +33,18 @@ namespace Bit.Core.Repositories.SqlServer
return results.ToList();
}
}
public async Task<ICollection<ProviderOrganization>> GetManyByUserIdAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<ProviderOrganization>(
"[dbo].[ProviderOrganization_ReadByUserId]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
}
}

View File

@ -115,6 +115,20 @@ namespace Bit.Core.Repositories.SqlServer
}
}
public async Task<IEnumerable<ProviderUserOrganizationDetails>> GetManyOrganizationDetailsByUserAsync(Guid userId,
ProviderUserStatusType? status = null)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<ProviderUserOrganizationDetails>(
"[dbo].[ProviderUserProviderOrganizationDetails_ReadByUserIdStatus]",
new { UserId = userId, Status = status },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
public async Task DeleteManyAsync(IEnumerable<Guid> providerUserIds)
{
using (var connection = new SqlConnection(ConnectionString))

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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
}
}

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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.");
}

View File

@ -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)
{

View File

@ -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();
}