mirror of
https://github.com/bitwarden/server.git
synced 2025-07-04 17:42:49 -05:00
Merge branch 'master' into feature/billing-obfuscation
This commit is contained in:
@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<!--2022.6.2-->
|
<!--2022.6.2-->
|
||||||
<Version>2023.2.1</Version>
|
<Version>2023.2.2</Version>
|
||||||
<RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace>
|
<RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace>
|
||||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
@ -278,6 +278,10 @@ Global
|
|||||||
{07143DFA-F242-47A4-A15E-39C9314D4140}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{07143DFA-F242-47A4-A15E-39C9314D4140}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{07143DFA-F242-47A4-A15E-39C9314D4140}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{07143DFA-F242-47A4-A15E-39C9314D4140}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{07143DFA-F242-47A4-A15E-39C9314D4140}.Release|Any CPU.Build.0 = Release|Any CPU
|
{07143DFA-F242-47A4-A15E-39C9314D4140}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D9A2CCBB-FB0A-4BBA-A9ED-BA9FF277C880}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D9A2CCBB-FB0A-4BBA-A9ED-BA9FF277C880}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D9A2CCBB-FB0A-4BBA-A9ED-BA9FF277C880}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D9A2CCBB-FB0A-4BBA-A9ED-BA9FF277C880}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -322,6 +326,7 @@ Global
|
|||||||
{B1595DA3-4C60-41AA-8BF0-499A5F75A885} = {287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}
|
{B1595DA3-4C60-41AA-8BF0-499A5F75A885} = {287CFF34-BBDB-4BC4-AF88-1E19A5A4679B}
|
||||||
{7E9A7DD5-EB78-4AC5-BFD5-64573FD2533B} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
{7E9A7DD5-EB78-4AC5-BFD5-64573FD2533B} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
||||||
{07143DFA-F242-47A4-A15E-39C9314D4140} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
|
{07143DFA-F242-47A4-A15E-39C9314D4140} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
|
||||||
|
{D9A2CCBB-FB0A-4BBA-A9ED-BA9FF277C880} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {E01CBF68-2E20-425F-9EDB-E0A6510CA92F}
|
SolutionGuid = {E01CBF68-2E20-425F-9EDB-E0A6510CA92F}
|
||||||
|
@ -26,10 +26,15 @@ public class CreateSecretCommand : ICreateSecretCommand
|
|||||||
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
var project = secret.Projects?.FirstOrDefault();
|
var project = secret.Projects?.FirstOrDefault();
|
||||||
|
|
||||||
|
if (project == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
var hasAccess = accessClient switch
|
var hasAccess = accessClient switch
|
||||||
{
|
{
|
||||||
AccessClientType.NoAccessCheck => true,
|
AccessClientType.NoAccessCheck => true,
|
||||||
AccessClientType.User => project != null && await _projectRepository.UserHasWriteAccessToProject(project.Id, userId),
|
AccessClientType.User => await _projectRepository.UserHasWriteAccessToProject(project.Id, userId),
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,16 +31,7 @@ public class UpdateSecretCommand : IUpdateSecretCommand
|
|||||||
var orgAdmin = await _currentContext.OrganizationAdmin(secret.OrganizationId);
|
var orgAdmin = await _currentContext.OrganizationAdmin(secret.OrganizationId);
|
||||||
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
|
||||||
var project = updatedSecret.Projects?.FirstOrDefault();
|
if (!await HasAccessToOriginalAndUpdatedProject(accessClient, secret, updatedSecret, userId))
|
||||||
|
|
||||||
var hasAccess = accessClient switch
|
|
||||||
{
|
|
||||||
AccessClientType.NoAccessCheck => true,
|
|
||||||
AccessClientType.User => project != null && await _projectRepository.UserHasWriteAccessToProject(project.Id, userId),
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!hasAccess)
|
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
@ -54,4 +45,21 @@ public class UpdateSecretCommand : IUpdateSecretCommand
|
|||||||
await _secretRepository.UpdateAsync(secret);
|
await _secretRepository.UpdateAsync(secret);
|
||||||
return secret;
|
return secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> HasAccessToOriginalAndUpdatedProject(AccessClientType accessClient, Secret secret, Secret updatedSecret, Guid userId)
|
||||||
|
{
|
||||||
|
switch (accessClient)
|
||||||
|
{
|
||||||
|
case AccessClientType.NoAccessCheck:
|
||||||
|
return true;
|
||||||
|
case AccessClientType.User:
|
||||||
|
var oldProject = secret.Projects?.FirstOrDefault();
|
||||||
|
var newProject = updatedSecret.Projects?.FirstOrDefault();
|
||||||
|
var accessToOld = oldProject != null && await _projectRepository.UserHasWriteAccessToProject(oldProject.Id, userId);
|
||||||
|
var accessToNew = newProject != null && await _projectRepository.UserHasWriteAccessToProject(newProject.Id, userId);
|
||||||
|
return accessToOld && accessToNew;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
|
||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
|
||||||
|
namespace Bit.Commercial.Core.SecretsManager.Commands.ServiceAccounts;
|
||||||
|
|
||||||
|
public class DeleteServiceAccountsCommand : IDeleteServiceAccountsCommand
|
||||||
|
{
|
||||||
|
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
|
|
||||||
|
public DeleteServiceAccountsCommand(
|
||||||
|
IServiceAccountRepository serviceAccountRepository,
|
||||||
|
ICurrentContext currentContext)
|
||||||
|
{
|
||||||
|
_serviceAccountRepository = serviceAccountRepository;
|
||||||
|
_currentContext = currentContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<Tuple<ServiceAccount, string>>> DeleteServiceAccounts(List<Guid> ids, Guid userId)
|
||||||
|
{
|
||||||
|
if (ids.Any() != true || userId == new Guid())
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceAccounts = (await _serviceAccountRepository.GetManyByIds(ids))?.ToList();
|
||||||
|
|
||||||
|
if (serviceAccounts?.Any() != true || serviceAccounts.Count != ids.Count)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure all service accounts belongs to the same organization
|
||||||
|
var organizationId = serviceAccounts.First().OrganizationId;
|
||||||
|
if (serviceAccounts.Any(p => p.OrganizationId != organizationId))
|
||||||
|
{
|
||||||
|
throw new BadRequestException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
||||||
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
|
||||||
|
var results = new List<Tuple<ServiceAccount, String>>(serviceAccounts.Count);
|
||||||
|
var deleteIds = new List<Guid>();
|
||||||
|
|
||||||
|
foreach (var sa in serviceAccounts)
|
||||||
|
{
|
||||||
|
var hasAccess = accessClient switch
|
||||||
|
{
|
||||||
|
AccessClientType.NoAccessCheck => true,
|
||||||
|
AccessClientType.User => await _serviceAccountRepository.UserHasWriteAccessToServiceAccount(sa.Id, userId),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!hasAccess)
|
||||||
|
{
|
||||||
|
results.Add(new Tuple<ServiceAccount, string>(sa, "access denied"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
results.Add(new Tuple<ServiceAccount, string>(sa, ""));
|
||||||
|
deleteIds.Add(sa.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deleteIds.Count > 0)
|
||||||
|
{
|
||||||
|
await _serviceAccountRepository.DeleteManyByIdAsync(deleteIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -28,6 +28,7 @@ public static class SecretsManagerCollectionExtensions
|
|||||||
services.AddScoped<IDeleteProjectCommand, DeleteProjectCommand>();
|
services.AddScoped<IDeleteProjectCommand, DeleteProjectCommand>();
|
||||||
services.AddScoped<ICreateServiceAccountCommand, CreateServiceAccountCommand>();
|
services.AddScoped<ICreateServiceAccountCommand, CreateServiceAccountCommand>();
|
||||||
services.AddScoped<IUpdateServiceAccountCommand, UpdateServiceAccountCommand>();
|
services.AddScoped<IUpdateServiceAccountCommand, UpdateServiceAccountCommand>();
|
||||||
|
services.AddScoped<IDeleteServiceAccountsCommand, DeleteServiceAccountsCommand>();
|
||||||
services.AddScoped<IRevokeAccessTokensCommand, RevokeAccessTokensCommand>();
|
services.AddScoped<IRevokeAccessTokensCommand, RevokeAccessTokensCommand>();
|
||||||
services.AddScoped<ICreateAccessTokenCommand, CreateAccessTokenCommand>();
|
services.AddScoped<ICreateAccessTokenCommand, CreateAccessTokenCommand>();
|
||||||
services.AddScoped<ICreateAccessPoliciesCommand, CreateAccessPoliciesCommand>();
|
services.AddScoped<ICreateAccessPoliciesCommand, CreateAccessPoliciesCommand>();
|
||||||
|
@ -157,7 +157,7 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>> GetManyByGrantedProjectIdAsync(Guid id)
|
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>> GetManyByGrantedProjectIdAsync(Guid id, Guid userId)
|
||||||
{
|
{
|
||||||
using var scope = ServiceScopeFactory.CreateScope();
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
@ -169,11 +169,19 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
|
|||||||
.Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User)
|
.Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User)
|
||||||
.Include(ap => ((GroupProjectAccessPolicy)ap).Group)
|
.Include(ap => ((GroupProjectAccessPolicy)ap).Group)
|
||||||
.Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount)
|
.Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount)
|
||||||
|
.Select(ap => new
|
||||||
|
{
|
||||||
|
ap,
|
||||||
|
CurrentUserInGroup = ap is GroupProjectAccessPolicy &&
|
||||||
|
((GroupProjectAccessPolicy)ap).Group.GroupUsers.Any(g =>
|
||||||
|
g.OrganizationUser.User.Id == userId),
|
||||||
|
})
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
return entities.Select(MapToCore);
|
|
||||||
|
return entities.Select(e => MapToCore(e.ap, e.CurrentUserInGroup));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>> GetManyByGrantedServiceAccountIdAsync(Guid id)
|
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>> GetManyByGrantedServiceAccountIdAsync(Guid id, Guid userId)
|
||||||
{
|
{
|
||||||
using var scope = ServiceScopeFactory.CreateScope();
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
@ -183,9 +191,16 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
|
|||||||
((GroupServiceAccountAccessPolicy)ap).GrantedServiceAccountId == id)
|
((GroupServiceAccountAccessPolicy)ap).GrantedServiceAccountId == id)
|
||||||
.Include(ap => ((UserServiceAccountAccessPolicy)ap).OrganizationUser.User)
|
.Include(ap => ((UserServiceAccountAccessPolicy)ap).OrganizationUser.User)
|
||||||
.Include(ap => ((GroupServiceAccountAccessPolicy)ap).Group)
|
.Include(ap => ((GroupServiceAccountAccessPolicy)ap).Group)
|
||||||
|
.Select(ap => new
|
||||||
|
{
|
||||||
|
ap,
|
||||||
|
CurrentUserInGroup = ap is GroupServiceAccountAccessPolicy &&
|
||||||
|
((GroupServiceAccountAccessPolicy)ap).Group.GroupUsers.Any(g =>
|
||||||
|
g.OrganizationUser.User.Id == userId),
|
||||||
|
})
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return entities.Select(MapToCore);
|
return entities.Select(e => MapToCore(e.ap, e.CurrentUserInGroup));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteAsync(Guid id)
|
public async Task DeleteAsync(Guid id)
|
||||||
@ -237,4 +252,26 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
|
|||||||
.Map<Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy>(ap),
|
.Map<Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy>(ap),
|
||||||
_ => throw new ArgumentException("Unsupported access policy type"),
|
_ => throw new ArgumentException("Unsupported access policy type"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private Core.SecretsManager.Entities.BaseAccessPolicy MapToCore(
|
||||||
|
BaseAccessPolicy baseAccessPolicyEntity, bool currentUserInGroup)
|
||||||
|
{
|
||||||
|
switch (baseAccessPolicyEntity)
|
||||||
|
{
|
||||||
|
case GroupProjectAccessPolicy ap:
|
||||||
|
{
|
||||||
|
var mapped = Mapper.Map<Core.SecretsManager.Entities.GroupProjectAccessPolicy>(ap);
|
||||||
|
mapped.CurrentUserInGroup = currentUserInGroup;
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
case GroupServiceAccountAccessPolicy ap:
|
||||||
|
{
|
||||||
|
var mapped = Mapper.Map<Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy>(ap);
|
||||||
|
mapped.CurrentUserInGroup = currentUserInGroup;
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return MapToCore(baseAccessPolicyEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,16 @@ public class ServiceAccountRepository : Repository<Core.SecretsManager.Entities.
|
|||||||
return Mapper.Map<List<Core.SecretsManager.Entities.ServiceAccount>>(serviceAccounts);
|
return Mapper.Map<List<Core.SecretsManager.Entities.ServiceAccount>>(serviceAccounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Core.SecretsManager.Entities.ServiceAccount>> GetManyByIds(IEnumerable<Guid> ids)
|
||||||
|
{
|
||||||
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
var serviceAccounts = await dbContext.ServiceAccount
|
||||||
|
.Where(c => ids.Contains(c.Id))
|
||||||
|
.ToListAsync();
|
||||||
|
return Mapper.Map<List<Core.SecretsManager.Entities.ServiceAccount>>(serviceAccounts);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> UserHasReadAccessToServiceAccount(Guid id, Guid userId)
|
public async Task<bool> UserHasReadAccessToServiceAccount(Guid id, Guid userId)
|
||||||
{
|
{
|
||||||
using var scope = ServiceScopeFactory.CreateScope();
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
@ -71,6 +81,26 @@ public class ServiceAccountRepository : Repository<Core.SecretsManager.Entities.
|
|||||||
return Mapper.Map<List<Core.SecretsManager.Entities.ServiceAccount>>(serviceAccounts);
|
return Mapper.Map<List<Core.SecretsManager.Entities.ServiceAccount>>(serviceAccounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task DeleteManyByIdAsync(IEnumerable<Guid> ids)
|
||||||
|
{
|
||||||
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
|
||||||
|
// Policies can't have a cascade delete, so we need to delete them manually.
|
||||||
|
var policies = dbContext.AccessPolicies.Where(ap =>
|
||||||
|
((ServiceAccountProjectAccessPolicy)ap).ServiceAccountId.HasValue && ids.Contains(((ServiceAccountProjectAccessPolicy)ap).ServiceAccountId!.Value) ||
|
||||||
|
((GroupServiceAccountAccessPolicy)ap).GrantedServiceAccountId.HasValue && ids.Contains(((GroupServiceAccountAccessPolicy)ap).GrantedServiceAccountId!.Value) ||
|
||||||
|
((UserServiceAccountAccessPolicy)ap).GrantedServiceAccountId.HasValue && ids.Contains(((UserServiceAccountAccessPolicy)ap).GrantedServiceAccountId!.Value));
|
||||||
|
dbContext.RemoveRange(policies);
|
||||||
|
|
||||||
|
var apiKeys = dbContext.ApiKeys.Where(a => a.ServiceAccountId.HasValue && ids.Contains(a.ServiceAccountId!.Value));
|
||||||
|
dbContext.RemoveRange(apiKeys);
|
||||||
|
|
||||||
|
var serviceAccounts = dbContext.ServiceAccount.Where(c => ids.Contains(c.Id));
|
||||||
|
dbContext.RemoveRange(serviceAccounts);
|
||||||
|
await dbContext.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
private static Expression<Func<ServiceAccount, bool>> UserHasReadAccessToServiceAccount(Guid userId) => sa =>
|
private static Expression<Func<ServiceAccount, bool>> UserHasReadAccessToServiceAccount(Guid userId) => sa =>
|
||||||
sa.UserAccessPolicies.Any(ap => ap.OrganizationUser.User.Id == userId && ap.Read) ||
|
sa.UserAccessPolicies.Any(ap => ap.OrganizationUser.User.Id == userId && ap.Read) ||
|
||||||
sa.GroupAccessPolicies.Any(ap => ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Read));
|
sa.GroupAccessPolicies.Any(ap => ap.Group.GroupUsers.Any(gu => gu.OrganizationUser.User.Id == userId && ap.Read));
|
||||||
|
@ -35,7 +35,9 @@ mkdir -p /etc/bitwarden/logs
|
|||||||
mkdir -p /etc/bitwarden/ca-certificates
|
mkdir -p /etc/bitwarden/ca-certificates
|
||||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||||
|
|
||||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||||
|
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
||||||
&& update-ca-certificates
|
&& update-ca-certificates
|
||||||
|
fi
|
||||||
|
|
||||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/Scim.dll
|
exec gosu $USERNAME:$GROUPNAME dotnet /app/Scim.dll
|
||||||
|
@ -35,10 +35,15 @@ mkdir -p /etc/bitwarden/logs
|
|||||||
mkdir -p /etc/bitwarden/ca-certificates
|
mkdir -p /etc/bitwarden/ca-certificates
|
||||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||||
|
|
||||||
cp /etc/bitwarden/identity/identity.pfx /app/identity.pfx
|
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||||
|
cp /etc/bitwarden/identity/identity.pfx /app/identity.pfx
|
||||||
|
fi
|
||||||
|
|
||||||
chown -R $USERNAME:$GROUPNAME /app
|
chown -R $USERNAME:$GROUPNAME /app
|
||||||
|
|
||||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||||
|
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
||||||
&& update-ca-certificates
|
&& update-ca-certificates
|
||||||
|
fi
|
||||||
|
|
||||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/Sso.dll
|
exec gosu $USERNAME:$GROUPNAME dotnet /app/Sso.dll
|
||||||
|
@ -34,6 +34,7 @@ public class UpdateSecretCommandTests
|
|||||||
public async Task UpdateAsync_Success(PermissionType permissionType, Secret data, SutProvider<UpdateSecretCommand> sutProvider, Guid userId, Project mockProject)
|
public async Task UpdateAsync_Success(PermissionType permissionType, Secret data, SutProvider<UpdateSecretCommand> sutProvider, Guid userId, Project mockProject)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(data.OrganizationId).Returns(true);
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(data.OrganizationId).Returns(true);
|
||||||
|
data.Projects = new List<Project>() { mockProject };
|
||||||
|
|
||||||
if (permissionType == PermissionType.RunAsAdmin)
|
if (permissionType == PermissionType.RunAsAdmin)
|
||||||
{
|
{
|
||||||
@ -41,7 +42,6 @@ public class UpdateSecretCommandTests
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
data.Projects = new List<Project>() { mockProject };
|
|
||||||
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(data.OrganizationId).Returns(false);
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(data.OrganizationId).Returns(false);
|
||||||
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject((Guid)(data.Projects?.First().Id), userId).Returns(true);
|
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject((Guid)(data.Projects?.First().Id), userId).Returns(true);
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,9 @@ mkdir -p /etc/bitwarden/logs
|
|||||||
mkdir -p /etc/bitwarden/ca-certificates
|
mkdir -p /etc/bitwarden/ca-certificates
|
||||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||||
|
|
||||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||||
|
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
||||||
&& update-ca-certificates
|
&& update-ca-certificates
|
||||||
|
fi
|
||||||
|
|
||||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/Admin.dll
|
exec gosu $USERNAME:$GROUPNAME dotnet /app/Admin.dll
|
||||||
|
@ -4,6 +4,7 @@ using Bit.Core.Entities;
|
|||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
@ -130,6 +131,14 @@ public class DevicesController : Controller
|
|||||||
await _deviceService.DeleteAsync(device);
|
await _deviceService.DeleteAsync(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[HttpGet("knowndevice")]
|
||||||
|
public async Task<bool> GetByIdentifierQuery(
|
||||||
|
[FromHeader(Name = "X-Request-Email")] string email,
|
||||||
|
[FromHeader(Name = "X-Device-Identifier")] string deviceIdentifier)
|
||||||
|
=> await GetByIdentifier(CoreHelpers.Base64UrlDecodeString(email), deviceIdentifier);
|
||||||
|
|
||||||
|
[Obsolete("Path is deprecated due to encoding issues, use /knowndevice instead.")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpGet("knowndevice/{email}/{identifier}")]
|
[HttpGet("knowndevice/{email}/{identifier}")]
|
||||||
public async Task<bool> GetByIdentifier(string email, string identifier)
|
public async Task<bool> GetByIdentifier(string email, string identifier)
|
||||||
|
@ -5,7 +5,6 @@ using Bit.Api.Models.Request.Organizations;
|
|||||||
using Bit.Api.Models.Response;
|
using Bit.Api.Models.Response;
|
||||||
using Bit.Api.Models.Response.Organizations;
|
using Bit.Api.Models.Response.Organizations;
|
||||||
using Bit.Api.SecretsManager;
|
using Bit.Api.SecretsManager;
|
||||||
using Bit.Api.Utilities;
|
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
@ -225,28 +224,6 @@ public class OrganizationsController : Controller
|
|||||||
return new OrganizationResponseModel(result.Item1);
|
return new OrganizationResponseModel(result.Item1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("2022-12-7 Moved to SelfHostedOrganizationLicensesController, to be removed in EC-815")]
|
|
||||||
[HttpPost("license")]
|
|
||||||
[SelfHosted(SelfHostedOnly = true)]
|
|
||||||
public async Task<OrganizationResponseModel> PostLicense(OrganizationCreateLicenseRequestModel model)
|
|
||||||
{
|
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
throw new UnauthorizedAccessException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var license = await ApiHelpers.ReadJsonFileFromBody<OrganizationLicense>(HttpContext, model.License);
|
|
||||||
if (license == null)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Invalid license");
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = await _organizationService.SignUpAsync(license, user, model.Key,
|
|
||||||
model.CollectionName, model.Keys?.PublicKey, model.Keys?.EncryptedPrivateKey);
|
|
||||||
return new OrganizationResponseModel(result.Item1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPut("{id}")]
|
[HttpPut("{id}")]
|
||||||
[HttpPost("{id}")]
|
[HttpPost("{id}")]
|
||||||
public async Task<OrganizationResponseModel> Put(string id, [FromBody] OrganizationUpdateRequestModel model)
|
public async Task<OrganizationResponseModel> Put(string id, [FromBody] OrganizationUpdateRequestModel model)
|
||||||
@ -447,34 +424,6 @@ public class OrganizationsController : Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Obsolete("2022-12-7 Moved to SelfHostedOrganizationLicensesController, to be removed in EC-815")]
|
|
||||||
[HttpPost("{id}/license")]
|
|
||||||
[SelfHosted(SelfHostedOnly = true)]
|
|
||||||
public async Task PostLicense(string id, LicenseRequestModel model)
|
|
||||||
{
|
|
||||||
var orgIdGuid = new Guid(id);
|
|
||||||
if (!await _currentContext.OrganizationOwner(orgIdGuid))
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var license = await ApiHelpers.ReadJsonFileFromBody<OrganizationLicense>(HttpContext, model.License);
|
|
||||||
if (license == null)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Invalid license");
|
|
||||||
}
|
|
||||||
|
|
||||||
var selfHostedOrganizationDetails = await _organizationRepository.GetSelfHostedOrganizationDetailsById(orgIdGuid);
|
|
||||||
if (selfHostedOrganizationDetails == null)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var existingOrganization = await _organizationRepository.GetByLicenseKeyAsync(license.LicenseKey);
|
|
||||||
|
|
||||||
await _updateOrganizationLicenseCommand.UpdateLicenseAsync(selfHostedOrganizationDetails, license, existingOrganization);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{id}/import")]
|
[HttpPost("{id}/import")]
|
||||||
public async Task Import(string id, [FromBody] ImportOrganizationUsersRequestModel model)
|
public async Task Import(string id, [FromBody] ImportOrganizationUsersRequestModel model)
|
||||||
{
|
{
|
||||||
|
@ -73,7 +73,7 @@ public class AccessPoliciesController : Controller
|
|||||||
var (accessClient, userId) = await GetAccessClientTypeAsync(project.OrganizationId);
|
var (accessClient, userId) = await GetAccessClientTypeAsync(project.OrganizationId);
|
||||||
var policies = request.ToBaseAccessPoliciesForProject(id);
|
var policies = request.ToBaseAccessPoliciesForProject(id);
|
||||||
await _createAccessPoliciesCommand.CreateManyAsync(policies, userId, accessClient);
|
await _createAccessPoliciesCommand.CreateManyAsync(policies, userId, accessClient);
|
||||||
var results = await _accessPolicyRepository.GetManyByGrantedProjectIdAsync(id);
|
var results = await _accessPolicyRepository.GetManyByGrantedProjectIdAsync(id, userId);
|
||||||
return new ProjectAccessPoliciesResponseModel(results);
|
return new ProjectAccessPoliciesResponseModel(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,9 +81,8 @@ public class AccessPoliciesController : Controller
|
|||||||
public async Task<ProjectAccessPoliciesResponseModel> GetProjectAccessPoliciesAsync([FromRoute] Guid id)
|
public async Task<ProjectAccessPoliciesResponseModel> GetProjectAccessPoliciesAsync([FromRoute] Guid id)
|
||||||
{
|
{
|
||||||
var project = await _projectRepository.GetByIdAsync(id);
|
var project = await _projectRepository.GetByIdAsync(id);
|
||||||
await CheckUserHasWriteAccessToProjectAsync(project);
|
var (_, userId) = await CheckUserHasWriteAccessToProjectAsync(project);
|
||||||
|
var results = await _accessPolicyRepository.GetManyByGrantedProjectIdAsync(id, userId);
|
||||||
var results = await _accessPolicyRepository.GetManyByGrantedProjectIdAsync(id);
|
|
||||||
return new ProjectAccessPoliciesResponseModel(results);
|
return new ProjectAccessPoliciesResponseModel(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +105,7 @@ public class AccessPoliciesController : Controller
|
|||||||
var (accessClient, userId) = await GetAccessClientTypeAsync(serviceAccount.OrganizationId);
|
var (accessClient, userId) = await GetAccessClientTypeAsync(serviceAccount.OrganizationId);
|
||||||
var policies = request.ToBaseAccessPoliciesForServiceAccount(id);
|
var policies = request.ToBaseAccessPoliciesForServiceAccount(id);
|
||||||
await _createAccessPoliciesCommand.CreateManyAsync(policies, userId, accessClient);
|
await _createAccessPoliciesCommand.CreateManyAsync(policies, userId, accessClient);
|
||||||
var results = await _accessPolicyRepository.GetManyByGrantedServiceAccountIdAsync(id);
|
var results = await _accessPolicyRepository.GetManyByGrantedServiceAccountIdAsync(id, userId);
|
||||||
return new ServiceAccountAccessPoliciesResponseModel(results);
|
return new ServiceAccountAccessPoliciesResponseModel(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,9 +114,8 @@ public class AccessPoliciesController : Controller
|
|||||||
[FromRoute] Guid id)
|
[FromRoute] Guid id)
|
||||||
{
|
{
|
||||||
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(id);
|
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(id);
|
||||||
await CheckUserHasWriteAccessToServiceAccountAsync(serviceAccount);
|
var (_, userId) = await CheckUserHasWriteAccessToServiceAccountAsync(serviceAccount);
|
||||||
|
var results = await _accessPolicyRepository.GetManyByGrantedServiceAccountIdAsync(id, userId);
|
||||||
var results = await _accessPolicyRepository.GetManyByGrantedServiceAccountIdAsync(id);
|
|
||||||
return new ServiceAccountAccessPoliciesResponseModel(results);
|
return new ServiceAccountAccessPoliciesResponseModel(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +204,7 @@ public class AccessPoliciesController : Controller
|
|||||||
var organizationUsers =
|
var organizationUsers =
|
||||||
await _organizationUserRepository.GetManyDetailsByOrganizationAsync(id);
|
await _organizationUserRepository.GetManyDetailsByOrganizationAsync(id);
|
||||||
var userResponses = organizationUsers
|
var userResponses = organizationUsers
|
||||||
.Where(user => user.AccessSecretsManager)
|
.Where(user => user.AccessSecretsManager && user.Status == OrganizationUserStatusType.Confirmed)
|
||||||
.Select(userDetails => new PotentialGranteeResponseModel(userDetails));
|
.Select(userDetails => new PotentialGranteeResponseModel(userDetails));
|
||||||
|
|
||||||
return new ListResponseModel<PotentialGranteeResponseModel>(userResponses.Concat(groupResponses));
|
return new ListResponseModel<PotentialGranteeResponseModel>(userResponses.Concat(groupResponses));
|
||||||
@ -244,7 +242,7 @@ public class AccessPoliciesController : Controller
|
|||||||
return new ListResponseModel<PotentialGranteeResponseModel>(projectResponses);
|
return new ListResponseModel<PotentialGranteeResponseModel>(projectResponses);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CheckUserHasWriteAccessToProjectAsync(Project project)
|
private async Task<(AccessClientType AccessClientType, Guid UserId)> CheckUserHasWriteAccessToProjectAsync(Project project)
|
||||||
{
|
{
|
||||||
if (project == null)
|
if (project == null)
|
||||||
{
|
{
|
||||||
@ -263,9 +261,10 @@ public class AccessPoliciesController : Controller
|
|||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
return (accessClient, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CheckUserHasWriteAccessToServiceAccountAsync(ServiceAccount serviceAccount)
|
private async Task<(AccessClientType AccessClientType, Guid UserId)> CheckUserHasWriteAccessToServiceAccountAsync(ServiceAccount serviceAccount)
|
||||||
{
|
{
|
||||||
if (serviceAccount == null)
|
if (serviceAccount == null)
|
||||||
{
|
{
|
||||||
@ -285,6 +284,7 @@ public class AccessPoliciesController : Controller
|
|||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
return (accessClient, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<(AccessClientType AccessClientType, Guid UserId)> GetAccessClientTypeAsync(Guid organizationId)
|
private async Task<(AccessClientType AccessClientType, Guid UserId)> GetAccessClientTypeAsync(Guid organizationId)
|
||||||
|
@ -61,6 +61,11 @@ public class SecretsManagerPortingController : Controller
|
|||||||
throw new BadRequestException("You cannot import this much data at once, the limit is 1000 projects and 6000 secrets.");
|
throw new BadRequestException("You cannot import this much data at once, the limit is 1000 projects and 6000 secrets.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (importRequest.Secrets.Any(s => s.ProjectIds.Count() > 1))
|
||||||
|
{
|
||||||
|
throw new BadRequestException("A secret can only be in one project at a time.");
|
||||||
|
}
|
||||||
|
|
||||||
await _importCommand.ImportAsync(organizationId, importRequest.ToSMImport());
|
await _importCommand.ImportAsync(organizationId, importRequest.ToSMImport());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ public class ServiceAccountsController : Controller
|
|||||||
private readonly ICreateAccessTokenCommand _createAccessTokenCommand;
|
private readonly ICreateAccessTokenCommand _createAccessTokenCommand;
|
||||||
private readonly ICreateServiceAccountCommand _createServiceAccountCommand;
|
private readonly ICreateServiceAccountCommand _createServiceAccountCommand;
|
||||||
private readonly IUpdateServiceAccountCommand _updateServiceAccountCommand;
|
private readonly IUpdateServiceAccountCommand _updateServiceAccountCommand;
|
||||||
|
private readonly IDeleteServiceAccountsCommand _deleteServiceAccountsCommand;
|
||||||
private readonly IRevokeAccessTokensCommand _revokeAccessTokensCommand;
|
private readonly IRevokeAccessTokensCommand _revokeAccessTokensCommand;
|
||||||
|
|
||||||
public ServiceAccountsController(
|
public ServiceAccountsController(
|
||||||
@ -35,6 +36,7 @@ public class ServiceAccountsController : Controller
|
|||||||
ICreateAccessTokenCommand createAccessTokenCommand,
|
ICreateAccessTokenCommand createAccessTokenCommand,
|
||||||
ICreateServiceAccountCommand createServiceAccountCommand,
|
ICreateServiceAccountCommand createServiceAccountCommand,
|
||||||
IUpdateServiceAccountCommand updateServiceAccountCommand,
|
IUpdateServiceAccountCommand updateServiceAccountCommand,
|
||||||
|
IDeleteServiceAccountsCommand deleteServiceAccountsCommand,
|
||||||
IRevokeAccessTokensCommand revokeAccessTokensCommand)
|
IRevokeAccessTokensCommand revokeAccessTokensCommand)
|
||||||
{
|
{
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
@ -43,6 +45,7 @@ public class ServiceAccountsController : Controller
|
|||||||
_apiKeyRepository = apiKeyRepository;
|
_apiKeyRepository = apiKeyRepository;
|
||||||
_createServiceAccountCommand = createServiceAccountCommand;
|
_createServiceAccountCommand = createServiceAccountCommand;
|
||||||
_updateServiceAccountCommand = updateServiceAccountCommand;
|
_updateServiceAccountCommand = updateServiceAccountCommand;
|
||||||
|
_deleteServiceAccountsCommand = deleteServiceAccountsCommand;
|
||||||
_revokeAccessTokensCommand = revokeAccessTokensCommand;
|
_revokeAccessTokensCommand = revokeAccessTokensCommand;
|
||||||
_createAccessTokenCommand = createAccessTokenCommand;
|
_createAccessTokenCommand = createAccessTokenCommand;
|
||||||
}
|
}
|
||||||
@ -67,6 +70,41 @@ public class ServiceAccountsController : Controller
|
|||||||
return new ListResponseModel<ServiceAccountResponseModel>(responses);
|
return new ListResponseModel<ServiceAccountResponseModel>(responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<ServiceAccountResponseModel> GetByServiceAccountIdAsync(
|
||||||
|
[FromRoute] Guid id)
|
||||||
|
{
|
||||||
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(id);
|
||||||
|
|
||||||
|
if (serviceAccount == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_currentContext.AccessSecretsManager(serviceAccount.OrganizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var orgAdmin = await _currentContext.OrganizationAdmin(serviceAccount.OrganizationId);
|
||||||
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
|
||||||
|
var hasAccess = accessClient switch
|
||||||
|
{
|
||||||
|
AccessClientType.NoAccessCheck => true,
|
||||||
|
AccessClientType.User => await _serviceAccountRepository.UserHasWriteAccessToServiceAccount(id, userId),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!hasAccess)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ServiceAccountResponseModel(serviceAccount);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("/organizations/{organizationId}/service-accounts")]
|
[HttpPost("/organizations/{organizationId}/service-accounts")]
|
||||||
public async Task<ServiceAccountResponseModel> CreateAsync([FromRoute] Guid organizationId,
|
public async Task<ServiceAccountResponseModel> CreateAsync([FromRoute] Guid organizationId,
|
||||||
[FromBody] ServiceAccountCreateRequestModel createRequest)
|
[FromBody] ServiceAccountCreateRequestModel createRequest)
|
||||||
@ -90,6 +128,16 @@ public class ServiceAccountsController : Controller
|
|||||||
return new ServiceAccountResponseModel(result);
|
return new ServiceAccountResponseModel(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("delete")]
|
||||||
|
public async Task<ListResponseModel<BulkDeleteResponseModel>> BulkDeleteAsync([FromBody] List<Guid> ids)
|
||||||
|
{
|
||||||
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
|
||||||
|
var results = await _deleteServiceAccountsCommand.DeleteServiceAccounts(ids, userId);
|
||||||
|
var responses = results.Select(r => new BulkDeleteResponseModel(r.Item1.Id, r.Item2));
|
||||||
|
return new ListResponseModel<BulkDeleteResponseModel>(responses);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("{id}/access-tokens")]
|
[HttpGet("{id}/access-tokens")]
|
||||||
public async Task<ListResponseModel<AccessTokenResponseModel>> GetAccessTokens([FromRoute] Guid id)
|
public async Task<ListResponseModel<AccessTokenResponseModel>> GetAccessTokens([FromRoute] Guid id)
|
||||||
{
|
{
|
||||||
|
@ -37,6 +37,7 @@ public class UserProjectAccessPolicyResponseModel : BaseAccessPolicyResponseMode
|
|||||||
OrganizationUserId = accessPolicy.OrganizationUserId;
|
OrganizationUserId = accessPolicy.OrganizationUserId;
|
||||||
GrantedProjectId = accessPolicy.GrantedProjectId;
|
GrantedProjectId = accessPolicy.GrantedProjectId;
|
||||||
OrganizationUserName = GetUserDisplayName(accessPolicy.User);
|
OrganizationUserName = GetUserDisplayName(accessPolicy.User);
|
||||||
|
UserId = accessPolicy.User?.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserProjectAccessPolicyResponseModel() : base(new UserProjectAccessPolicy(), _objectName)
|
public UserProjectAccessPolicyResponseModel() : base(new UserProjectAccessPolicy(), _objectName)
|
||||||
@ -45,6 +46,7 @@ public class UserProjectAccessPolicyResponseModel : BaseAccessPolicyResponseMode
|
|||||||
|
|
||||||
public Guid? OrganizationUserId { get; set; }
|
public Guid? OrganizationUserId { get; set; }
|
||||||
public string? OrganizationUserName { get; set; }
|
public string? OrganizationUserName { get; set; }
|
||||||
|
public Guid? UserId { get; set; }
|
||||||
public Guid? GrantedProjectId { get; set; }
|
public Guid? GrantedProjectId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +60,7 @@ public class UserServiceAccountAccessPolicyResponseModel : BaseAccessPolicyRespo
|
|||||||
OrganizationUserId = accessPolicy.OrganizationUserId;
|
OrganizationUserId = accessPolicy.OrganizationUserId;
|
||||||
GrantedServiceAccountId = accessPolicy.GrantedServiceAccountId;
|
GrantedServiceAccountId = accessPolicy.GrantedServiceAccountId;
|
||||||
OrganizationUserName = GetUserDisplayName(accessPolicy.User);
|
OrganizationUserName = GetUserDisplayName(accessPolicy.User);
|
||||||
|
UserId = accessPolicy.User?.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserServiceAccountAccessPolicyResponseModel() : base(new UserServiceAccountAccessPolicy(), _objectName)
|
public UserServiceAccountAccessPolicyResponseModel() : base(new UserServiceAccountAccessPolicy(), _objectName)
|
||||||
@ -66,6 +69,7 @@ public class UserServiceAccountAccessPolicyResponseModel : BaseAccessPolicyRespo
|
|||||||
|
|
||||||
public Guid? OrganizationUserId { get; set; }
|
public Guid? OrganizationUserId { get; set; }
|
||||||
public string? OrganizationUserName { get; set; }
|
public string? OrganizationUserName { get; set; }
|
||||||
|
public Guid? UserId { get; set; }
|
||||||
public Guid? GrantedServiceAccountId { get; set; }
|
public Guid? GrantedServiceAccountId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,6 +83,7 @@ public class GroupProjectAccessPolicyResponseModel : BaseAccessPolicyResponseMod
|
|||||||
GroupId = accessPolicy.GroupId;
|
GroupId = accessPolicy.GroupId;
|
||||||
GrantedProjectId = accessPolicy.GrantedProjectId;
|
GrantedProjectId = accessPolicy.GrantedProjectId;
|
||||||
GroupName = accessPolicy.Group?.Name;
|
GroupName = accessPolicy.Group?.Name;
|
||||||
|
CurrentUserInGroup = accessPolicy.CurrentUserInGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupProjectAccessPolicyResponseModel() : base(new GroupProjectAccessPolicy(), _objectName)
|
public GroupProjectAccessPolicyResponseModel() : base(new GroupProjectAccessPolicy(), _objectName)
|
||||||
@ -87,6 +92,7 @@ public class GroupProjectAccessPolicyResponseModel : BaseAccessPolicyResponseMod
|
|||||||
|
|
||||||
public Guid? GroupId { get; set; }
|
public Guid? GroupId { get; set; }
|
||||||
public string? GroupName { get; set; }
|
public string? GroupName { get; set; }
|
||||||
|
public bool? CurrentUserInGroup { get; set; }
|
||||||
public Guid? GrantedProjectId { get; set; }
|
public Guid? GrantedProjectId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +106,7 @@ public class GroupServiceAccountAccessPolicyResponseModel : BaseAccessPolicyResp
|
|||||||
GroupId = accessPolicy.GroupId;
|
GroupId = accessPolicy.GroupId;
|
||||||
GroupName = accessPolicy.Group?.Name;
|
GroupName = accessPolicy.Group?.Name;
|
||||||
GrantedServiceAccountId = accessPolicy.GrantedServiceAccountId;
|
GrantedServiceAccountId = accessPolicy.GrantedServiceAccountId;
|
||||||
|
CurrentUserInGroup = accessPolicy.CurrentUserInGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupServiceAccountAccessPolicyResponseModel() : base(new GroupServiceAccountAccessPolicy(), _objectName)
|
public GroupServiceAccountAccessPolicyResponseModel() : base(new GroupServiceAccountAccessPolicy(), _objectName)
|
||||||
@ -109,6 +116,7 @@ public class GroupServiceAccountAccessPolicyResponseModel : BaseAccessPolicyResp
|
|||||||
public Guid? GroupId { get; set; }
|
public Guid? GroupId { get; set; }
|
||||||
public string? GroupName { get; set; }
|
public string? GroupName { get; set; }
|
||||||
public Guid? GrantedServiceAccountId { get; set; }
|
public Guid? GrantedServiceAccountId { get; set; }
|
||||||
|
public bool? CurrentUserInGroup { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ServiceAccountProjectAccessPolicyResponseModel : BaseAccessPolicyResponseModel
|
public class ServiceAccountProjectAccessPolicyResponseModel : BaseAccessPolicyResponseModel
|
||||||
|
@ -35,7 +35,9 @@ mkdir -p /etc/bitwarden/logs
|
|||||||
mkdir -p /etc/bitwarden/ca-certificates
|
mkdir -p /etc/bitwarden/ca-certificates
|
||||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||||
|
|
||||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||||
|
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
||||||
&& update-ca-certificates
|
&& update-ca-certificates
|
||||||
|
fi
|
||||||
|
|
||||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/Api.dll
|
exec gosu $USERNAME:$GROUPNAME dotnet /app/Api.dll
|
||||||
|
@ -147,7 +147,7 @@ public class FreshdeskController : Controller
|
|||||||
{
|
{
|
||||||
var freshdeskAuthkey = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{_billingSettings.FreshdeskApiKey}:X"));
|
var freshdeskAuthkey = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{_billingSettings.FreshdeskApiKey}:X"));
|
||||||
var httpClient = _httpClientFactory.CreateClient("FreshdeskApi");
|
var httpClient = _httpClientFactory.CreateClient("FreshdeskApi");
|
||||||
request.Headers.Add("Authorization", freshdeskAuthkey);
|
request.Headers.Add("Authorization", $"Basic {freshdeskAuthkey}");
|
||||||
var response = await httpClient.SendAsync(request);
|
var response = await httpClient.SendAsync(request);
|
||||||
if (response.StatusCode != System.Net.HttpStatusCode.TooManyRequests || retriedCount > 3)
|
if (response.StatusCode != System.Net.HttpStatusCode.TooManyRequests || retriedCount > 3)
|
||||||
{
|
{
|
||||||
|
@ -35,7 +35,9 @@ mkdir -p /etc/bitwarden/logs
|
|||||||
mkdir -p /etc/bitwarden/ca-certificates
|
mkdir -p /etc/bitwarden/ca-certificates
|
||||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||||
|
|
||||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||||
|
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
||||||
&& update-ca-certificates
|
&& update-ca-certificates
|
||||||
|
fi
|
||||||
|
|
||||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/Billing.dll
|
exec gosu $USERNAME:$GROUPNAME dotnet /app/Billing.dll
|
||||||
|
@ -22,3 +22,8 @@ public static class AuthenticationSchemes
|
|||||||
{
|
{
|
||||||
public const string BitwardenExternalCookieAuthenticationScheme = "bw.external";
|
public const string BitwardenExternalCookieAuthenticationScheme = "bw.external";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class FeatureFlagKeys
|
||||||
|
{
|
||||||
|
public const string SecretsManager = "secrets-manager";
|
||||||
|
}
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
|
||||||
|
namespace Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
|
||||||
|
|
||||||
|
public interface IDeleteServiceAccountsCommand
|
||||||
|
{
|
||||||
|
Task<List<Tuple<ServiceAccount, string>>> DeleteServiceAccounts(List<Guid> ids, Guid userId);
|
||||||
|
}
|
||||||
|
|
@ -41,6 +41,7 @@ public class GroupProjectAccessPolicy : BaseAccessPolicy
|
|||||||
{
|
{
|
||||||
public Guid? GroupId { get; set; }
|
public Guid? GroupId { get; set; }
|
||||||
public Group? Group { get; set; }
|
public Group? Group { get; set; }
|
||||||
|
public bool? CurrentUserInGroup { get; set; }
|
||||||
public Guid? GrantedProjectId { get; set; }
|
public Guid? GrantedProjectId { get; set; }
|
||||||
public Project? GrantedProject { get; set; }
|
public Project? GrantedProject { get; set; }
|
||||||
}
|
}
|
||||||
@ -49,6 +50,7 @@ public class GroupServiceAccountAccessPolicy : BaseAccessPolicy
|
|||||||
{
|
{
|
||||||
public Guid? GroupId { get; set; }
|
public Guid? GroupId { get; set; }
|
||||||
public Group? Group { get; set; }
|
public Group? Group { get; set; }
|
||||||
|
public bool? CurrentUserInGroup { get; set; }
|
||||||
public Guid? GrantedServiceAccountId { get; set; }
|
public Guid? GrantedServiceAccountId { get; set; }
|
||||||
public ServiceAccount? GrantedServiceAccount { get; set; }
|
public ServiceAccount? GrantedServiceAccount { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ public interface IAccessPolicyRepository
|
|||||||
Task<List<BaseAccessPolicy>> CreateManyAsync(List<BaseAccessPolicy> baseAccessPolicies);
|
Task<List<BaseAccessPolicy>> CreateManyAsync(List<BaseAccessPolicy> baseAccessPolicies);
|
||||||
Task<bool> AccessPolicyExists(BaseAccessPolicy baseAccessPolicy);
|
Task<bool> AccessPolicyExists(BaseAccessPolicy baseAccessPolicy);
|
||||||
Task<BaseAccessPolicy?> GetByIdAsync(Guid id);
|
Task<BaseAccessPolicy?> GetByIdAsync(Guid id);
|
||||||
Task<IEnumerable<BaseAccessPolicy>> GetManyByGrantedProjectIdAsync(Guid id);
|
Task<IEnumerable<BaseAccessPolicy>> GetManyByGrantedProjectIdAsync(Guid id, Guid userId);
|
||||||
Task<IEnumerable<BaseAccessPolicy>> GetManyByGrantedServiceAccountIdAsync(Guid id);
|
Task<IEnumerable<BaseAccessPolicy>> GetManyByGrantedServiceAccountIdAsync(Guid id, Guid userId);
|
||||||
Task<IEnumerable<BaseAccessPolicy>> GetManyByServiceAccountIdAsync(Guid id, Guid userId,
|
Task<IEnumerable<BaseAccessPolicy>> GetManyByServiceAccountIdAsync(Guid id, Guid userId,
|
||||||
AccessClientType accessType);
|
AccessClientType accessType);
|
||||||
Task ReplaceAsync(BaseAccessPolicy baseAccessPolicy);
|
Task ReplaceAsync(BaseAccessPolicy baseAccessPolicy);
|
||||||
|
@ -7,8 +7,10 @@ public interface IServiceAccountRepository
|
|||||||
{
|
{
|
||||||
Task<IEnumerable<ServiceAccount>> GetManyByOrganizationIdAsync(Guid organizationId, Guid userId, AccessClientType accessType);
|
Task<IEnumerable<ServiceAccount>> GetManyByOrganizationIdAsync(Guid organizationId, Guid userId, AccessClientType accessType);
|
||||||
Task<ServiceAccount> GetByIdAsync(Guid id);
|
Task<ServiceAccount> GetByIdAsync(Guid id);
|
||||||
|
Task<IEnumerable<ServiceAccount>> GetManyByIds(IEnumerable<Guid> ids);
|
||||||
Task<ServiceAccount> CreateAsync(ServiceAccount serviceAccount);
|
Task<ServiceAccount> CreateAsync(ServiceAccount serviceAccount);
|
||||||
Task ReplaceAsync(ServiceAccount serviceAccount);
|
Task ReplaceAsync(ServiceAccount serviceAccount);
|
||||||
|
Task DeleteManyByIdAsync(IEnumerable<Guid> ids);
|
||||||
Task<bool> UserHasReadAccessToServiceAccount(Guid id, Guid userId);
|
Task<bool> UserHasReadAccessToServiceAccount(Guid id, Guid userId);
|
||||||
Task<bool> UserHasWriteAccessToServiceAccount(Guid id, Guid userId);
|
Task<bool> UserHasWriteAccessToServiceAccount(Guid id, Guid userId);
|
||||||
Task<IEnumerable<ServiceAccount>> GetManyByOrganizationIdWriteAccessAsync(Guid organizationId, Guid userId, AccessClientType accessType);
|
Task<IEnumerable<ServiceAccount>> GetManyByOrganizationIdWriteAccessAsync(Guid organizationId, Guid userId, AccessClientType accessType);
|
||||||
|
@ -1,6 +1,39 @@
|
|||||||
namespace Bit.Core.Services;
|
using Bit.Core.Context;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services;
|
||||||
|
|
||||||
public interface IFeatureService
|
public interface IFeatureService
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether online access to feature status is available.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if the service is online, otherwise false.</returns>
|
||||||
bool IsOnline();
|
bool IsOnline();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether a given feature is enabled.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key of the feature to check.</param>
|
||||||
|
/// <param name="currentContext">A context providing information that can be used to evaluate whether a feature should be on or off.</param>
|
||||||
|
/// <param name="defaultValue">The default value for the feature.</param>
|
||||||
|
/// <returns>True if the feature is enabled, otherwise false.</returns>
|
||||||
|
bool IsEnabled(string key, ICurrentContext currentContext, bool defaultValue = false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the integer variation of a feature.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key of the feature to check.</param>
|
||||||
|
/// <param name="currentContext">A context providing information that can be used to evaluate the feature value.</param>
|
||||||
|
/// <param name="defaultValue">The default value for the feature.</param>
|
||||||
|
/// <returns>The feature variation value.</returns>
|
||||||
|
int GetIntVariation(string key, ICurrentContext currentContext, int defaultValue = 0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the string variation of a feature.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key of the feature to check.</param>
|
||||||
|
/// <param name="currentContext">A context providing information that can be used to evaluate the feature value.</param>
|
||||||
|
/// <param name="defaultValue">The default value for the feature.</param>
|
||||||
|
/// <returns>The feature variation value.</returns>
|
||||||
|
string GetStringVariation(string key, ICurrentContext currentContext, string defaultValue = null);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.Settings;
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Settings;
|
||||||
using LaunchDarkly.Sdk.Server;
|
using LaunchDarkly.Sdk.Server;
|
||||||
using LaunchDarkly.Sdk.Server.Integrations;
|
using LaunchDarkly.Sdk.Server.Integrations;
|
||||||
|
|
||||||
@ -47,8 +48,44 @@ public class LaunchDarklyFeatureService : IFeatureService, IDisposable
|
|||||||
return _client.Initialized && !_client.IsOffline();
|
return _client.Initialized && !_client.IsOffline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsEnabled(string key, ICurrentContext currentContext, bool defaultValue = false)
|
||||||
|
{
|
||||||
|
return _client.BoolVariation(key, BuildContext(currentContext), defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetIntVariation(string key, ICurrentContext currentContext, int defaultValue = 0)
|
||||||
|
{
|
||||||
|
return _client.IntVariation(key, BuildContext(currentContext), defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetStringVariation(string key, ICurrentContext currentContext, string defaultValue = null)
|
||||||
|
{
|
||||||
|
return _client.StringVariation(key, BuildContext(currentContext), defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_client?.Dispose();
|
_client?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LaunchDarkly.Sdk.Context BuildContext(ICurrentContext currentContext)
|
||||||
|
{
|
||||||
|
var builder = LaunchDarkly.Sdk.Context.MultiBuilder();
|
||||||
|
|
||||||
|
if (currentContext.UserId.HasValue)
|
||||||
|
{
|
||||||
|
var user = LaunchDarkly.Sdk.Context.Builder(currentContext.UserId.Value.ToString());
|
||||||
|
user.Kind(LaunchDarkly.Sdk.ContextKind.Default);
|
||||||
|
builder.Add(user.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentContext.OrganizationId.HasValue)
|
||||||
|
{
|
||||||
|
var org = LaunchDarkly.Sdk.Context.Builder(currentContext.OrganizationId.Value.ToString());
|
||||||
|
org.Kind("org");
|
||||||
|
builder.Add(org.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,9 @@ mkdir -p /etc/bitwarden/logs
|
|||||||
mkdir -p /etc/bitwarden/ca-certificates
|
mkdir -p /etc/bitwarden/ca-certificates
|
||||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||||
|
|
||||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||||
|
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
||||||
&& update-ca-certificates
|
&& update-ca-certificates
|
||||||
|
fi
|
||||||
|
|
||||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/Events.dll
|
exec gosu $USERNAME:$GROUPNAME dotnet /app/Events.dll
|
||||||
|
@ -34,7 +34,9 @@ mkdir -p /etc/bitwarden/logs
|
|||||||
#mkdir -p /etc/bitwarden/ca-certificates
|
#mkdir -p /etc/bitwarden/ca-certificates
|
||||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||||
|
|
||||||
#cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||||
# && update-ca-certificates
|
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
||||||
|
&& update-ca-certificates
|
||||||
|
fi
|
||||||
|
|
||||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/EventsProcessor.dll
|
exec gosu $USERNAME:$GROUPNAME dotnet /app/EventsProcessor.dll
|
||||||
|
@ -34,7 +34,9 @@ mkdir -p /etc/bitwarden/logs
|
|||||||
mkdir -p /etc/bitwarden/ca-certificates
|
mkdir -p /etc/bitwarden/ca-certificates
|
||||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||||
|
|
||||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||||
|
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
||||||
&& update-ca-certificates
|
&& update-ca-certificates
|
||||||
|
fi
|
||||||
|
|
||||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/Icons.dll
|
exec gosu $USERNAME:$GROUPNAME dotnet /app/Icons.dll
|
||||||
|
@ -35,10 +35,15 @@ mkdir -p /etc/bitwarden/logs
|
|||||||
mkdir -p /etc/bitwarden/ca-certificates
|
mkdir -p /etc/bitwarden/ca-certificates
|
||||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||||
|
|
||||||
cp /etc/bitwarden/identity/identity.pfx /app/identity.pfx
|
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||||
|
cp /etc/bitwarden/identity/identity.pfx /app/identity.pfx
|
||||||
|
fi
|
||||||
|
|
||||||
chown -R $USERNAME:$GROUPNAME /app
|
chown -R $USERNAME:$GROUPNAME /app
|
||||||
|
|
||||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||||
|
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
||||||
&& update-ca-certificates
|
&& update-ca-certificates
|
||||||
|
fi
|
||||||
|
|
||||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/Identity.dll
|
exec gosu $USERNAME:$GROUPNAME dotnet /app/Identity.dll
|
||||||
|
@ -186,13 +186,13 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
|
|||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var groups =
|
var groups =
|
||||||
from cg in dbContext.CollectionGroups
|
from c in collections
|
||||||
where cg.Collection.OrganizationId == organizationId
|
join cg in dbContext.CollectionGroups on c.Id equals cg.CollectionId
|
||||||
group cg by cg.CollectionId into g
|
group cg by cg.CollectionId into g
|
||||||
select g;
|
select g;
|
||||||
var users =
|
var users =
|
||||||
from cu in dbContext.CollectionUsers
|
from c in collections
|
||||||
where cu.Collection.OrganizationId == organizationId
|
join cu in dbContext.CollectionUsers on c.Id equals cu.CollectionId
|
||||||
group cu by cu.CollectionId into u
|
group cu by cu.CollectionId into u
|
||||||
select u;
|
select u;
|
||||||
|
|
||||||
@ -230,19 +230,16 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
|
|||||||
{
|
{
|
||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
var groups =
|
var groups =
|
||||||
from cg in dbContext.CollectionGroups
|
from c in collections
|
||||||
where cg.Collection.OrganizationId == organizationId
|
join cg in dbContext.CollectionGroups on c.Id equals cg.CollectionId
|
||||||
&& collections.Select(c => c.Id).Contains(cg.Collection.Id)
|
|
||||||
group cg by cg.CollectionId into g
|
group cg by cg.CollectionId into g
|
||||||
select g;
|
select g;
|
||||||
var users =
|
var users =
|
||||||
from cu in dbContext.CollectionUsers
|
from c in collections
|
||||||
where cu.Collection.OrganizationId == organizationId
|
join cu in dbContext.CollectionUsers on c.Id equals cu.CollectionId
|
||||||
&& collections.Select(c => c.Id).Contains(cu.Collection.Id)
|
|
||||||
group cu by cu.CollectionId into u
|
group cu by cu.CollectionId into u
|
||||||
select u;
|
select u;
|
||||||
|
|
||||||
|
|
||||||
return collections.Select(collection =>
|
return collections.Select(collection =>
|
||||||
new Tuple<Core.Entities.Collection, CollectionAccessDetails>(
|
new Tuple<Core.Entities.Collection, CollectionAccessDetails>(
|
||||||
collection,
|
collection,
|
||||||
|
@ -34,7 +34,9 @@ mkdir -p /etc/bitwarden/logs
|
|||||||
mkdir -p /etc/bitwarden/ca-certificates
|
mkdir -p /etc/bitwarden/ca-certificates
|
||||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||||
|
|
||||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||||
|
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
||||||
&& update-ca-certificates
|
&& update-ca-certificates
|
||||||
|
fi
|
||||||
|
|
||||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/Notifications.dll
|
exec gosu $USERNAME:$GROUPNAME dotnet /app/Notifications.dll
|
||||||
|
@ -702,7 +702,6 @@ public class AccessPoliciesControllerTest : IClassFixture<ApiApplicationFactory>
|
|||||||
result!.UserAccessPolicies.First(ap => ap.OrganizationUserId == ownerOrgUserId).OrganizationUserId);
|
result!.UserAccessPolicies.First(ap => ap.OrganizationUserId == ownerOrgUserId).OrganizationUserId);
|
||||||
Assert.True(result.UserAccessPolicies.First().Read);
|
Assert.True(result.UserAccessPolicies.First().Read);
|
||||||
Assert.True(result.UserAccessPolicies.First().Write);
|
Assert.True(result.UserAccessPolicies.First().Write);
|
||||||
AssertHelper.AssertRecent(result.UserAccessPolicies.First().RevisionDate);
|
|
||||||
|
|
||||||
var createdAccessPolicy =
|
var createdAccessPolicy =
|
||||||
await _accessPolicyRepository.GetByIdAsync(result.UserAccessPolicies.First().Id);
|
await _accessPolicyRepository.GetByIdAsync(result.UserAccessPolicies.First().Id);
|
||||||
@ -710,7 +709,6 @@ public class AccessPoliciesControllerTest : IClassFixture<ApiApplicationFactory>
|
|||||||
Assert.Equal(result.UserAccessPolicies.First().Read, createdAccessPolicy!.Read);
|
Assert.Equal(result.UserAccessPolicies.First().Read, createdAccessPolicy!.Read);
|
||||||
Assert.Equal(result.UserAccessPolicies.First().Write, createdAccessPolicy.Write);
|
Assert.Equal(result.UserAccessPolicies.First().Write, createdAccessPolicy.Write);
|
||||||
Assert.Equal(result.UserAccessPolicies.First().Id, createdAccessPolicy.Id);
|
Assert.Equal(result.UserAccessPolicies.First().Id, createdAccessPolicy.Id);
|
||||||
AssertHelper.AssertRecent(createdAccessPolicy.RevisionDate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -123,12 +123,14 @@ public class ProjectsControllerTest : IClassFixture<ApiApplicationFactory>, IAsy
|
|||||||
var (org, adminOrgUser) = await _organizationHelper.Initialize(true, true);
|
var (org, adminOrgUser) = await _organizationHelper.Initialize(true, true);
|
||||||
await LoginAsync(_email);
|
await LoginAsync(_email);
|
||||||
var orgUserId = adminOrgUser.Id;
|
var orgUserId = adminOrgUser.Id;
|
||||||
|
var currentUserId = adminOrgUser.UserId!.Value;
|
||||||
|
|
||||||
if (permissionType == PermissionType.RunAsUserWithPermission)
|
if (permissionType == PermissionType.RunAsUserWithPermission)
|
||||||
{
|
{
|
||||||
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
|
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
|
||||||
await LoginAsync(email);
|
await LoginAsync(email);
|
||||||
orgUserId = orgUser.Id;
|
orgUserId = orgUser.Id;
|
||||||
|
currentUserId = orgUser.UserId!.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = new ProjectCreateRequestModel { Name = _mockEncryptedString };
|
var request = new ProjectCreateRequestModel { Name = _mockEncryptedString };
|
||||||
@ -150,7 +152,7 @@ public class ProjectsControllerTest : IClassFixture<ApiApplicationFactory>, IAsy
|
|||||||
Assert.Null(createdProject.DeletedDate);
|
Assert.Null(createdProject.DeletedDate);
|
||||||
|
|
||||||
// Check permissions have been bootstrapped.
|
// Check permissions have been bootstrapped.
|
||||||
var accessPolicies = await _accessPolicyRepository.GetManyByGrantedProjectIdAsync(createdProject.Id);
|
var accessPolicies = await _accessPolicyRepository.GetManyByGrantedProjectIdAsync(createdProject.Id, currentUserId);
|
||||||
Assert.NotNull(accessPolicies);
|
Assert.NotNull(accessPolicies);
|
||||||
var ap = (UserProjectAccessPolicy)accessPolicies.First();
|
var ap = (UserProjectAccessPolicy)accessPolicies.First();
|
||||||
Assert.Equal(createdProject.Id, ap.GrantedProjectId);
|
Assert.Equal(createdProject.Id, ap.GrantedProjectId);
|
||||||
|
@ -148,11 +148,14 @@ public class SecretsControllerTest : IClassFixture<ApiApplicationFactory>, IAsyn
|
|||||||
var (org, _) = await _organizationHelper.Initialize(true, true);
|
var (org, _) = await _organizationHelper.Initialize(true, true);
|
||||||
await LoginAsync(_email);
|
await LoginAsync(_email);
|
||||||
|
|
||||||
|
var project = await _projectRepository.CreateAsync(new Project { Name = "123" });
|
||||||
|
|
||||||
var request = new SecretCreateRequestModel
|
var request = new SecretCreateRequestModel
|
||||||
{
|
{
|
||||||
|
ProjectIds = new Guid[] { project.Id },
|
||||||
Key = _mockEncryptedString,
|
Key = _mockEncryptedString,
|
||||||
Value = _mockEncryptedString,
|
Value = _mockEncryptedString,
|
||||||
Note = _mockEncryptedString
|
Note = _mockEncryptedString,
|
||||||
};
|
};
|
||||||
|
|
||||||
var response = await _client.PostAsJsonAsync($"/organizations/{org.Id}/secrets", request);
|
var response = await _client.PostAsJsonAsync($"/organizations/{org.Id}/secrets", request);
|
||||||
|
@ -138,7 +138,7 @@ public class ServiceAccountsControllerTest : IClassFixture<ApiApplicationFactory
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task Create_Admin_Success()
|
public async Task Create_Admin_Success()
|
||||||
{
|
{
|
||||||
var (org, _) = await _organizationHelper.Initialize(true, true);
|
var (org, orgUser) = await _organizationHelper.Initialize(true, true);
|
||||||
await LoginAsync(_email);
|
await LoginAsync(_email);
|
||||||
|
|
||||||
var request = new ServiceAccountCreateRequestModel { Name = _mockEncryptedString };
|
var request = new ServiceAccountCreateRequestModel { Name = _mockEncryptedString };
|
||||||
@ -159,7 +159,7 @@ public class ServiceAccountsControllerTest : IClassFixture<ApiApplicationFactory
|
|||||||
AssertHelper.AssertRecent(createdServiceAccount.CreationDate);
|
AssertHelper.AssertRecent(createdServiceAccount.CreationDate);
|
||||||
|
|
||||||
// Check permissions have been bootstrapped.
|
// Check permissions have been bootstrapped.
|
||||||
var accessPolicies = await _accessPolicyRepository.GetManyByGrantedServiceAccountIdAsync(createdServiceAccount.Id);
|
var accessPolicies = await _accessPolicyRepository.GetManyByGrantedServiceAccountIdAsync(createdServiceAccount.Id, orgUser.UserId!.Value);
|
||||||
Assert.NotNull(accessPolicies);
|
Assert.NotNull(accessPolicies);
|
||||||
var ap = accessPolicies!.First();
|
var ap = accessPolicies!.First();
|
||||||
Assert.True(ap.Read);
|
Assert.True(ap.Read);
|
||||||
@ -275,6 +275,92 @@ public class ServiceAccountsControllerTest : IClassFixture<ApiApplicationFactory
|
|||||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(false, false)]
|
||||||
|
[InlineData(true, false)]
|
||||||
|
[InlineData(false, true)]
|
||||||
|
public async Task Delete_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets)
|
||||||
|
{
|
||||||
|
var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets);
|
||||||
|
await LoginAsync(_email);
|
||||||
|
|
||||||
|
var initialServiceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||||
|
{
|
||||||
|
OrganizationId = org.Id,
|
||||||
|
Name = _mockEncryptedString,
|
||||||
|
});
|
||||||
|
|
||||||
|
var request = new List<Guid> { initialServiceAccount.Id };
|
||||||
|
|
||||||
|
var response = await _client.PutAsJsonAsync("/service-accounts/delete", request);
|
||||||
|
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Delete_MissingAccessPolicy_AccessDenied()
|
||||||
|
{
|
||||||
|
var (org, _) = await _organizationHelper.Initialize(true, true);
|
||||||
|
var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
|
||||||
|
await LoginAsync(email);
|
||||||
|
|
||||||
|
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||||
|
{
|
||||||
|
OrganizationId = org.Id,
|
||||||
|
Name = _mockEncryptedString,
|
||||||
|
});
|
||||||
|
|
||||||
|
var ids = new List<Guid> { serviceAccount.Id };
|
||||||
|
|
||||||
|
var response = await _client.PostAsJsonAsync("/service-accounts/delete", ids);
|
||||||
|
|
||||||
|
var results = await response.Content.ReadFromJsonAsync<ListResponseModel<BulkDeleteResponseModel>>();
|
||||||
|
Assert.NotNull(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(PermissionType.RunAsAdmin)]
|
||||||
|
[InlineData(PermissionType.RunAsUserWithPermission)]
|
||||||
|
public async Task Delete_Success(PermissionType permissionType)
|
||||||
|
{
|
||||||
|
var (org, _) = await _organizationHelper.Initialize(true, true);
|
||||||
|
|
||||||
|
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||||
|
{
|
||||||
|
OrganizationId = org.Id,
|
||||||
|
Name = _mockEncryptedString,
|
||||||
|
});
|
||||||
|
|
||||||
|
await _apiKeyRepository.CreateAsync(new ApiKey { ServiceAccountId = serviceAccount.Id });
|
||||||
|
|
||||||
|
if (permissionType == PermissionType.RunAsAdmin)
|
||||||
|
{
|
||||||
|
await LoginAsync(_email);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
|
||||||
|
await LoginAsync(email);
|
||||||
|
|
||||||
|
await _accessPolicyRepository.CreateManyAsync(new List<BaseAccessPolicy> {
|
||||||
|
new UserServiceAccountAccessPolicy
|
||||||
|
{
|
||||||
|
GrantedServiceAccountId = serviceAccount.Id,
|
||||||
|
OrganizationUserId = orgUser.Id,
|
||||||
|
Write = true,
|
||||||
|
Read = true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var ids = new List<Guid> { serviceAccount.Id };
|
||||||
|
|
||||||
|
var response = await _client.PostAsJsonAsync("/service-accounts/delete", ids);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
var sa = await _serviceAccountRepository.GetManyByIds(ids);
|
||||||
|
Assert.Empty(sa);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(false, false)]
|
[InlineData(false, false)]
|
||||||
[InlineData(true, false)]
|
[InlineData(true, false)]
|
||||||
|
@ -113,7 +113,7 @@ public class AccessPoliciesControllerTests
|
|||||||
var result = await sutProvider.Sut.GetProjectAccessPoliciesAsync(id);
|
var result = await sutProvider.Sut.GetProjectAccessPoliciesAsync(id);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
||||||
.GetManyByGrantedProjectIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)));
|
.GetManyByGrantedProjectIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), Arg.Any<Guid>());
|
||||||
|
|
||||||
Assert.Empty(result.GroupAccessPolicies);
|
Assert.Empty(result.GroupAccessPolicies);
|
||||||
Assert.Empty(result.UserAccessPolicies);
|
Assert.Empty(result.UserAccessPolicies);
|
||||||
@ -135,7 +135,7 @@ public class AccessPoliciesControllerTests
|
|||||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetProjectAccessPoliciesAsync(id));
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetProjectAccessPoliciesAsync(id));
|
||||||
|
|
||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
||||||
.GetManyByGrantedProjectIdAsync(Arg.Any<Guid>());
|
.GetManyByGrantedProjectIdAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
@ -161,13 +161,13 @@ public class AccessPoliciesControllerTests
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
sutProvider.GetDependency<IAccessPolicyRepository>().GetManyByGrantedProjectIdAsync(default)
|
sutProvider.GetDependency<IAccessPolicyRepository>().GetManyByGrantedProjectIdAsync(default, default)
|
||||||
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { resultAccessPolicy });
|
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { resultAccessPolicy });
|
||||||
|
|
||||||
var result = await sutProvider.Sut.GetProjectAccessPoliciesAsync(id);
|
var result = await sutProvider.Sut.GetProjectAccessPoliciesAsync(id);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
||||||
.GetManyByGrantedProjectIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)));
|
.GetManyByGrantedProjectIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), Arg.Any<Guid>());
|
||||||
|
|
||||||
Assert.Empty(result.GroupAccessPolicies);
|
Assert.Empty(result.GroupAccessPolicies);
|
||||||
Assert.NotEmpty(result.UserAccessPolicies);
|
Assert.NotEmpty(result.UserAccessPolicies);
|
||||||
@ -187,13 +187,13 @@ public class AccessPoliciesControllerTests
|
|||||||
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(default, default)
|
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(default, default)
|
||||||
.ReturnsForAnyArgs(false);
|
.ReturnsForAnyArgs(false);
|
||||||
|
|
||||||
sutProvider.GetDependency<IAccessPolicyRepository>().GetManyByGrantedProjectIdAsync(default)
|
sutProvider.GetDependency<IAccessPolicyRepository>().GetManyByGrantedProjectIdAsync(default, default)
|
||||||
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { resultAccessPolicy });
|
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { resultAccessPolicy });
|
||||||
|
|
||||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetProjectAccessPoliciesAsync(id));
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetProjectAccessPoliciesAsync(id));
|
||||||
|
|
||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
||||||
.GetManyByGrantedProjectIdAsync(Arg.Any<Guid>());
|
.GetManyByGrantedProjectIdAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
@ -222,7 +222,7 @@ public class AccessPoliciesControllerTests
|
|||||||
var result = await sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id);
|
var result = await sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
||||||
.GetManyByGrantedServiceAccountIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)));
|
.GetManyByGrantedServiceAccountIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), Arg.Any<Guid>());
|
||||||
|
|
||||||
Assert.Empty(result.UserAccessPolicies);
|
Assert.Empty(result.UserAccessPolicies);
|
||||||
Assert.Empty(result.GroupAccessPolicies);
|
Assert.Empty(result.GroupAccessPolicies);
|
||||||
@ -243,7 +243,7 @@ public class AccessPoliciesControllerTests
|
|||||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id));
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id));
|
||||||
|
|
||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
||||||
.GetManyByGrantedServiceAccountIdAsync(Arg.Any<Guid>());
|
.GetManyByGrantedServiceAccountIdAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
@ -270,13 +270,13 @@ public class AccessPoliciesControllerTests
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
sutProvider.GetDependency<IAccessPolicyRepository>().GetManyByGrantedServiceAccountIdAsync(default)
|
sutProvider.GetDependency<IAccessPolicyRepository>().GetManyByGrantedServiceAccountIdAsync(default, default)
|
||||||
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { resultAccessPolicy });
|
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { resultAccessPolicy });
|
||||||
|
|
||||||
var result = await sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id);
|
var result = await sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
||||||
.GetManyByGrantedServiceAccountIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)));
|
.GetManyByGrantedServiceAccountIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), Arg.Any<Guid>());
|
||||||
|
|
||||||
Assert.Empty(result.GroupAccessPolicies);
|
Assert.Empty(result.GroupAccessPolicies);
|
||||||
Assert.NotEmpty(result.UserAccessPolicies);
|
Assert.NotEmpty(result.UserAccessPolicies);
|
||||||
@ -295,13 +295,13 @@ public class AccessPoliciesControllerTests
|
|||||||
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(default, default)
|
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(default, default)
|
||||||
.ReturnsForAnyArgs(false);
|
.ReturnsForAnyArgs(false);
|
||||||
|
|
||||||
sutProvider.GetDependency<IAccessPolicyRepository>().GetManyByGrantedServiceAccountIdAsync(default)
|
sutProvider.GetDependency<IAccessPolicyRepository>().GetManyByGrantedServiceAccountIdAsync(default, default)
|
||||||
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { resultAccessPolicy });
|
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { resultAccessPolicy });
|
||||||
|
|
||||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id));
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id));
|
||||||
|
|
||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
||||||
.GetManyByGrantedServiceAccountIdAsync(Arg.Any<Guid>());
|
.GetManyByGrantedServiceAccountIdAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
using AutoFixture;
|
using AutoFixture;
|
||||||
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Bit.Core.Test.Services;
|
namespace Bit.Core.Test.Services;
|
||||||
@ -25,4 +27,68 @@ public class LaunchDarklyFeatureServiceTests
|
|||||||
|
|
||||||
Assert.False(sutProvider.Sut.IsOnline());
|
Assert.False(sutProvider.Sut.IsOnline());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public void DefaultFeatureValue_WhenSelfHost(string key)
|
||||||
|
{
|
||||||
|
var sutProvider = GetSutProvider(new Core.Settings.GlobalSettings() { SelfHosted = true });
|
||||||
|
|
||||||
|
var currentContext = Substitute.For<ICurrentContext>();
|
||||||
|
currentContext.UserId.Returns(Guid.NewGuid());
|
||||||
|
|
||||||
|
Assert.False(sutProvider.Sut.IsEnabled(key, currentContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DefaultFeatureValue_NoSdkKey()
|
||||||
|
{
|
||||||
|
var sutProvider = GetSutProvider(new Core.Settings.GlobalSettings());
|
||||||
|
|
||||||
|
var currentContext = Substitute.For<ICurrentContext>();
|
||||||
|
currentContext.UserId.Returns(Guid.NewGuid());
|
||||||
|
|
||||||
|
Assert.False(sutProvider.Sut.IsEnabled(FeatureFlagKeys.SecretsManager, currentContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact(Skip = "For local development")]
|
||||||
|
public void FeatureValue_Boolean()
|
||||||
|
{
|
||||||
|
var settings = new Core.Settings.GlobalSettings();
|
||||||
|
settings.LaunchDarkly.SdkKey = "somevalue";
|
||||||
|
|
||||||
|
var sutProvider = GetSutProvider(settings);
|
||||||
|
|
||||||
|
var currentContext = Substitute.For<ICurrentContext>();
|
||||||
|
currentContext.UserId.Returns(Guid.NewGuid());
|
||||||
|
|
||||||
|
Assert.False(sutProvider.Sut.IsEnabled(FeatureFlagKeys.SecretsManager, currentContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact(Skip = "For local development")]
|
||||||
|
public void FeatureValue_Int()
|
||||||
|
{
|
||||||
|
var settings = new Core.Settings.GlobalSettings();
|
||||||
|
settings.LaunchDarkly.SdkKey = "somevalue";
|
||||||
|
|
||||||
|
var sutProvider = GetSutProvider(settings);
|
||||||
|
|
||||||
|
var currentContext = Substitute.For<ICurrentContext>();
|
||||||
|
currentContext.UserId.Returns(Guid.NewGuid());
|
||||||
|
|
||||||
|
Assert.Equal(0, sutProvider.Sut.GetIntVariation(FeatureFlagKeys.SecretsManager, currentContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact(Skip = "For local development")]
|
||||||
|
public void FeatureValue_String()
|
||||||
|
{
|
||||||
|
var settings = new Core.Settings.GlobalSettings();
|
||||||
|
settings.LaunchDarkly.SdkKey = "somevalue";
|
||||||
|
|
||||||
|
var sutProvider = GetSutProvider(settings);
|
||||||
|
|
||||||
|
var currentContext = Substitute.For<ICurrentContext>();
|
||||||
|
currentContext.UserId.Returns(Guid.NewGuid());
|
||||||
|
|
||||||
|
Assert.Null(sutProvider.Sut.GetStringVariation(FeatureFlagKeys.SecretsManager, currentContext));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -260,6 +260,58 @@
|
|||||||
"IdentityModel": "4.4.0"
|
"IdentityModel": "4.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"LaunchDarkly.Cache": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.0.2",
|
||||||
|
"contentHash": "0bEnUVFVeW1TTDXb/bW6kS3FLQTLeGtw7Xh8yt6WNO56utVmtgcrMLvcnF6yeTn+N4FXrKfW09KkLNmK8YYQvw=="
|
||||||
|
},
|
||||||
|
"LaunchDarkly.CommonSdk": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.0",
|
||||||
|
"contentHash": "YYYq+41gZRMQ8dIoMC6HOq/dI+4RY3HsexLLAaE9T1+1tVMeQkbCqak7sVeKX4QcE7xlXx23lWgipYUkRoRUyw==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections.Immutable": "1.7.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"LaunchDarkly.EventSource": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "5.0.1",
|
||||||
|
"contentHash": "DN44Ry5M4lyrjiF7LEu0Ijco7Wm8R7mJopN+giYsYjkQlszsXdFvm3POoehIDAOtL1HHl5bZvF9k9xK034u3IA==",
|
||||||
|
"dependencies": {
|
||||||
|
"LaunchDarkly.Logging": "[1.0.1, 3.0.0)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"LaunchDarkly.InternalSdk": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "3.1.0",
|
||||||
|
"contentHash": "jW8VEfFciuCcJUEuvSzmrbMVYYXwGL/ZWHUZLiA4aDOQ1LcEXp32uK405NQW/izEypUfWB+9TaSjPpFIC+5Wzw==",
|
||||||
|
"dependencies": {
|
||||||
|
"LaunchDarkly.CommonSdk": "6.0.0",
|
||||||
|
"LaunchDarkly.Logging": "[2.0.0, 3.0.0)",
|
||||||
|
"System.Collections.Immutable": "1.7.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"LaunchDarkly.Logging": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "2.0.0",
|
||||||
|
"contentHash": "lsLKNqAZ7HIlkdTIrf4FetfRA1SUDE3WlaZQn79aSVkLjYWEhUhkDDK7hORGh4JoA3V2gXN+cIvJQax2uR/ijA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Logging.Abstractions": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"LaunchDarkly.ServerSdk": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "7.0.0",
|
||||||
|
"contentHash": "gkTWb+f5QlsXIqFAciBef3qKZU2y0Hy3Fpt4pvZoxNcnBKg2PNTDSnbpbYEKPeQ1yk1avNaI/tKprnahfrmJFg==",
|
||||||
|
"dependencies": {
|
||||||
|
"LaunchDarkly.Cache": "1.0.2",
|
||||||
|
"LaunchDarkly.CommonSdk": "6.0.0",
|
||||||
|
"LaunchDarkly.EventSource": "5.0.1",
|
||||||
|
"LaunchDarkly.InternalSdk": "3.1.0",
|
||||||
|
"LaunchDarkly.Logging": "2.0.0",
|
||||||
|
"System.Collections.Immutable": "1.7.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"libsodium": {
|
"libsodium": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.0.18.2",
|
"resolved": "1.0.18.2",
|
||||||
@ -1325,8 +1377,8 @@
|
|||||||
},
|
},
|
||||||
"System.Collections.Immutable": {
|
"System.Collections.Immutable": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.7.0",
|
"resolved": "1.7.1",
|
||||||
"contentHash": "RVSM6wZUo6L2y6P3vN6gjUtyJ2IF2RVtrepF3J7nrDKfFQd5u/SnSUFclchYQis8/k5scHy9E+fVeKVQLnnkzw=="
|
"contentHash": "B43Zsz5EfMwyEbnObwRxW5u85fzJma3lrDeGcSAV1qkhSRTNY5uXAByTn9h9ddNdhM+4/YoLc/CI43umjwIl9Q=="
|
||||||
},
|
},
|
||||||
"System.Collections.NonGeneric": {
|
"System.Collections.NonGeneric": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
@ -2686,6 +2738,7 @@
|
|||||||
"Handlebars.Net": "[2.1.2, )",
|
"Handlebars.Net": "[2.1.2, )",
|
||||||
"IdentityServer4": "[4.1.2, )",
|
"IdentityServer4": "[4.1.2, )",
|
||||||
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
|
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
|
||||||
|
"LaunchDarkly.ServerSdk": "[7.0.0, )",
|
||||||
"MailKit": "[3.2.0, )",
|
"MailKit": "[3.2.0, )",
|
||||||
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",
|
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",
|
||||||
"Microsoft.Azure.Cosmos.Table": "[1.0.8, )",
|
"Microsoft.Azure.Cosmos.Table": "[1.0.8, )",
|
||||||
|
Reference in New Issue
Block a user