Skip to content

Commit

Permalink
[SM-394] Secrets Manager (#2164)
Browse files Browse the repository at this point in the history
Long lived feature branch for Secrets Manager

Co-authored-by: Thomas Avery <[email protected]>
Co-authored-by: cd-bitwarden <[email protected]>
Co-authored-by: CarleyDiaz-Bitwarden <[email protected]>
Co-authored-by: Thomas Avery <[email protected]>
Co-authored-by: Colton Hurst <[email protected]>
  • Loading branch information
6 people authored Jan 13, 2023
1 parent 09e524c commit 1f0fc43
Show file tree
Hide file tree
Showing 188 changed files with 21,344 additions and 327 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ jobs:
ASPNETCORE_ENVIRONMENT: Production
swaggerGen: "True"
DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX: 2
GLOBALSETTINGS__SQLSERVER__CONNECTIONSTRING: "placeholder"

- name: Upload Swagger artifact
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
Expand Down
14 changes: 14 additions & 0 deletions bitwarden-server.sln
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTestCommon", "te
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Scim", "bitwarden_license\src\Scim\Scim.csproj", "{BC3B3F8C-621A-4CB8-9563-6EC0A2C8C747}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlServerEFScaffold", "util\SqlServerEFScaffold\SqlServerEFScaffold.csproj", "{2F2E8BB0-6838-48DA-B581-71B9F13DE364}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commercial.Infrastructure.EntityFramework", "bitwarden_license\src\Commercial.Infrastructure.EntityFramework\Commercial.Infrastructure.EntityFramework.csproj", "{5AB3BBFB-9D98-4EF8-BFCD-462D50A16EB1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure.EFIntegration.Test", "test\Infrastructure.EFIntegration.Test\Infrastructure.EFIntegration.Test.csproj", "{7EFB1124-F40A-40EB-9EDA-94FD540AA8FD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.IntegrationTest", "test\Api.IntegrationTest\Api.IntegrationTest.csproj", "{CBE96C6D-A4D6-46E1-94C5-42D6CAD8531C}"
Expand Down Expand Up @@ -236,6 +240,14 @@ Global
{BC3B3F8C-621A-4CB8-9563-6EC0A2C8C747}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BC3B3F8C-621A-4CB8-9563-6EC0A2C8C747}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BC3B3F8C-621A-4CB8-9563-6EC0A2C8C747}.Release|Any CPU.Build.0 = Release|Any CPU
{2F2E8BB0-6838-48DA-B581-71B9F13DE364}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2F2E8BB0-6838-48DA-B581-71B9F13DE364}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2F2E8BB0-6838-48DA-B581-71B9F13DE364}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2F2E8BB0-6838-48DA-B581-71B9F13DE364}.Release|Any CPU.Build.0 = Release|Any CPU
{5AB3BBFB-9D98-4EF8-BFCD-462D50A16EB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5AB3BBFB-9D98-4EF8-BFCD-462D50A16EB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5AB3BBFB-9D98-4EF8-BFCD-462D50A16EB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5AB3BBFB-9D98-4EF8-BFCD-462D50A16EB1}.Release|Any CPU.Build.0 = Release|Any CPU
{7EFB1124-F40A-40EB-9EDA-94FD540AA8FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7EFB1124-F40A-40EB-9EDA-94FD540AA8FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7EFB1124-F40A-40EB-9EDA-94FD540AA8FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -299,6 +311,8 @@ Global
{0D3B2BD2-53F3-421D-AD8F-C19B954C796B} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
{0923DE59-5FB1-44F2-9302-A09D2236B470} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
{BC3B3F8C-621A-4CB8-9563-6EC0A2C8C747} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}
{2F2E8BB0-6838-48DA-B581-71B9F13DE364} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
{5AB3BBFB-9D98-4EF8-BFCD-462D50A16EB1} = {4FDB6543-F68B-4202-9EA6-7FEA984D2D0A}
{7EFB1124-F40A-40EB-9EDA-94FD540AA8FD} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
{CBE96C6D-A4D6-46E1-94C5-42D6CAD8531C} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
{FE998849-5FC8-41A2-B7C9-9227901471A0} = {287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.SecretManagerFeatures.AccessTokens.Interfaces;
using Bit.Core.Utilities;

namespace Bit.Commercial.Core.SecretManagerFeatures.AccessTokens;

public class CreateAccessTokenCommand : ICreateAccessTokenCommand
{
private readonly int _clientSecretMaxLength = 30;
private readonly IApiKeyRepository _apiKeyRepository;

public CreateAccessTokenCommand(IApiKeyRepository apiKeyRepository)
{
_apiKeyRepository = apiKeyRepository;
}

public async Task<ApiKey> CreateAsync(ApiKey apiKey)
{
apiKey.ClientSecret = CoreHelpers.SecureRandomString(_clientSecretMaxLength);
return await _apiKeyRepository.CreateAsync(apiKey);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.SecretManagerFeatures.Projects.Interfaces;

namespace Bit.Commercial.Core.SecretManagerFeatures.Projects;

public class CreateProjectCommand : ICreateProjectCommand
{
private readonly IProjectRepository _projectRepository;

public CreateProjectCommand(IProjectRepository projectRepository)
{
_projectRepository = projectRepository;
}

public async Task<Project> CreateAsync(Project project)
{
return await _projectRepository.CreateAsync(project);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.SecretManagerFeatures.Projects.Interfaces;

namespace Bit.Commercial.Core.SecretManagerFeatures.Projects;

public class DeleteProjectCommand : IDeleteProjectCommand
{
private readonly IProjectRepository _projectRepository;

public DeleteProjectCommand(IProjectRepository projectRepository)
{
_projectRepository = projectRepository;
}

public async Task<List<Tuple<Project, string>>> DeleteProjects(List<Guid> ids)
{
var projects = await _projectRepository.GetManyByIds(ids);

if (projects?.Any() != true)
{
throw new NotFoundException();
}

var results = ids.Select(id =>
{
var project = projects.FirstOrDefault(project => project.Id == id);
if (project == null)
{
throw new NotFoundException();
}
// TODO Once permissions are implemented add check for each project here.
else
{
return new Tuple<Project, string>(project, "");
}
}).ToList();

await _projectRepository.DeleteManyByIdAsync(ids);
return results;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.SecretManagerFeatures.Projects.Interfaces;

namespace Bit.Commercial.Core.SecretManagerFeatures.Projects;

public class UpdateProjectCommand : IUpdateProjectCommand
{
private readonly IProjectRepository _projectRepository;

public UpdateProjectCommand(IProjectRepository projectRepository)
{
_projectRepository = projectRepository;
}

public async Task<Project> UpdateAsync(Project project)
{
var existingProject = await _projectRepository.GetByIdAsync(project.Id);
if (existingProject == null)
{
throw new NotFoundException();
}

project.OrganizationId = existingProject.OrganizationId;
project.CreationDate = existingProject.CreationDate;
project.DeletedDate = existingProject.DeletedDate;
project.RevisionDate = DateTime.UtcNow;

await _projectRepository.ReplaceAsync(project);
return project;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Bit.Commercial.Core.SecretManagerFeatures.AccessTokens;
using Bit.Commercial.Core.SecretManagerFeatures.Projects;
using Bit.Commercial.Core.SecretManagerFeatures.Secrets;
using Bit.Commercial.Core.SecretManagerFeatures.ServiceAccounts;
using Bit.Core.SecretManagerFeatures.AccessTokens.Interfaces;
using Bit.Core.SecretManagerFeatures.Projects.Interfaces;
using Bit.Core.SecretManagerFeatures.Secrets.Interfaces;
using Bit.Core.SecretManagerFeatures.ServiceAccounts.Interfaces;
using Microsoft.Extensions.DependencyInjection;

namespace Bit.Commercial.Core.SecretManagerFeatures;

public static class SecretManagerCollectionExtensions
{
public static void AddSecretManagerServices(this IServiceCollection services)
{
services.AddScoped<ICreateSecretCommand, CreateSecretCommand>();
services.AddScoped<IUpdateSecretCommand, UpdateSecretCommand>();
services.AddScoped<IDeleteSecretCommand, DeleteSecretCommand>();
services.AddScoped<ICreateProjectCommand, CreateProjectCommand>();
services.AddScoped<IUpdateProjectCommand, UpdateProjectCommand>();
services.AddScoped<IDeleteProjectCommand, DeleteProjectCommand>();
services.AddScoped<ICreateServiceAccountCommand, CreateServiceAccountCommand>();
services.AddScoped<IUpdateServiceAccountCommand, UpdateServiceAccountCommand>();
services.AddScoped<ICreateAccessTokenCommand, CreateAccessTokenCommand>();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.SecretManagerFeatures.Secrets.Interfaces;

namespace Bit.Commercial.Core.SecretManagerFeatures.Secrets;

public class CreateSecretCommand : ICreateSecretCommand
{
private readonly ISecretRepository _secretRepository;

public CreateSecretCommand(ISecretRepository secretRepository)
{
_secretRepository = secretRepository;
}

public async Task<Secret> CreateAsync(Secret secret)
{
return await _secretRepository.CreateAsync(secret);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.SecretManagerFeatures.Secrets.Interfaces;

namespace Bit.Commercial.Core.SecretManagerFeatures.Secrets;

public class DeleteSecretCommand : IDeleteSecretCommand
{
private readonly ISecretRepository _secretRepository;

public DeleteSecretCommand(ISecretRepository secretRepository)
{
_secretRepository = secretRepository;
}

public async Task<List<Tuple<Secret, string>>> DeleteSecrets(List<Guid> ids)
{
var secrets = await _secretRepository.GetManyByIds(ids);

if (secrets?.Any() != true)
{
throw new NotFoundException();
}

var results = ids.Select(id =>
{
var secret = secrets.FirstOrDefault(secret => secret.Id == id);
if (secret == null)
{
throw new NotFoundException();
}
// TODO Once permissions are implemented add check for each secret here.
else
{
return new Tuple<Secret, string>(secret, "");
}
}).ToList();

await _secretRepository.SoftDeleteManyByIdAsync(ids);
return results;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.SecretManagerFeatures.Secrets.Interfaces;

namespace Bit.Commercial.Core.SecretManagerFeatures.Secrets;

public class UpdateSecretCommand : IUpdateSecretCommand
{
private readonly ISecretRepository _secretRepository;

public UpdateSecretCommand(ISecretRepository secretRepository)
{
_secretRepository = secretRepository;
}

public async Task<Secret> UpdateAsync(Secret secret)
{
var existingSecret = await _secretRepository.GetByIdAsync(secret.Id);
if (existingSecret == null)
{
throw new NotFoundException();
}

secret.OrganizationId = existingSecret.OrganizationId;
secret.CreationDate = existingSecret.CreationDate;
secret.DeletedDate = existingSecret.DeletedDate;
secret.RevisionDate = DateTime.UtcNow;

await _secretRepository.UpdateAsync(secret);
return secret;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.SecretManagerFeatures.ServiceAccounts.Interfaces;

namespace Bit.Commercial.Core.SecretManagerFeatures.ServiceAccounts;

public class CreateServiceAccountCommand : ICreateServiceAccountCommand
{
private readonly IServiceAccountRepository _serviceAccountRepository;

public CreateServiceAccountCommand(IServiceAccountRepository serviceAccountRepository)
{
_serviceAccountRepository = serviceAccountRepository;
}

public async Task<ServiceAccount> CreateAsync(ServiceAccount serviceAccount)
{
return await _serviceAccountRepository.CreateAsync(serviceAccount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.SecretManagerFeatures.ServiceAccounts.Interfaces;

namespace Bit.Commercial.Core.SecretManagerFeatures.ServiceAccounts;

public class UpdateServiceAccountCommand : IUpdateServiceAccountCommand
{
private readonly IServiceAccountRepository _serviceAccountRepository;

public UpdateServiceAccountCommand(IServiceAccountRepository serviceAccountRepository)
{
_serviceAccountRepository = serviceAccountRepository;
}

public async Task<ServiceAccount> UpdateAsync(ServiceAccount serviceAccount)
{
var existingServiceAccount = await _serviceAccountRepository.GetByIdAsync(serviceAccount.Id);
if (existingServiceAccount == null)
{
throw new NotFoundException();
}

serviceAccount.OrganizationId = existingServiceAccount.OrganizationId;
serviceAccount.CreationDate = existingServiceAccount.CreationDate;
serviceAccount.RevisionDate = DateTime.UtcNow;

await _serviceAccountRepository.ReplaceAsync(serviceAccount);
return serviceAccount;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
using Bit.Commercial.Core.Services;
using Bit.Commercial.Core.SecretManagerFeatures;
using Bit.Commercial.Core.Services;
using Bit.Core.Services;
using Microsoft.Extensions.DependencyInjection;

namespace Bit.Commercial.Core.Utilities;

public static class ServiceCollectionExtensions
{
public static void AddCommCoreServices(this IServiceCollection services)
public static void AddCommercialCoreServices(this IServiceCollection services)
{
services.AddScoped<IProviderService, ProviderService>();
}

public static void AddCommercialSecretsManagerServices(this IServiceCollection services)
{
services.AddSecretManagerServices();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Core\Core.csproj" />
<ProjectReference Include="..\..\..\src\Infrastructure.EntityFramework\Infrastructure.EntityFramework.csproj" />
</ItemGroup>
</Project>
Loading

0 comments on commit 1f0fc43

Please sign in to comment.