Skip to content

Commit

Permalink
Merge branch 'main' into renovate/lock-file-maintenance
Browse files Browse the repository at this point in the history
  • Loading branch information
ike-kottlowski committed Apr 27, 2024
2 parents 81d1507 + 8142ba7 commit 2f5cd32
Show file tree
Hide file tree
Showing 57 changed files with 9,625 additions and 241 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ public class CreateProviderCommand : ICreateProviderCommand

public async Task CreateMspAsync(Provider provider, string ownerEmail, int teamsMinimumSeats, int enterpriseMinimumSeats)
{
var isConsolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling);
var owner = await _userRepository.GetByEmailAsync(ownerEmail);
if (owner == null)
{
Expand All @@ -57,6 +56,8 @@ public async Task CreateMspAsync(Provider provider, string ownerEmail, int teams
Status = ProviderUserStatusType.Confirmed,
};

var isConsolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling);

if (isConsolidatedBillingEnabled)
{
var providerPlans = new List<ProviderPlan>
Expand All @@ -73,7 +74,6 @@ public async Task CreateMspAsync(Provider provider, string ownerEmail, int teams

await _providerUserRepository.CreateAsync(providerUser);
await _providerService.SendProviderSetupInviteEmailAsync(provider, owner.Email);

}

public async Task CreateResellerAsync(Provider provider)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Providers.Interfaces;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Commands;
using Bit.Core.Billing.Constants;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Microsoft.Extensions.Logging;
using Stripe;

Expand All @@ -20,6 +25,8 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv
private readonly IOrganizationService _organizationService;
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
private readonly IStripeAdapter _stripeAdapter;
private readonly IScaleSeatsCommand _scaleSeatsCommand;
private readonly IFeatureService _featureService;

public RemoveOrganizationFromProviderCommand(
IEventService eventService,
Expand All @@ -28,7 +35,9 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv
IOrganizationRepository organizationRepository,
IOrganizationService organizationService,
IProviderOrganizationRepository providerOrganizationRepository,
IStripeAdapter stripeAdapter)
IStripeAdapter stripeAdapter,
IScaleSeatsCommand scaleSeatsCommand,
IFeatureService featureService)
{
_eventService = eventService;
_logger = logger;
Expand All @@ -37,6 +46,8 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv
_organizationService = organizationService;
_providerOrganizationRepository = providerOrganizationRepository;
_stripeAdapter = stripeAdapter;
_scaleSeatsCommand = scaleSeatsCommand;
_featureService = featureService;
}

public async Task RemoveOrganizationFromProvider(
Expand Down Expand Up @@ -65,8 +76,6 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv

organization.BillingEmail = organizationOwnerEmails.MinBy(email => email);

await _organizationRepository.ReplaceAsync(organization);

var customerUpdateOptions = new CustomerUpdateOptions
{
Coupon = string.Empty,
Expand All @@ -75,13 +84,41 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv

await _stripeAdapter.CustomerUpdateAsync(organization.GatewayCustomerId, customerUpdateOptions);

var subscriptionUpdateOptions = new SubscriptionUpdateOptions
var isConsolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling);

if (isConsolidatedBillingEnabled && provider.Status == ProviderStatusType.Billable)
{
CollectionMethod = "send_invoice",
DaysUntilDue = 30
};
var plan = StaticStore.GetPlan(organization.PlanType).PasswordManager;
var subscriptionCreateOptions = new SubscriptionCreateOptions
{
Customer = organization.GatewayCustomerId,
CollectionMethod = StripeConstants.CollectionMethod.SendInvoice,
DaysUntilDue = 30,
AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true },
Metadata = new Dictionary<string, string>
{
{ "organizationId", organization.Id.ToString() }
},
OffSession = true,
ProrationBehavior = StripeConstants.ProrationBehavior.CreateProrations,
Items = [new SubscriptionItemOptions { Price = plan.StripeSeatPlanId, Quantity = organization.Seats }]
};
var subscription = await _stripeAdapter.SubscriptionCreateAsync(subscriptionCreateOptions);
organization.GatewaySubscriptionId = subscription.Id;
await _scaleSeatsCommand.ScalePasswordManagerSeats(provider, organization.PlanType,
-(organization.Seats ?? 0));
}
else
{
var subscriptionUpdateOptions = new SubscriptionUpdateOptions
{
CollectionMethod = "send_invoice",
DaysUntilDue = 30
};
await _stripeAdapter.SubscriptionUpdateAsync(organization.GatewaySubscriptionId, subscriptionUpdateOptions);
}

await _stripeAdapter.SubscriptionUpdateAsync(organization.GatewaySubscriptionId, subscriptionUpdateOptions);
await _organizationRepository.ReplaceAsync(organization);

await _mailService.SendProviderUpdatePaymentMethod(
organization.Id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Bit.Core.AdminConsole.Models.Business.Tokenables;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Billing.Extensions;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
Expand Down Expand Up @@ -518,7 +519,9 @@ private static string GetEnumDisplayName(Enum value)
public async Task<ProviderOrganization> CreateOrganizationAsync(Guid providerId,
OrganizationSignup organizationSignup, string clientOwnerEmail, User user)
{
var consolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling);
var provider = await _providerRepository.GetByIdAsync(providerId);

var consolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) && provider.IsBillable();

ThrowOnInvalidPlanType(organizationSignup.Plan, consolidatedBillingEnabled);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#nullable enable
using Bit.Core.Exceptions;
using Bit.Core.SecretsManager.Entities;
using Bit.Core.SecretsManager.Models.Data;
using Bit.Core.SecretsManager.Queries.Secrets.Interfaces;
using Bit.Core.SecretsManager.Repositories;

namespace Bit.Commercial.Core.SecretsManager.Queries.Secrets;

public class SecretsSyncQuery : ISecretsSyncQuery
{
private readonly ISecretRepository _secretRepository;
private readonly IServiceAccountRepository _serviceAccountRepository;

public SecretsSyncQuery(
ISecretRepository secretRepository,
IServiceAccountRepository serviceAccountRepository)
{
_secretRepository = secretRepository;
_serviceAccountRepository = serviceAccountRepository;
}

public async Task<(bool HasChanges, IEnumerable<Secret>? Secrets)> GetAsync(SecretsSyncRequest syncRequest)
{
if (syncRequest.LastSyncedDate == null)
{
return await GetSecretsAsync(syncRequest);
}

var serviceAccount = await _serviceAccountRepository.GetByIdAsync(syncRequest.ServiceAccountId);
if (serviceAccount == null)
{
throw new NotFoundException();
}

if (syncRequest.LastSyncedDate.Value <= serviceAccount.RevisionDate)
{
return await GetSecretsAsync(syncRequest);
}

return (HasChanges: false, null);
}

private async Task<(bool HasChanges, IEnumerable<Secret>? Secrets)> GetSecretsAsync(SecretsSyncRequest syncRequest)
{
var secrets = await _secretRepository.GetManyByOrganizationIdAsync(syncRequest.OrganizationId,
syncRequest.ServiceAccountId, syncRequest.AccessClientType);
return (HasChanges: true, Secrets: secrets);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Bit.Commercial.Core.SecretsManager.Queries;
using Bit.Commercial.Core.SecretsManager.Queries.AccessPolicies;
using Bit.Commercial.Core.SecretsManager.Queries.Projects;
using Bit.Commercial.Core.SecretsManager.Queries.Secrets;
using Bit.Commercial.Core.SecretsManager.Queries.ServiceAccounts;
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
using Bit.Core.SecretsManager.Commands.AccessTokens.Interfaces;
Expand All @@ -23,6 +24,7 @@
using Bit.Core.SecretsManager.Queries.AccessPolicies.Interfaces;
using Bit.Core.SecretsManager.Queries.Interfaces;
using Bit.Core.SecretsManager.Queries.Projects.Interfaces;
using Bit.Core.SecretsManager.Queries.Secrets.Interfaces;
using Bit.Core.SecretsManager.Queries.ServiceAccounts.Interfaces;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -43,6 +45,7 @@ public static void AddSecretsManagerServices(this IServiceCollection services)
services.AddScoped<IMaxProjectsQuery, MaxProjectsQuery>();
services.AddScoped<ISameOrganizationQuery, SameOrganizationQuery>();
services.AddScoped<IServiceAccountSecretsDetailsQuery, ServiceAccountSecretsDetailsQuery>();
services.AddScoped<ISecretsSyncQuery, SecretsSyncQuery>();
services.AddScoped<ICreateSecretCommand, CreateSecretCommand>();
services.AddScoped<IUpdateSecretCommand, UpdateSecretCommand>();
services.AddScoped<IDeleteSecretCommand, DeleteSecretCommand>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
policy.GrantedProject.GroupAccessPolicies.Any(ap =>
ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Write));

public async Task<List<Core.SecretsManager.Entities.BaseAccessPolicy>> CreateManyAsync(List<Core.SecretsManager.Entities.BaseAccessPolicy> baseAccessPolicies)
public async Task<List<Core.SecretsManager.Entities.BaseAccessPolicy>> CreateManyAsync(
List<Core.SecretsManager.Entities.BaseAccessPolicy> baseAccessPolicies)
{
using var scope = ServiceScopeFactory.CreateScope();
await using var scope = ServiceScopeFactory.CreateAsyncScope();
var dbContext = GetDatabaseContext(scope);
await using var transaction = await dbContext.Database.BeginTransactionAsync();

var serviceAccountIds = new List<Guid>();
foreach (var baseAccessPolicy in baseAccessPolicies)
{
baseAccessPolicy.SetNewId();
Expand Down Expand Up @@ -64,12 +68,22 @@ public async Task<List<Core.SecretsManager.Entities.BaseAccessPolicy>> CreateMan
{
var entity = Mapper.Map<ServiceAccountProjectAccessPolicy>(accessPolicy);
await dbContext.AddAsync(entity);
serviceAccountIds.Add(entity.ServiceAccountId!.Value);
break;
}
}
}

if (serviceAccountIds.Count > 0)
{
var utcNow = DateTime.UtcNow;
await dbContext.ServiceAccount
.Where(sa => serviceAccountIds.Contains(sa.Id))
.ExecuteUpdateAsync(setters => setters.SetProperty(sa => sa.RevisionDate, utcNow));
}

await dbContext.SaveChangesAsync();
await transaction.CommitAsync();
return baseAccessPolicies;
}

Expand Down Expand Up @@ -190,6 +204,16 @@ public async Task DeleteAsync(Guid id)
var entity = await dbContext.AccessPolicies.FindAsync(id);
if (entity != null)
{
if (entity is ServiceAccountProjectAccessPolicy serviceAccountProjectAccessPolicy)
{
var serviceAccount =
await dbContext.ServiceAccount.FindAsync(serviceAccountProjectAccessPolicy.ServiceAccountId);
if (serviceAccount != null)
{
serviceAccount.RevisionDate = DateTime.UtcNow;
}
}

dbContext.Remove(entity);
await dbContext.SaveChangesAsync();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,23 +70,43 @@ public async Task<int> GetProjectCountByOrganizationIdAsync(Guid organizationId)

public async Task DeleteManyByIdAsync(IEnumerable<Guid> ids)
{
using var scope = ServiceScopeFactory.CreateScope();
var utcNow = DateTime.UtcNow;
await using var scope = ServiceScopeFactory.CreateAsyncScope();
var dbContext = GetDatabaseContext(scope);
var projects = dbContext.Project
.Where(c => ids.Contains(c.Id))
.Include(p => p.Secrets);
await projects.ForEachAsync(project =>
await using var transaction = await dbContext.Database.BeginTransactionAsync();

var serviceAccountIds = await dbContext.Project
.Where(p => ids.Contains(p.Id))
.Include(p => p.ServiceAccountAccessPolicies)
.SelectMany(p => p.ServiceAccountAccessPolicies.Select(ap => ap.ServiceAccountId!.Value))
.Distinct()
.ToListAsync();

var secretIds = await dbContext.Project
.Where(p => ids.Contains(p.Id))
.Include(p => p.Secrets)
.SelectMany(p => p.Secrets.Select(s => s.Id))
.Distinct()
.ToListAsync();

var utcNow = DateTime.UtcNow;
if (serviceAccountIds.Count > 0)
{
foreach (var projectSecret in project.Secrets)
{
projectSecret.RevisionDate = utcNow;
}
await dbContext.ServiceAccount
.Where(sa => serviceAccountIds.Contains(sa.Id))
.ExecuteUpdateAsync(setters =>
setters.SetProperty(sa => sa.RevisionDate, utcNow));
}

dbContext.Remove(project);
});
if (secretIds.Count > 0)
{
await dbContext.Secret
.Where(s => secretIds.Contains(s.Id))
.ExecuteUpdateAsync(setters =>
setters.SetProperty(s => s.RevisionDate, utcNow));
}

await dbContext.SaveChangesAsync();
await dbContext.Project.Where(p => ids.Contains(p.Id)).ExecuteDeleteAsync();
await transaction.CommitAsync();
}

public async Task<IEnumerable<Core.SecretsManager.Entities.Project>> GetManyWithSecretsByIds(IEnumerable<Guid> ids)
Expand Down Expand Up @@ -199,8 +219,4 @@ private IQueryable<ProjectPermissionDetails> ProjectToPermissionDetails(IQueryab

private static Expression<Func<Project, bool>> ServiceAccountHasReadAccessToProject(Guid serviceAccountId) => p =>
p.ServiceAccountAccessPolicies.Any(ap => ap.ServiceAccount.Id == serviceAccountId && ap.Read);

private static Expression<Func<Project, bool>> ServiceAccountHasWriteAccessToProject(Guid serviceAccountId) => p =>
p.ServiceAccountAccessPolicies.Any(ap => ap.ServiceAccount.Id == serviceAccountId && ap.Write);

}

0 comments on commit 2f5cd32

Please sign in to comment.