mirror of
https://github.com/bitwarden/server.git
synced 2025-04-08 06:28:14 -05:00
app cache with org ability checks on events
This commit is contained in:
parent
d75ca51d75
commit
e9116f8c44
21
src/Core/Models/Data/OrganizationAbility.cs
Normal file
21
src/Core/Models/Data/OrganizationAbility.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using Bit.Core.Models.Table;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Data
|
||||||
|
{
|
||||||
|
public class OrganizationAbility
|
||||||
|
{
|
||||||
|
public OrganizationAbility() { }
|
||||||
|
|
||||||
|
public OrganizationAbility(Organization organization)
|
||||||
|
{
|
||||||
|
Id = organization.Id;
|
||||||
|
UseEvents = organization.UseEvents;
|
||||||
|
Enabled = organization.Enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public bool UseEvents { get; set; }
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
|
|
||||||
namespace Bit.Core.Repositories
|
namespace Bit.Core.Repositories
|
||||||
@ -10,5 +11,6 @@ namespace Bit.Core.Repositories
|
|||||||
Task<ICollection<Organization>> GetManyByEnabledAsync();
|
Task<ICollection<Organization>> GetManyByEnabledAsync();
|
||||||
Task<ICollection<Organization>> GetManyByUserIdAsync(Guid userId);
|
Task<ICollection<Organization>> GetManyByUserIdAsync(Guid userId);
|
||||||
Task UpdateStorageAsync(Guid id);
|
Task UpdateStorageAsync(Guid id);
|
||||||
|
Task<ICollection<OrganizationAbility>> GetManyAbilitiesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ using System.Data;
|
|||||||
using Dapper;
|
using Dapper;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
|
||||||
namespace Bit.Core.Repositories.SqlServer
|
namespace Bit.Core.Repositories.SqlServer
|
||||||
{
|
{
|
||||||
@ -55,5 +56,17 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
commandTimeout: 180);
|
commandTimeout: 180);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<ICollection<OrganizationAbility>> GetManyAbilitiesAsync()
|
||||||
|
{
|
||||||
|
using(var connection = new SqlConnection(ConnectionString))
|
||||||
|
{
|
||||||
|
var results = await connection.QueryAsync<OrganizationAbility>(
|
||||||
|
"[dbo].[Organization_ReadAbilities]",
|
||||||
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
|
return results.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
15
src/Core/Services/IApplicationCacheService.cs
Normal file
15
src/Core/Services/IApplicationCacheService.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.Models.Table;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services
|
||||||
|
{
|
||||||
|
public interface IApplicationCacheService
|
||||||
|
{
|
||||||
|
Task<IDictionary<Guid, OrganizationAbility>> GetOrganizationAbilitiesAsync();
|
||||||
|
Task UpsertOrganizationAbilityAsync(Organization organization);
|
||||||
|
Task DeleteOrganizationAbilityAsync(Guid organizationId);
|
||||||
|
}
|
||||||
|
}
|
@ -13,17 +13,20 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
private readonly IEventWriteService _eventWriteService;
|
private readonly IEventWriteService _eventWriteService;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
|
private readonly IApplicationCacheService _applicationCacheService;
|
||||||
private readonly CurrentContext _currentContext;
|
private readonly CurrentContext _currentContext;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
|
||||||
public EventService(
|
public EventService(
|
||||||
IEventWriteService eventWriteService,
|
IEventWriteService eventWriteService,
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
|
IApplicationCacheService applicationCacheService,
|
||||||
CurrentContext currentContext,
|
CurrentContext currentContext,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
_eventWriteService = eventWriteService;
|
_eventWriteService = eventWriteService;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
|
_applicationCacheService = applicationCacheService;
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
}
|
}
|
||||||
@ -42,10 +45,13 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
||||||
IEnumerable<IEvent> orgEvents;
|
IEnumerable<IEvent> orgEvents;
|
||||||
if(_currentContext.UserId.HasValue)
|
if(_currentContext.UserId.HasValue)
|
||||||
{
|
{
|
||||||
orgEvents = _currentContext.Organizations.Select(o => new EventMessage(_currentContext)
|
orgEvents = _currentContext.Organizations
|
||||||
|
.Where(o => CanUseEvents(orgAbilities, o.Id))
|
||||||
|
.Select(o => new EventMessage(_currentContext)
|
||||||
{
|
{
|
||||||
OrganizationId = o.Id,
|
OrganizationId = o.Id,
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
@ -57,7 +63,8 @@ namespace Bit.Core.Services
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var orgs = await _organizationUserRepository.GetManyByUserAsync(userId);
|
var orgs = await _organizationUserRepository.GetManyByUserAsync(userId);
|
||||||
orgEvents = orgs.Where(o => o.Status == OrganizationUserStatusType.Confirmed)
|
orgEvents = orgs
|
||||||
|
.Where(o => o.Status == OrganizationUserStatusType.Confirmed && CanUseEvents(orgAbilities, o.Id))
|
||||||
.Select(o => new EventMessage(_currentContext)
|
.Select(o => new EventMessage(_currentContext)
|
||||||
{
|
{
|
||||||
OrganizationId = o.OrganizationId,
|
OrganizationId = o.OrganizationId,
|
||||||
@ -87,6 +94,15 @@ namespace Bit.Core.Services
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(cipher.OrganizationId.HasValue)
|
||||||
|
{
|
||||||
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
||||||
|
if(!CanUseEvents(orgAbilities, cipher.OrganizationId.Value))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var e = new EventMessage(_currentContext)
|
var e = new EventMessage(_currentContext)
|
||||||
{
|
{
|
||||||
OrganizationId = cipher.OrganizationId,
|
OrganizationId = cipher.OrganizationId,
|
||||||
@ -101,6 +117,12 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task LogCollectionEventAsync(Collection collection, EventType type)
|
public async Task LogCollectionEventAsync(Collection collection, EventType type)
|
||||||
{
|
{
|
||||||
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
||||||
|
if(!CanUseEvents(orgAbilities, collection.OrganizationId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var e = new EventMessage(_currentContext)
|
var e = new EventMessage(_currentContext)
|
||||||
{
|
{
|
||||||
OrganizationId = collection.OrganizationId,
|
OrganizationId = collection.OrganizationId,
|
||||||
@ -114,6 +136,12 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task LogGroupEventAsync(Group group, EventType type)
|
public async Task LogGroupEventAsync(Group group, EventType type)
|
||||||
{
|
{
|
||||||
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
||||||
|
if(!CanUseEvents(orgAbilities, group.OrganizationId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var e = new EventMessage(_currentContext)
|
var e = new EventMessage(_currentContext)
|
||||||
{
|
{
|
||||||
OrganizationId = group.OrganizationId,
|
OrganizationId = group.OrganizationId,
|
||||||
@ -127,6 +155,12 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task LogOrganizationUserEventAsync(OrganizationUser organizationUser, EventType type)
|
public async Task LogOrganizationUserEventAsync(OrganizationUser organizationUser, EventType type)
|
||||||
{
|
{
|
||||||
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
||||||
|
if(!CanUseEvents(orgAbilities, organizationUser.OrganizationId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var e = new EventMessage(_currentContext)
|
var e = new EventMessage(_currentContext)
|
||||||
{
|
{
|
||||||
OrganizationId = organizationUser.OrganizationId,
|
OrganizationId = organizationUser.OrganizationId,
|
||||||
@ -141,6 +175,11 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task LogOrganizationEventAsync(Organization organization, EventType type)
|
public async Task LogOrganizationEventAsync(Organization organization, EventType type)
|
||||||
{
|
{
|
||||||
|
if(!organization.Enabled || !organization.UseEvents)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var e = new EventMessage(_currentContext)
|
var e = new EventMessage(_currentContext)
|
||||||
{
|
{
|
||||||
OrganizationId = organization.Id,
|
OrganizationId = organization.Id,
|
||||||
@ -150,5 +189,11 @@ namespace Bit.Core.Services
|
|||||||
};
|
};
|
||||||
await _eventWriteService.CreateAsync(e);
|
await _eventWriteService.CreateAsync(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool CanUseEvents(IDictionary<Guid, OrganizationAbility> orgAbilities, Guid orgId)
|
||||||
|
{
|
||||||
|
return orgAbilities != null && orgAbilities.ContainsKey(orgId) &&
|
||||||
|
orgAbilities[orgId].Enabled && orgAbilities[orgId].UseEvents;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.Models.Table;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services
|
||||||
|
{
|
||||||
|
public class InMemoryApplicationCacheService : IApplicationCacheService
|
||||||
|
{
|
||||||
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
|
private DateTime _lastOrgAbilityRefresh = DateTime.MinValue;
|
||||||
|
private IDictionary<Guid, OrganizationAbility> _orgAbilities;
|
||||||
|
private TimeSpan _orgAbilitiesRefreshInterval = TimeSpan.FromMinutes(10);
|
||||||
|
|
||||||
|
public InMemoryApplicationCacheService(
|
||||||
|
IOrganizationRepository organizationRepository)
|
||||||
|
{
|
||||||
|
_organizationRepository = organizationRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IDictionary<Guid, OrganizationAbility>> GetOrganizationAbilitiesAsync()
|
||||||
|
{
|
||||||
|
await InitOrganizationAbilitiesAsync();
|
||||||
|
return _orgAbilities;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpsertOrganizationAbilityAsync(Organization organization)
|
||||||
|
{
|
||||||
|
await InitOrganizationAbilitiesAsync();
|
||||||
|
var newAbility = new OrganizationAbility(organization);
|
||||||
|
|
||||||
|
if(_orgAbilities.ContainsKey(organization.Id))
|
||||||
|
{
|
||||||
|
_orgAbilities[organization.Id] = newAbility;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_orgAbilities.Add(organization.Id, newAbility);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DeleteOrganizationAbilityAsync(Guid organizationId)
|
||||||
|
{
|
||||||
|
if(_orgAbilities != null && _orgAbilities.ContainsKey(organizationId))
|
||||||
|
{
|
||||||
|
_orgAbilities.Remove(organizationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InitOrganizationAbilitiesAsync()
|
||||||
|
{
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
if(_orgAbilities == null || (now - _lastOrgAbilityRefresh) > _orgAbilitiesRefreshInterval)
|
||||||
|
{
|
||||||
|
var abilities = await _organizationRepository.GetManyAbilitiesAsync();
|
||||||
|
_orgAbilities = abilities.ToDictionary(a => a.Id);
|
||||||
|
_lastOrgAbilityRefresh = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,7 @@ namespace Bit.Core.Services
|
|||||||
private readonly ILicensingService _licensingService;
|
private readonly ILicensingService _licensingService;
|
||||||
private readonly IEventService _eventService;
|
private readonly IEventService _eventService;
|
||||||
private readonly IInstallationRepository _installationRepository;
|
private readonly IInstallationRepository _installationRepository;
|
||||||
|
private readonly IApplicationCacheService _applicationCacheService;
|
||||||
private readonly StripePaymentService _stripePaymentService;
|
private readonly StripePaymentService _stripePaymentService;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ namespace Bit.Core.Services
|
|||||||
ILicensingService licensingService,
|
ILicensingService licensingService,
|
||||||
IEventService eventService,
|
IEventService eventService,
|
||||||
IInstallationRepository installationRepository,
|
IInstallationRepository installationRepository,
|
||||||
|
IApplicationCacheService applicationCacheService,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
@ -63,13 +65,14 @@ namespace Bit.Core.Services
|
|||||||
_licensingService = licensingService;
|
_licensingService = licensingService;
|
||||||
_eventService = eventService;
|
_eventService = eventService;
|
||||||
_installationRepository = installationRepository;
|
_installationRepository = installationRepository;
|
||||||
|
_applicationCacheService = applicationCacheService;
|
||||||
_stripePaymentService = new StripePaymentService();
|
_stripePaymentService = new StripePaymentService();
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken)
|
public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
var organization = await GetOrgById(organizationId);
|
||||||
if(organization == null)
|
if(organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -78,13 +81,13 @@ namespace Bit.Core.Services
|
|||||||
var updated = await _stripePaymentService.UpdatePaymentMethodAsync(organization, paymentToken);
|
var updated = await _stripePaymentService.UpdatePaymentMethodAsync(organization, paymentToken);
|
||||||
if(updated)
|
if(updated)
|
||||||
{
|
{
|
||||||
await _organizationRepository.ReplaceAsync(organization);
|
await ReplaceAndUpdateCache(organization);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CancelSubscriptionAsync(Guid organizationId, bool endOfPeriod = false)
|
public async Task CancelSubscriptionAsync(Guid organizationId, bool endOfPeriod = false)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
var organization = await GetOrgById(organizationId);
|
||||||
if(organization == null)
|
if(organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -95,7 +98,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task ReinstateSubscriptionAsync(Guid organizationId)
|
public async Task ReinstateSubscriptionAsync(Guid organizationId)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
var organization = await GetOrgById(organizationId);
|
||||||
if(organization == null)
|
if(organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -106,7 +109,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task UpgradePlanAsync(Guid organizationId, PlanType plan, int additionalSeats)
|
public async Task UpgradePlanAsync(Guid organizationId, PlanType plan, int additionalSeats)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
var organization = await GetOrgById(organizationId);
|
||||||
if(organization == null)
|
if(organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -243,7 +246,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task AdjustStorageAsync(Guid organizationId, short storageAdjustmentGb)
|
public async Task AdjustStorageAsync(Guid organizationId, short storageAdjustmentGb)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
var organization = await GetOrgById(organizationId);
|
||||||
if(organization == null)
|
if(organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -262,12 +265,12 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
await BillingHelpers.AdjustStorageAsync(_stripePaymentService, organization, storageAdjustmentGb,
|
await BillingHelpers.AdjustStorageAsync(_stripePaymentService, organization, storageAdjustmentGb,
|
||||||
plan.StripStoragePlanId);
|
plan.StripStoragePlanId);
|
||||||
await _organizationRepository.ReplaceAsync(organization);
|
await ReplaceAndUpdateCache(organization);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AdjustSeatsAsync(Guid organizationId, int seatAdjustment)
|
public async Task AdjustSeatsAsync(Guid organizationId, int seatAdjustment)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
var organization = await GetOrgById(organizationId);
|
||||||
if(organization == null)
|
if(organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -361,12 +364,12 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
organization.Seats = (short?)newSeatTotal;
|
organization.Seats = (short?)newSeatTotal;
|
||||||
await _organizationRepository.ReplaceAsync(organization);
|
await ReplaceAndUpdateCache(organization);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task VerifyBankAsync(Guid organizationId, int amount1, int amount2)
|
public async Task VerifyBankAsync(Guid organizationId, int amount1, int amount2)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
var organization = await GetOrgById(organizationId);
|
||||||
if(organization == null)
|
if(organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -622,6 +625,7 @@ namespace Bit.Core.Services
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _organizationRepository.CreateAsync(organization);
|
await _organizationRepository.CreateAsync(organization);
|
||||||
|
await _applicationCacheService.UpsertOrganizationAbilityAsync(organization);
|
||||||
|
|
||||||
var orgUser = new OrganizationUser
|
var orgUser = new OrganizationUser
|
||||||
{
|
{
|
||||||
@ -666,6 +670,7 @@ namespace Bit.Core.Services
|
|||||||
if(organization.Id != default(Guid))
|
if(organization.Id != default(Guid))
|
||||||
{
|
{
|
||||||
await _organizationRepository.DeleteAsync(organization);
|
await _organizationRepository.DeleteAsync(organization);
|
||||||
|
await _applicationCacheService.DeleteOrganizationAbilityAsync(organization.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw;
|
throw;
|
||||||
@ -674,7 +679,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task UpdateLicenseAsync(Guid organizationId, OrganizationLicense license)
|
public async Task UpdateLicenseAsync(Guid organizationId, OrganizationLicense license)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
var organization = await GetOrgById(organizationId);
|
||||||
if(organization == null)
|
if(organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -753,7 +758,7 @@ namespace Bit.Core.Services
|
|||||||
organization.ExpirationDate = license.Expires;
|
organization.ExpirationDate = license.Expires;
|
||||||
organization.LicenseKey = license.LicenseKey;
|
organization.LicenseKey = license.LicenseKey;
|
||||||
organization.RevisionDate = DateTime.UtcNow;
|
organization.RevisionDate = DateTime.UtcNow;
|
||||||
await _organizationRepository.ReplaceAsync(organization);
|
await ReplaceAndUpdateCache(organization);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteAsync(Organization organization)
|
public async Task DeleteAsync(Organization organization)
|
||||||
@ -764,17 +769,18 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _organizationRepository.DeleteAsync(organization);
|
await _organizationRepository.DeleteAsync(organization);
|
||||||
|
await _applicationCacheService.DeleteOrganizationAbilityAsync(organization.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DisableAsync(Guid organizationId, DateTime? expirationDate)
|
public async Task DisableAsync(Guid organizationId, DateTime? expirationDate)
|
||||||
{
|
{
|
||||||
var org = await _organizationRepository.GetByIdAsync(organizationId);
|
var org = await GetOrgById(organizationId);
|
||||||
if(org != null && org.Enabled)
|
if(org != null && org.Enabled)
|
||||||
{
|
{
|
||||||
org.Enabled = false;
|
org.Enabled = false;
|
||||||
org.ExpirationDate = expirationDate;
|
org.ExpirationDate = expirationDate;
|
||||||
org.RevisionDate = DateTime.UtcNow;
|
org.RevisionDate = DateTime.UtcNow;
|
||||||
await _organizationRepository.ReplaceAsync(org);
|
await ReplaceAndUpdateCache(org);
|
||||||
|
|
||||||
// TODO: send email to owners?
|
// TODO: send email to owners?
|
||||||
}
|
}
|
||||||
@ -782,22 +788,22 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task UpdateExpirationDateAsync(Guid organizationId, DateTime? expirationDate)
|
public async Task UpdateExpirationDateAsync(Guid organizationId, DateTime? expirationDate)
|
||||||
{
|
{
|
||||||
var org = await _organizationRepository.GetByIdAsync(organizationId);
|
var org = await GetOrgById(organizationId);
|
||||||
if(org != null)
|
if(org != null)
|
||||||
{
|
{
|
||||||
org.ExpirationDate = expirationDate;
|
org.ExpirationDate = expirationDate;
|
||||||
org.RevisionDate = DateTime.UtcNow;
|
org.RevisionDate = DateTime.UtcNow;
|
||||||
await _organizationRepository.ReplaceAsync(org);
|
await ReplaceAndUpdateCache(org);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task EnableAsync(Guid organizationId)
|
public async Task EnableAsync(Guid organizationId)
|
||||||
{
|
{
|
||||||
var org = await _organizationRepository.GetByIdAsync(organizationId);
|
var org = await GetOrgById(organizationId);
|
||||||
if(org != null && !org.Enabled)
|
if(org != null && !org.Enabled)
|
||||||
{
|
{
|
||||||
org.Enabled = true;
|
org.Enabled = true;
|
||||||
await _organizationRepository.ReplaceAsync(org);
|
await ReplaceAndUpdateCache(org);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -808,8 +814,7 @@ namespace Bit.Core.Services
|
|||||||
throw new ApplicationException("Cannot create org this way. Call SignUpAsync.");
|
throw new ApplicationException("Cannot create org this way. Call SignUpAsync.");
|
||||||
}
|
}
|
||||||
|
|
||||||
await _organizationRepository.ReplaceAsync(organization);
|
await ReplaceAndUpdateCache(organization, EventType.Organization_Updated);
|
||||||
await _eventService.LogOrganizationEventAsync(organization, EventType.Organization_Updated);
|
|
||||||
|
|
||||||
if(updateBilling && !string.IsNullOrWhiteSpace(organization.GatewayCustomerId))
|
if(updateBilling && !string.IsNullOrWhiteSpace(organization.GatewayCustomerId))
|
||||||
{
|
{
|
||||||
@ -834,7 +839,7 @@ namespace Bit.Core.Services
|
|||||||
IEnumerable<string> emails, OrganizationUserType type, bool accessAll, string externalId,
|
IEnumerable<string> emails, OrganizationUserType type, bool accessAll, string externalId,
|
||||||
IEnumerable<SelectionReadOnly> collections)
|
IEnumerable<SelectionReadOnly> collections)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
var organization = await GetOrgById(organizationId);
|
||||||
if(organization == null)
|
if(organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -915,7 +920,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
private async Task SendInviteAsync(OrganizationUser orgUser)
|
private async Task SendInviteAsync(OrganizationUser orgUser)
|
||||||
{
|
{
|
||||||
var org = await _organizationRepository.GetByIdAsync(orgUser.OrganizationId);
|
var org = await GetOrgById(orgUser.OrganizationId);
|
||||||
var nowMillis = CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow);
|
var nowMillis = CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow);
|
||||||
var token = _dataProtector.Protect(
|
var token = _dataProtector.Protect(
|
||||||
$"OrganizationUserInvite {orgUser.Id} {orgUser.Email} {nowMillis}");
|
$"OrganizationUserInvite {orgUser.Id} {orgUser.Email} {nowMillis}");
|
||||||
@ -937,7 +942,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
if(orgUser.Type == OrganizationUserType.Owner || orgUser.Type == OrganizationUserType.Admin)
|
if(orgUser.Type == OrganizationUserType.Owner || orgUser.Type == OrganizationUserType.Admin)
|
||||||
{
|
{
|
||||||
var org = await _organizationRepository.GetByIdAsync(orgUser.OrganizationId);
|
var org = await GetOrgById(orgUser.OrganizationId);
|
||||||
if(org.PlanType == PlanType.Free)
|
if(org.PlanType == PlanType.Free)
|
||||||
{
|
{
|
||||||
var adminCount = await _organizationUserRepository.GetCountByFreeOrganizationAdminUserAsync(user.Id);
|
var adminCount = await _organizationUserRepository.GetCountByFreeOrganizationAdminUserAsync(user.Id);
|
||||||
@ -999,7 +1004,7 @@ namespace Bit.Core.Services
|
|||||||
throw new BadRequestException("User not valid.");
|
throw new BadRequestException("User not valid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var org = await _organizationRepository.GetByIdAsync(organizationId);
|
var org = await GetOrgById(organizationId);
|
||||||
if(org.PlanType == PlanType.Free &&
|
if(org.PlanType == PlanType.Free &&
|
||||||
(orgUser.Type == OrganizationUserType.Admin || orgUser.Type == OrganizationUserType.Owner))
|
(orgUser.Type == OrganizationUserType.Admin || orgUser.Type == OrganizationUserType.Owner))
|
||||||
{
|
{
|
||||||
@ -1140,7 +1145,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task<OrganizationLicense> GenerateLicenseAsync(Guid organizationId, Guid installationId)
|
public async Task<OrganizationLicense> GenerateLicenseAsync(Guid organizationId, Guid installationId)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
var organization = await GetOrgById(organizationId);
|
||||||
return await GenerateLicenseAsync(organization, installationId);
|
return await GenerateLicenseAsync(organization, installationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1168,7 +1173,7 @@ namespace Bit.Core.Services
|
|||||||
IEnumerable<ImportedOrganizationUser> newUsers,
|
IEnumerable<ImportedOrganizationUser> newUsers,
|
||||||
IEnumerable<string> removeUserExternalIds)
|
IEnumerable<string> removeUserExternalIds)
|
||||||
{
|
{
|
||||||
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
var organization = await GetOrgById(organizationId);
|
||||||
if(organization == null)
|
if(organization == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -1340,5 +1345,21 @@ namespace Bit.Core.Services
|
|||||||
var devices = await _deviceRepository.GetManyByUserIdAsync(userId);
|
var devices = await _deviceRepository.GetManyByUserIdAsync(userId);
|
||||||
return devices.Where(d => !string.IsNullOrWhiteSpace(d.PushToken)).Select(d => d.Id.ToString());
|
return devices.Where(d => !string.IsNullOrWhiteSpace(d.PushToken)).Select(d => d.Id.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task ReplaceAndUpdateCache(Organization org, EventType? orgEvent = null)
|
||||||
|
{
|
||||||
|
await _organizationRepository.ReplaceAsync(org);
|
||||||
|
await _applicationCacheService.UpsertOrganizationAbilityAsync(org);
|
||||||
|
|
||||||
|
if(orgEvent.HasValue)
|
||||||
|
{
|
||||||
|
await _eventService.LogOrganizationEventAsync(org, orgEvent.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Organization> GetOrgById(Guid id)
|
||||||
|
{
|
||||||
|
return await _organizationRepository.GetByIdAsync(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,6 +67,7 @@ namespace Bit.Core.Utilities
|
|||||||
{
|
{
|
||||||
services.AddSingleton<IMailService, BackupMailService>();
|
services.AddSingleton<IMailService, BackupMailService>();
|
||||||
services.AddSingleton<ILicensingService, LicensingService>();
|
services.AddSingleton<ILicensingService, LicensingService>();
|
||||||
|
services.AddSingleton<IApplicationCacheService, InMemoryApplicationCacheService>();
|
||||||
|
|
||||||
if(CoreHelpers.SettingHasValue(globalSettings.Mail.SendGridApiKey))
|
if(CoreHelpers.SettingHasValue(globalSettings.Mail.SendGridApiKey))
|
||||||
{
|
{
|
||||||
|
@ -223,5 +223,6 @@
|
|||||||
<Build Include="dbo\Stored Procedures\Event_ReadPageByOrganizationId.sql" />
|
<Build Include="dbo\Stored Procedures\Event_ReadPageByOrganizationId.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\Event_ReadPageByCipherId.sql" />
|
<Build Include="dbo\Stored Procedures\Event_ReadPageByCipherId.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\Event_ReadPageByOrganizationIdActingUserId.sql" />
|
<Build Include="dbo\Stored Procedures\Event_ReadPageByOrganizationIdActingUserId.sql" />
|
||||||
|
<Build Include="dbo\Stored Procedures\Organization_ReadAbilities.sql" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
12
src/Sql/dbo/Stored Procedures/Organization_ReadAbilities.sql
Normal file
12
src/Sql/dbo/Stored Procedures/Organization_ReadAbilities.sql
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[Organization_ReadAbilities]
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
[Id],
|
||||||
|
[UseEvents],
|
||||||
|
[Enabled]
|
||||||
|
FROM
|
||||||
|
[dbo].[Organization]
|
||||||
|
END
|
@ -202,6 +202,26 @@ BEGIN
|
|||||||
END
|
END
|
||||||
GO
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID('[dbo].[Organization_ReadAbilities]') IS NULL
|
||||||
|
BEGIN
|
||||||
|
EXEC('CREATE PROCEDURE [dbo].[Organization_ReadAbilities] AS BEGIN SET NOCOUNT ON; END')
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
ALTER PROCEDURE [dbo].[Organization_ReadAbilities]
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
[Id],
|
||||||
|
[UseEvents],
|
||||||
|
[Enabled]
|
||||||
|
FROM
|
||||||
|
[dbo].[Organization]
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'OrganizationView')
|
IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'OrganizationView')
|
||||||
BEGIN
|
BEGIN
|
||||||
DROP VIEW [dbo].[OrganizationView]
|
DROP VIEW [dbo].[OrganizationView]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user