mirror of
https://github.com/bitwarden/server.git
synced 2025-07-02 16:42:50 -05:00
Merge branch 'master' into feature/billing-obfuscation
This commit is contained in:
69
.github/workflows/build.yml
vendored
69
.github/workflows/build.yml
vendored
@ -38,7 +38,7 @@ jobs:
|
|||||||
|
|
||||||
testing:
|
testing:
|
||||||
name: Testing
|
name: Testing
|
||||||
runs-on: windows-2022
|
runs-on: ubuntu-22.04
|
||||||
env:
|
env:
|
||||||
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
|
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
|
||||||
steps:
|
steps:
|
||||||
@ -46,13 +46,10 @@ jobs:
|
|||||||
uses: actions/setup-dotnet@9211491ffb35dd6a6657ca4f45d43dfe6e97c829
|
uses: actions/setup-dotnet@9211491ffb35dd6a6657ca4f45d43dfe6e97c829
|
||||||
with:
|
with:
|
||||||
dotnet-version: "6.0.x"
|
dotnet-version: "6.0.x"
|
||||||
- name: Set up MSBuild
|
|
||||||
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
|
|
||||||
|
|
||||||
- name: Print environment
|
- name: Print environment
|
||||||
run: |
|
run: |
|
||||||
dotnet --info
|
dotnet --info
|
||||||
msbuild -version
|
|
||||||
nuget help | grep Version
|
nuget help | grep Version
|
||||||
echo "GitHub ref: $GITHUB_REF"
|
echo "GitHub ref: $GITHUB_REF"
|
||||||
echo "GitHub event: $GITHUB_EVENT"
|
echo "GitHub event: $GITHUB_EVENT"
|
||||||
@ -64,20 +61,23 @@ jobs:
|
|||||||
run: dotnet restore --locked-mode
|
run: dotnet restore --locked-mode
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Remove SQL proj
|
||||||
|
run: dotnet sln bitwarden-server.sln remove src/Sql/Sql.sqlproj
|
||||||
|
|
||||||
- name: Build OSS solution
|
- name: Build OSS solution
|
||||||
run: msbuild bitwarden-server.sln /p:Configuration=Debug /p:DefineConstants="OSS" /verbosity:minimal
|
run: dotnet build bitwarden-server.sln -p:Configuration=Debug -p:DefineConstants="OSS" --verbosity minimal
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Build solution
|
- name: Build solution
|
||||||
run: msbuild bitwarden-server.sln /p:Configuration=Debug /verbosity:minimal
|
run: dotnet build bitwarden-server.sln -p:Configuration=Debug --verbosity minimal
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Test OSS solution
|
- name: Test OSS solution
|
||||||
run: dotnet test ./test --configuration Debug --no-build --logger "trx;LogFileName=oss-test-results.trx" || true
|
run: dotnet test ./test --configuration Debug --no-build --logger "trx;LogFileName=oss-test-results.trx"
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Test Bitwarden solution
|
- name: Test Bitwarden solution
|
||||||
run: dotnet test ./bitwarden_license/test --configuration Debug --no-build --logger "trx;LogFileName=bw-test-results.trx" || true
|
run: dotnet test ./bitwarden_license/test --configuration Debug --no-build --logger "trx;LogFileName=bw-test-results.trx"
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Report test results
|
- name: Report test results
|
||||||
@ -250,6 +250,14 @@ jobs:
|
|||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
||||||
|
|
||||||
|
- name: Set up image tag
|
||||||
|
run: |
|
||||||
|
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
|
||||||
|
if [[ "$IMAGE_TAG" == "master" ]]; then
|
||||||
|
IMAGE_TAG=dev
|
||||||
|
fi
|
||||||
|
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
|
||||||
|
|
||||||
########## Build Docker Image ##########
|
########## Build Docker Image ##########
|
||||||
- name: Setup project name
|
- name: Setup project name
|
||||||
id: setup
|
id: setup
|
||||||
@ -277,28 +285,44 @@ jobs:
|
|||||||
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
||||||
run: docker build -t $PROJECT_NAME ${{ matrix.base_path }}/${{ matrix.project_name }}
|
run: docker build -t $PROJECT_NAME ${{ matrix.base_path }}/${{ matrix.project_name }}
|
||||||
|
|
||||||
########## ACR ##########
|
########## QA ACR ##########
|
||||||
- name: Login to Azure - QA Subscription
|
- name: Login to Azure - QA Subscription
|
||||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
||||||
with:
|
with:
|
||||||
creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
|
creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
|
||||||
|
|
||||||
- name: Login to Azure ACR
|
- name: Login to QA ACR
|
||||||
run: az acr login -n bitwardenqa
|
run: az acr login -n bitwardenqa
|
||||||
|
|
||||||
- name: Tag and Push image to Azure ACR QA registry
|
- name: Tag and push image to QA ACR
|
||||||
env:
|
env:
|
||||||
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
||||||
REGISTRY: bitwardenqa.azurecr.io
|
REGISTRY: bitwardenqa.azurecr.io
|
||||||
run: |
|
run: |
|
||||||
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
|
|
||||||
if [[ "$IMAGE_TAG" == "master" ]]; then
|
|
||||||
IMAGE_TAG=dev
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker tag $PROJECT_NAME \
|
docker tag $PROJECT_NAME \
|
||||||
$REGISTRY/$PROJECT_NAME:$IMAGE_TAG
|
$REGISTRY/$PROJECT_NAME:${{ env.IMAGE_TAG }}
|
||||||
docker push $REGISTRY/$PROJECT_NAME:$IMAGE_TAG
|
docker push $REGISTRY/$PROJECT_NAME:${{ env.IMAGE_TAG }}
|
||||||
|
|
||||||
|
- name: Log out of Docker
|
||||||
|
run: docker logout
|
||||||
|
|
||||||
|
########## PROD ACR ##########
|
||||||
|
- name: Login to Azure - PROD Subscription
|
||||||
|
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
||||||
|
with:
|
||||||
|
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||||
|
|
||||||
|
- name: Login to PROD ACR
|
||||||
|
run: az acr login -n bitwardenprod
|
||||||
|
|
||||||
|
- name: Tag and push image to PROD ACR
|
||||||
|
env:
|
||||||
|
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
||||||
|
REGISTRY: bitwardenprod.azurecr.io
|
||||||
|
run: |
|
||||||
|
docker tag $PROJECT_NAME \
|
||||||
|
$REGISTRY/$PROJECT_NAME:${{ env.IMAGE_TAG }}
|
||||||
|
docker push $REGISTRY/$PROJECT_NAME:${{ env.IMAGE_TAG }}
|
||||||
|
|
||||||
- name: Log out of Docker
|
- name: Log out of Docker
|
||||||
run: docker logout
|
run: docker logout
|
||||||
@ -366,14 +390,9 @@ jobs:
|
|||||||
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
PROJECT_NAME: ${{ steps.setup.outputs.project_name }}
|
||||||
REGISTRY: bitwarden
|
REGISTRY: bitwarden
|
||||||
run: |
|
run: |
|
||||||
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
|
|
||||||
if [[ "$IMAGE_TAG" == "master" ]]; then
|
|
||||||
IMAGE_TAG=dev
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker tag $PROJECT_NAME \
|
docker tag $PROJECT_NAME \
|
||||||
$REGISTRY/$PROJECT_NAME:$IMAGE_TAG
|
$REGISTRY/$PROJECT_NAME:${{ env.IMAGE_TAG }}
|
||||||
docker push $REGISTRY/$PROJECT_NAME:$IMAGE_TAG
|
docker push $REGISTRY/$PROJECT_NAME:${{ env.IMAGE_TAG }}
|
||||||
|
|
||||||
- name: Log out of Docker and disable Docker Notary
|
- name: Log out of Docker and disable Docker Notary
|
||||||
if: |
|
if: |
|
||||||
|
11
.run/Full Server - Self-hosted.run.xml
Normal file
11
.run/Full Server - Self-hosted.run.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Full Server - Self-hosted" type="CompoundRunConfigurationType">
|
||||||
|
<toRun name="Admin: Admin-SelfHost" type="LaunchSettings" />
|
||||||
|
<toRun name="Api: Api-SelfHost" type="LaunchSettings" />
|
||||||
|
<toRun name="Events: Events-SelfHost" type="LaunchSettings" />
|
||||||
|
<toRun name="Identity: Identity-SelfHost" type="LaunchSettings" />
|
||||||
|
<toRun name="Notifications: Notifications-SelfHost" type="LaunchSettings" />
|
||||||
|
<toRun name="Sso: Sso-SelfHost" type="LaunchSettings" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
14
.run/Full Server.run.xml
Normal file
14
.run/Full Server.run.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Full Server" type="CompoundRunConfigurationType">
|
||||||
|
<toRun name="Admin" type="LaunchSettings" />
|
||||||
|
<toRun name="Api" type="LaunchSettings" />
|
||||||
|
<toRun name="Billing" type="LaunchSettings" />
|
||||||
|
<toRun name="Events" type="LaunchSettings" />
|
||||||
|
<toRun name="EventsProcessor" type="LaunchSettings" />
|
||||||
|
<toRun name="Icons" type="LaunchSettings" />
|
||||||
|
<toRun name="Identity" type="LaunchSettings" />
|
||||||
|
<toRun name="Notifications" type="LaunchSettings" />
|
||||||
|
<toRun name="Sso" type="LaunchSettings" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
7
.run/Min Server - Self-hosted.run.xml
Normal file
7
.run/Min Server - Self-hosted.run.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Min Server - Self-hosted" type="CompoundRunConfigurationType">
|
||||||
|
<toRun name="Api: Api-SelfHost" type="LaunchSettings" />
|
||||||
|
<toRun name="Identity: Identity-SelfHost" type="LaunchSettings" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
7
.run/Min Server.run.xml
Normal file
7
.run/Min Server.run.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Min Server" type="CompoundRunConfigurationType">
|
||||||
|
<toRun name="Api" type="LaunchSettings" />
|
||||||
|
<toRun name="Identity" type="LaunchSettings" />
|
||||||
|
<method v="2" />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
@ -33,6 +33,12 @@ public class CreateAccessTokenCommand : ICreateAccessTokenCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(apiKey.ServiceAccountId.Value);
|
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(apiKey.ServiceAccountId.Value);
|
||||||
|
|
||||||
|
if (!_currentContext.AccessSecretsManager(serviceAccount.OrganizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
var orgAdmin = await _currentContext.OrganizationAdmin(serviceAccount.OrganizationId);
|
var orgAdmin = await _currentContext.OrganizationAdmin(serviceAccount.OrganizationId);
|
||||||
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
|
||||||
@ -46,7 +52,7 @@ public class CreateAccessTokenCommand : ICreateAccessTokenCommand
|
|||||||
|
|
||||||
if (!hasAccess)
|
if (!hasAccess)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
apiKey.ClientSecret = CoreHelpers.SecureRandomString(_clientSecretMaxLength);
|
apiKey.ClientSecret = CoreHelpers.SecureRandomString(_clientSecretMaxLength);
|
||||||
|
@ -36,7 +36,12 @@ public class DeleteProjectCommand : IDeleteProjectCommand
|
|||||||
var organizationId = projects.First().OrganizationId;
|
var organizationId = projects.First().OrganizationId;
|
||||||
if (projects.Any(p => p.OrganizationId != organizationId))
|
if (projects.Any(p => p.OrganizationId != organizationId))
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new BadRequestException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
||||||
|
@ -26,6 +26,11 @@ public class UpdateProjectCommand : IUpdateProjectCommand
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_currentContext.AccessSecretsManager(project.OrganizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
var orgAdmin = await _currentContext.OrganizationAdmin(project.OrganizationId);
|
var orgAdmin = await _currentContext.OrganizationAdmin(project.OrganizationId);
|
||||||
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
|
||||||
@ -38,7 +43,7 @@ public class UpdateProjectCommand : IUpdateProjectCommand
|
|||||||
|
|
||||||
if (!hasAccess)
|
if (!hasAccess)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
project.Name = updatedProject.Name;
|
project.Name = updatedProject.Name;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
@ -7,10 +8,12 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
|||||||
|
|
||||||
public class DeleteSecretCommand : IDeleteSecretCommand
|
public class DeleteSecretCommand : IDeleteSecretCommand
|
||||||
{
|
{
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly ISecretRepository _secretRepository;
|
private readonly ISecretRepository _secretRepository;
|
||||||
|
|
||||||
public DeleteSecretCommand(ISecretRepository secretRepository)
|
public DeleteSecretCommand(ICurrentContext currentContext, ISecretRepository secretRepository)
|
||||||
{
|
{
|
||||||
|
_currentContext = currentContext;
|
||||||
_secretRepository = secretRepository;
|
_secretRepository = secretRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,6 +26,18 @@ public class DeleteSecretCommand : IDeleteSecretCommand
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure all secrets belongs to the same organization
|
||||||
|
var organizationId = secrets.First().OrganizationId;
|
||||||
|
if (secrets.Any(p => p.OrganizationId != organizationId))
|
||||||
|
{
|
||||||
|
throw new BadRequestException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
var results = ids.Select(id =>
|
var results = ids.Select(id =>
|
||||||
{
|
{
|
||||||
var secret = secrets.FirstOrDefault(secret => secret.Id == id);
|
var secret = secrets.FirstOrDefault(secret => secret.Id == id);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
|
||||||
@ -6,15 +7,34 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.ServiceAccounts;
|
|||||||
|
|
||||||
public class CreateServiceAccountCommand : ICreateServiceAccountCommand
|
public class CreateServiceAccountCommand : ICreateServiceAccountCommand
|
||||||
{
|
{
|
||||||
|
private readonly IAccessPolicyRepository _accessPolicyRepository;
|
||||||
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly IServiceAccountRepository _serviceAccountRepository;
|
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||||
|
|
||||||
public CreateServiceAccountCommand(IServiceAccountRepository serviceAccountRepository)
|
public CreateServiceAccountCommand(
|
||||||
|
IAccessPolicyRepository accessPolicyRepository,
|
||||||
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
|
IServiceAccountRepository serviceAccountRepository)
|
||||||
{
|
{
|
||||||
|
_accessPolicyRepository = accessPolicyRepository;
|
||||||
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_serviceAccountRepository = serviceAccountRepository;
|
_serviceAccountRepository = serviceAccountRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ServiceAccount> CreateAsync(ServiceAccount serviceAccount)
|
public async Task<ServiceAccount> CreateAsync(ServiceAccount serviceAccount, Guid userId)
|
||||||
{
|
{
|
||||||
return await _serviceAccountRepository.CreateAsync(serviceAccount);
|
var createdServiceAccount = await _serviceAccountRepository.CreateAsync(serviceAccount);
|
||||||
|
|
||||||
|
var user = await _organizationUserRepository.GetByOrganizationAsync(createdServiceAccount.OrganizationId,
|
||||||
|
userId);
|
||||||
|
var accessPolicy = new UserServiceAccountAccessPolicy
|
||||||
|
{
|
||||||
|
OrganizationUserId = user.Id,
|
||||||
|
GrantedServiceAccountId = createdServiceAccount.Id,
|
||||||
|
Read = true,
|
||||||
|
Write = true,
|
||||||
|
};
|
||||||
|
await _accessPolicyRepository.CreateManyAsync(new List<BaseAccessPolicy> { accessPolicy });
|
||||||
|
return createdServiceAccount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,11 @@ public class UpdateServiceAccountCommand : IUpdateServiceAccountCommand
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_currentContext.AccessSecretsManager(serviceAccount.OrganizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
var orgAdmin = await _currentContext.OrganizationAdmin(serviceAccount.OrganizationId);
|
var orgAdmin = await _currentContext.OrganizationAdmin(serviceAccount.OrganizationId);
|
||||||
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
|
||||||
@ -38,7 +43,7 @@ public class UpdateServiceAccountCommand : IUpdateServiceAccountCommand
|
|||||||
|
|
||||||
if (!hasAccess)
|
if (!hasAccess)
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceAccount.Name = updatedServiceAccount.Name;
|
serviceAccount.Name = updatedServiceAccount.Name;
|
||||||
|
@ -138,7 +138,7 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>?> GetManyByProjectId(Guid id)
|
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>> GetManyByGrantedProjectIdAsync(Guid id)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
@ -153,10 +153,25 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
|
|||||||
.Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount)
|
.Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return !entities.Any() ? null : entities.Select(MapToCore);
|
return entities.Select(MapToCore);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>> GetManyByGrantedServiceAccountIdAsync(Guid id)
|
||||||
|
{
|
||||||
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
|
||||||
|
var entities = await dbContext.AccessPolicies.Where(ap =>
|
||||||
|
((UserServiceAccountAccessPolicy)ap).GrantedServiceAccountId == id ||
|
||||||
|
((GroupServiceAccountAccessPolicy)ap).GrantedServiceAccountId == id)
|
||||||
|
.Include(ap => ((UserServiceAccountAccessPolicy)ap).OrganizationUser.User)
|
||||||
|
.Include(ap => ((GroupServiceAccountAccessPolicy)ap).Group)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
return entities.Select(MapToCore);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task DeleteAsync(Guid id)
|
public async Task DeleteAsync(Guid id)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
@ -178,6 +193,8 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
|
|||||||
UserProjectAccessPolicy ap => Mapper.Map<Core.SecretsManager.Entities.UserProjectAccessPolicy>(ap),
|
UserProjectAccessPolicy ap => Mapper.Map<Core.SecretsManager.Entities.UserProjectAccessPolicy>(ap),
|
||||||
GroupProjectAccessPolicy ap => Mapper.Map<Core.SecretsManager.Entities.GroupProjectAccessPolicy>(ap),
|
GroupProjectAccessPolicy ap => Mapper.Map<Core.SecretsManager.Entities.GroupProjectAccessPolicy>(ap),
|
||||||
ServiceAccountProjectAccessPolicy ap => Mapper.Map<Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy>(ap),
|
ServiceAccountProjectAccessPolicy ap => Mapper.Map<Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy>(ap),
|
||||||
|
UserServiceAccountAccessPolicy ap => Mapper.Map<Core.SecretsManager.Entities.UserServiceAccountAccessPolicy>(ap),
|
||||||
|
GroupServiceAccountAccessPolicy ap => Mapper.Map<Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy>(ap),
|
||||||
_ => throw new ArgumentException("Unsupported access policy type")
|
_ => throw new ArgumentException("Unsupported access policy type")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ public class CreateServiceAccountCommandTests
|
|||||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(saData.Id).Returns(saData);
|
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(saData.Id).Returns(saData);
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(saData.Id, userId).Returns(false);
|
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(saData.Id, userId).Returns(false);
|
||||||
|
|
||||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => sutProvider.Sut.CreateAsync(data, userId));
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.CreateAsync(data, userId));
|
||||||
|
|
||||||
await sutProvider.GetDependency<IApiKeyRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
|
await sutProvider.GetDependency<IApiKeyRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
|
||||||
}
|
}
|
||||||
@ -49,6 +49,7 @@ public class CreateServiceAccountCommandTests
|
|||||||
data.ServiceAccountId = saData.Id;
|
data.ServiceAccountId = saData.Id;
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(saData.Id).Returns(saData);
|
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(saData.Id).Returns(saData);
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(saData.Id, userId).Returns(true);
|
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(saData.Id, userId).Returns(true);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(saData.OrganizationId).Returns(true);
|
||||||
|
|
||||||
await sutProvider.Sut.CreateAsync(data, userId);
|
await sutProvider.Sut.CreateAsync(data, userId);
|
||||||
|
|
||||||
@ -64,6 +65,7 @@ public class CreateServiceAccountCommandTests
|
|||||||
data.ServiceAccountId = saData.Id;
|
data.ServiceAccountId = saData.Id;
|
||||||
|
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(saData.Id).Returns(saData);
|
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(saData.Id).Returns(saData);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(saData.OrganizationId).Returns(true);
|
||||||
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(saData.OrganizationId).Returns(true);
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(saData.OrganizationId).Returns(true);
|
||||||
|
|
||||||
await sutProvider.Sut.CreateAsync(data, userId);
|
await sutProvider.Sut.CreateAsync(data, userId);
|
||||||
|
@ -28,7 +28,7 @@ public class DeleteProjectCommandTests
|
|||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task DeleteSecrets_OneIdNotFound_Throws_NotFoundException(List<Guid> data, Guid userId,
|
public async Task Delete_OneIdNotFound_Throws_NotFoundException(List<Guid> data, Guid userId,
|
||||||
SutProvider<DeleteProjectCommand> sutProvider)
|
SutProvider<DeleteProjectCommand> sutProvider)
|
||||||
{
|
{
|
||||||
var project = new Project()
|
var project = new Project()
|
||||||
@ -49,6 +49,7 @@ public class DeleteProjectCommandTests
|
|||||||
{
|
{
|
||||||
var projects = data.Select(id => new Project { Id = id, OrganizationId = organizationId }).ToList();
|
var projects = data.Select(id => new Project { Id = id, OrganizationId = organizationId }).ToList();
|
||||||
|
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(organizationId).Returns(true);
|
||||||
sutProvider.GetDependency<ICurrentContext>().ClientType = ClientType.User;
|
sutProvider.GetDependency<ICurrentContext>().ClientType = ClientType.User;
|
||||||
sutProvider.GetDependency<IProjectRepository>().GetManyByIds(data).Returns(projects);
|
sutProvider.GetDependency<IProjectRepository>().GetManyByIds(data).Returns(projects);
|
||||||
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(Arg.Any<Guid>(), userId).Returns(true);
|
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(Arg.Any<Guid>(), userId).Returns(true);
|
||||||
@ -65,11 +66,12 @@ public class DeleteProjectCommandTests
|
|||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task DeleteSecrets_User_No_Permission(List<Guid> data, Guid userId, Guid organizationId,
|
public async Task Delete_User_No_Permission(List<Guid> data, Guid userId, Guid organizationId,
|
||||||
SutProvider<DeleteProjectCommand> sutProvider)
|
SutProvider<DeleteProjectCommand> sutProvider)
|
||||||
{
|
{
|
||||||
var projects = data.Select(id => new Project { Id = id, OrganizationId = organizationId }).ToList();
|
var projects = data.Select(id => new Project { Id = id, OrganizationId = organizationId }).ToList();
|
||||||
|
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(organizationId).Returns(true);
|
||||||
sutProvider.GetDependency<ICurrentContext>().ClientType = ClientType.User;
|
sutProvider.GetDependency<ICurrentContext>().ClientType = ClientType.User;
|
||||||
sutProvider.GetDependency<IProjectRepository>().GetManyByIds(data).Returns(projects);
|
sutProvider.GetDependency<IProjectRepository>().GetManyByIds(data).Returns(projects);
|
||||||
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(userId, userId).Returns(false);
|
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(userId, userId).Returns(false);
|
||||||
@ -86,11 +88,12 @@ public class DeleteProjectCommandTests
|
|||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task DeleteSecrets_OrganizationAdmin_Success(List<Guid> data, Guid userId, Guid organizationId,
|
public async Task Delete_OrganizationAdmin_Success(List<Guid> data, Guid userId, Guid organizationId,
|
||||||
SutProvider<DeleteProjectCommand> sutProvider)
|
SutProvider<DeleteProjectCommand> sutProvider)
|
||||||
{
|
{
|
||||||
var projects = data.Select(id => new Project { Id = id, OrganizationId = organizationId }).ToList();
|
var projects = data.Select(id => new Project { Id = id, OrganizationId = organizationId }).ToList();
|
||||||
|
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(organizationId).Returns(true);
|
||||||
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(organizationId).Returns(true);
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(organizationId).Returns(true);
|
||||||
sutProvider.GetDependency<IProjectRepository>().GetManyByIds(data).Returns(projects);
|
sutProvider.GetDependency<IProjectRepository>().GetManyByIds(data).Returns(projects);
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ public class UpdateProjectCommandTests
|
|||||||
public async Task UpdateAsync_Admin_Succeeds(Project project, Guid userId, SutProvider<UpdateProjectCommand> sutProvider)
|
public async Task UpdateAsync_Admin_Succeeds(Project project, Guid userId, SutProvider<UpdateProjectCommand> sutProvider)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(project.Id).Returns(project);
|
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(project.Id).Returns(project);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(project.OrganizationId).Returns(true);
|
||||||
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(project.OrganizationId).Returns(true);
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(project.OrganizationId).Returns(true);
|
||||||
|
|
||||||
var project2 = new Project { Id = project.Id, Name = "newName" };
|
var project2 = new Project { Id = project.Id, Name = "newName" };
|
||||||
@ -51,8 +52,9 @@ public class UpdateProjectCommandTests
|
|||||||
{
|
{
|
||||||
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(project.Id).Returns(project);
|
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(project.Id).Returns(project);
|
||||||
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(project.Id, userId).Returns(false);
|
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(project.Id, userId).Returns(false);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(project.OrganizationId).Returns(true);
|
||||||
|
|
||||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => sutProvider.Sut.UpdateAsync(project, userId));
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateAsync(project, userId));
|
||||||
|
|
||||||
await sutProvider.GetDependency<IProjectRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
await sutProvider.GetDependency<IProjectRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
||||||
}
|
}
|
||||||
@ -63,6 +65,7 @@ public class UpdateProjectCommandTests
|
|||||||
{
|
{
|
||||||
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(project.Id).Returns(project);
|
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(project.Id).Returns(project);
|
||||||
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(project.Id, userId).Returns(true);
|
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(project.Id, userId).Returns(true);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(project.OrganizationId).Returns(true);
|
||||||
|
|
||||||
var project2 = new Project { Id = project.Id, Name = "newName" };
|
var project2 = new Project { Id = project.Id, Name = "newName" };
|
||||||
var result = await sutProvider.Sut.UpdateAsync(project2, userId);
|
var result = await sutProvider.Sut.UpdateAsync(project2, userId);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
using Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
||||||
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
@ -56,6 +57,7 @@ public class DeleteSecretCommandTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(data).Returns(secrets);
|
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(data).Returns(secrets);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(default).ReturnsForAnyArgs(true);
|
||||||
|
|
||||||
var results = await sutProvider.Sut.DeleteSecrets(data);
|
var results = await sutProvider.Sut.DeleteSecrets(data);
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using Bit.Commercial.Core.SecretsManager.Commands.ServiceAccounts;
|
using Bit.Commercial.Core.SecretsManager.Commands.ServiceAccounts;
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
@ -15,9 +17,18 @@ public class CreateServiceAccountCommandTests
|
|||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task CreateAsync_CallsCreate(ServiceAccount data,
|
public async Task CreateAsync_CallsCreate(ServiceAccount data,
|
||||||
|
Guid userId,
|
||||||
SutProvider<CreateServiceAccountCommand> sutProvider)
|
SutProvider<CreateServiceAccountCommand> sutProvider)
|
||||||
{
|
{
|
||||||
await sutProvider.Sut.CreateAsync(data);
|
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||||
|
.GetByOrganizationAsync(Arg.Any<Guid>(), Arg.Any<Guid>())
|
||||||
|
.Returns(new OrganizationUser() { Id = userId });
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IServiceAccountRepository>()
|
||||||
|
.CreateAsync(Arg.Any<ServiceAccount>())
|
||||||
|
.Returns(data);
|
||||||
|
|
||||||
|
await sutProvider.Sut.CreateAsync(data, userId);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IServiceAccountRepository>().Received(1)
|
await sutProvider.GetDependency<IServiceAccountRepository>().Received(1)
|
||||||
.CreateAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data)));
|
.CreateAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data)));
|
||||||
|
@ -18,7 +18,7 @@ public class UpdateServiceAccountCommandTests
|
|||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task UpdateAsync_ServiceAccountDoesNotExist_ThrowsNotFound(ServiceAccount data, Guid userId, SutProvider<UpdateServiceAccountCommand> sutProvider)
|
public async Task UpdateAsync_ServiceAccountDoesNotExist_ThrowsNotFound(ServiceAccount data, Guid userId, SutProvider<UpdateServiceAccountCommand> sutProvider)
|
||||||
{
|
{
|
||||||
var exception = await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateAsync(data, userId));
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateAsync(data, userId));
|
||||||
|
|
||||||
await sutProvider.GetDependency<IServiceAccountRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
await sutProvider.GetDependency<IServiceAccountRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
||||||
}
|
}
|
||||||
@ -30,7 +30,7 @@ public class UpdateServiceAccountCommandTests
|
|||||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(data.Id).Returns(data);
|
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(data.Id).Returns(data);
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(data.Id, userId).Returns(false);
|
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(data.Id, userId).Returns(false);
|
||||||
|
|
||||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => sutProvider.Sut.UpdateAsync(data, userId));
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateAsync(data, userId));
|
||||||
|
|
||||||
await sutProvider.GetDependency<IServiceAccountRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
await sutProvider.GetDependency<IServiceAccountRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
||||||
}
|
}
|
||||||
@ -39,6 +39,7 @@ public class UpdateServiceAccountCommandTests
|
|||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task UpdateAsync_User_Success(ServiceAccount data, Guid userId, SutProvider<UpdateServiceAccountCommand> sutProvider)
|
public async Task UpdateAsync_User_Success(ServiceAccount data, Guid userId, SutProvider<UpdateServiceAccountCommand> sutProvider)
|
||||||
{
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(data.OrganizationId).Returns(true);
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(data.Id).Returns(data);
|
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(data.Id).Returns(data);
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(data.Id, userId).Returns(true);
|
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(data.Id, userId).Returns(true);
|
||||||
|
|
||||||
@ -54,6 +55,7 @@ public class UpdateServiceAccountCommandTests
|
|||||||
public async Task UpdateAsync_Admin_Success(ServiceAccount data, Guid userId, SutProvider<UpdateServiceAccountCommand> sutProvider)
|
public async Task UpdateAsync_Admin_Success(ServiceAccount data, Guid userId, SutProvider<UpdateServiceAccountCommand> sutProvider)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(data.Id).Returns(data);
|
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(data.Id).Returns(data);
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(data.OrganizationId).Returns(true);
|
||||||
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(data.OrganizationId).Returns(true);
|
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(data.OrganizationId).Returns(true);
|
||||||
|
|
||||||
await sutProvider.Sut.UpdateAsync(data, userId);
|
await sutProvider.Sut.UpdateAsync(data, userId);
|
||||||
@ -66,6 +68,7 @@ public class UpdateServiceAccountCommandTests
|
|||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task UpdateAsync_DoesNotModifyOrganizationId(ServiceAccount existingServiceAccount, Guid userId, SutProvider<UpdateServiceAccountCommand> sutProvider)
|
public async Task UpdateAsync_DoesNotModifyOrganizationId(ServiceAccount existingServiceAccount, Guid userId, SutProvider<UpdateServiceAccountCommand> sutProvider)
|
||||||
{
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(existingServiceAccount.OrganizationId).Returns(true);
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(existingServiceAccount.Id).Returns(existingServiceAccount);
|
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(existingServiceAccount.Id).Returns(existingServiceAccount);
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(existingServiceAccount.Id, userId).Returns(true);
|
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(existingServiceAccount.Id, userId).Returns(true);
|
||||||
|
|
||||||
@ -87,6 +90,7 @@ public class UpdateServiceAccountCommandTests
|
|||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task UpdateAsync_DoesNotModifyCreationDate(ServiceAccount existingServiceAccount, Guid userId, SutProvider<UpdateServiceAccountCommand> sutProvider)
|
public async Task UpdateAsync_DoesNotModifyCreationDate(ServiceAccount existingServiceAccount, Guid userId, SutProvider<UpdateServiceAccountCommand> sutProvider)
|
||||||
{
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(existingServiceAccount.OrganizationId).Returns(true);
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(existingServiceAccount.Id).Returns(existingServiceAccount);
|
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(existingServiceAccount.Id).Returns(existingServiceAccount);
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(existingServiceAccount.Id, userId).Returns(true);
|
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(existingServiceAccount.Id, userId).Returns(true);
|
||||||
|
|
||||||
@ -108,6 +112,7 @@ public class UpdateServiceAccountCommandTests
|
|||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task UpdateAsync_RevisionDateIsUpdatedToUtcNow(ServiceAccount existingServiceAccount, Guid userId, SutProvider<UpdateServiceAccountCommand> sutProvider)
|
public async Task UpdateAsync_RevisionDateIsUpdatedToUtcNow(ServiceAccount existingServiceAccount, Guid userId, SutProvider<UpdateServiceAccountCommand> sutProvider)
|
||||||
{
|
{
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(existingServiceAccount.OrganizationId).Returns(true);
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(existingServiceAccount.Id).Returns(existingServiceAccount);
|
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(existingServiceAccount.Id).Returns(existingServiceAccount);
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(existingServiceAccount.Id, userId).Returns(true);
|
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(existingServiceAccount.Id, userId).Returns(true);
|
||||||
|
|
||||||
|
@ -6,7 +6,15 @@
|
|||||||
# in the future and investigate if we can migrate back.
|
# in the future and investigate if we can migrate back.
|
||||||
# docker-compose --profile mssql exec mssql bash /mnt/helpers/run_migrations.sh @args
|
# docker-compose --profile mssql exec mssql bash /mnt/helpers/run_migrations.sh @args
|
||||||
|
|
||||||
param([switch]$all = $false, [switch]$postgres = $false, [switch]$mysql = $false, [switch]$mssql = $false, [switch]$sqlite = $false)
|
param(
|
||||||
|
[switch]$all = $false,
|
||||||
|
[switch]$postgres = $false,
|
||||||
|
[switch]$mysql = $false,
|
||||||
|
[switch]$mssql = $false,
|
||||||
|
[switch]$sqlite = $false,
|
||||||
|
[switch]$selfhost = $false,
|
||||||
|
[switch]$pipeline = $false
|
||||||
|
)
|
||||||
|
|
||||||
if (!$all -and !$postgres -and !$mysql -and !$sqlite) {
|
if (!$all -and !$postgres -and !$mysql -and !$sqlite) {
|
||||||
$mssql = $true;
|
$mssql = $true;
|
||||||
@ -21,6 +29,12 @@ if ($all -or $postgres -or $mysql -or $sqlite) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($all -or $mssql) {
|
if ($all -or $mssql) {
|
||||||
|
if ($selfhost) {
|
||||||
|
$migrationArgs = "-s"
|
||||||
|
} elseif ($pipeline) {
|
||||||
|
$migrationArgs = "-p"
|
||||||
|
}
|
||||||
|
|
||||||
Write-Host "Starting Microsoft SQL Server Migrations"
|
Write-Host "Starting Microsoft SQL Server Migrations"
|
||||||
docker run `
|
docker run `
|
||||||
-v "$(pwd)/helpers/mssql:/mnt/helpers" `
|
-v "$(pwd)/helpers/mssql:/mnt/helpers" `
|
||||||
@ -30,7 +44,7 @@ if ($all -or $mssql) {
|
|||||||
--network=bitwardenserver_default `
|
--network=bitwardenserver_default `
|
||||||
--rm `
|
--rm `
|
||||||
mcr.microsoft.com/mssql-tools `
|
mcr.microsoft.com/mssql-tools `
|
||||||
/mnt/helpers/run_migrations.sh @args
|
/mnt/helpers/run_migrations.sh $migrationArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
$currentDir = Get-Location
|
$currentDir = Get-Location
|
||||||
|
@ -227,6 +227,7 @@ RUN mkdir -p /var/log/bitwarden
|
|||||||
RUN mkdir -p /var/log/nginx/logs
|
RUN mkdir -p /var/log/nginx/logs
|
||||||
RUN mkdir -p /etc/nginx/http.d
|
RUN mkdir -p /etc/nginx/http.d
|
||||||
RUN mkdir -p /var/run/nginx
|
RUN mkdir -p /var/run/nginx
|
||||||
|
RUN mkdir -p /var/lib/nginx/tmp
|
||||||
RUN touch /var/run/nginx/nginx.pid
|
RUN touch /var/run/nginx/nginx.pid
|
||||||
RUN mkdir -p /app
|
RUN mkdir -p /app
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ server {
|
|||||||
include /etc/nginx/security-headers-ssl.conf;
|
include /etc/nginx/security-headers-ssl.conf;
|
||||||
{{/if}}
|
{{/if}}
|
||||||
include /etc/nginx/security-headers.conf;
|
include /etc/nginx/security-headers.conf;
|
||||||
add_header Content-Security-Policy "{{{String.Coalesce env.BW_CSP "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://haveibeenpwned.com; child-src 'self' https://*.duosecurity.com https://*.duofederal.com; frame-src 'self' https://*.duosecurity.com https://*.duofederal.com; connect-src 'self' https://api.pwnedpasswords.com https://api.2fa.directory; object-src 'self' blob:;"}}}";
|
add_header Content-Security-Policy "{{{String.Coalesce env.BW_CSP "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://haveibeenpwned.com; child-src 'self' https://*.duosecurity.com https://*.duofederal.com; frame-src 'self' https://*.duosecurity.com https://*.duofederal.com; connect-src 'self' https://api.pwnedpasswords.com https://api.2fa.directory; object-src 'self' blob:;"}}}";
|
||||||
add_header X-Frame-Options SAMEORIGIN;
|
add_header X-Frame-Options SAMEORIGIN;
|
||||||
add_header X-Robots-Tag "noindex, nofollow";
|
add_header X-Robots-Tag "noindex, nofollow";
|
||||||
}
|
}
|
||||||
@ -101,7 +101,7 @@ server {
|
|||||||
root /app/Web;
|
root /app/Web;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /attachments {
|
location /attachments/ {
|
||||||
alias /etc/bitwarden/attachments/;
|
alias /etc/bitwarden/attachments/;
|
||||||
}
|
}
|
||||||
{{#if (String.Equal env.BW_ENABLE_API "true")}}
|
{{#if (String.Equal env.BW_ENABLE_API "true")}}
|
||||||
|
@ -3,6 +3,7 @@ using System.Text.Json;
|
|||||||
using Bit.Admin.Models;
|
using Bit.Admin.Models;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Models.BitStripe;
|
using Bit.Core.Models.BitStripe;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
@ -18,7 +19,7 @@ public class ToolsController : Controller
|
|||||||
{
|
{
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
private readonly IOrganizationRepository _organizationRepository;
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly IOrganizationService _organizationService;
|
private readonly ICloudGetOrganizationLicenseQuery _cloudGetOrganizationLicenseQuery;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly ITransactionRepository _transactionRepository;
|
private readonly ITransactionRepository _transactionRepository;
|
||||||
private readonly IInstallationRepository _installationRepository;
|
private readonly IInstallationRepository _installationRepository;
|
||||||
@ -30,7 +31,7 @@ public class ToolsController : Controller
|
|||||||
public ToolsController(
|
public ToolsController(
|
||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings,
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
IOrganizationService organizationService,
|
ICloudGetOrganizationLicenseQuery cloudGetOrganizationLicenseQuery,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
ITransactionRepository transactionRepository,
|
ITransactionRepository transactionRepository,
|
||||||
IInstallationRepository installationRepository,
|
IInstallationRepository installationRepository,
|
||||||
@ -41,7 +42,7 @@ public class ToolsController : Controller
|
|||||||
{
|
{
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_organizationService = organizationService;
|
_cloudGetOrganizationLicenseQuery = cloudGetOrganizationLicenseQuery;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_transactionRepository = transactionRepository;
|
_transactionRepository = transactionRepository;
|
||||||
_installationRepository = installationRepository;
|
_installationRepository = installationRepository;
|
||||||
@ -259,7 +260,7 @@ public class ToolsController : Controller
|
|||||||
|
|
||||||
if (organization != null)
|
if (organization != null)
|
||||||
{
|
{
|
||||||
var license = await _organizationService.GenerateLicenseAsync(organization,
|
var license = await _cloudGetOrganizationLicenseQuery.GetLicenseAsync(organization,
|
||||||
model.InstallationId.Value, model.Version);
|
model.InstallationId.Value, model.Version);
|
||||||
var ms = new MemoryStream();
|
var ms = new MemoryStream();
|
||||||
await JsonSerializer.SerializeAsync(ms, license, JsonHelpers.Indented);
|
await JsonSerializer.SerializeAsync(ms, license, JsonHelpers.Indented);
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Api.OrganizationLicenses;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
@ -14,26 +17,26 @@ namespace Bit.Api.Controllers;
|
|||||||
[SelfHosted(NotSelfHostedOnly = true)]
|
[SelfHosted(NotSelfHostedOnly = true)]
|
||||||
public class LicensesController : Controller
|
public class LicensesController : Controller
|
||||||
{
|
{
|
||||||
private readonly ILicensingService _licensingService;
|
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly IOrganizationRepository _organizationRepository;
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly IOrganizationService _organizationService;
|
private readonly ICloudGetOrganizationLicenseQuery _cloudGetOrganizationLicenseQuery;
|
||||||
|
private readonly IValidateBillingSyncKeyCommand _validateBillingSyncKeyCommand;
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
|
|
||||||
public LicensesController(
|
public LicensesController(
|
||||||
ILicensingService licensingService,
|
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
IOrganizationService organizationService,
|
ICloudGetOrganizationLicenseQuery cloudGetOrganizationLicenseQuery,
|
||||||
|
IValidateBillingSyncKeyCommand validateBillingSyncKeyCommand,
|
||||||
ICurrentContext currentContext)
|
ICurrentContext currentContext)
|
||||||
{
|
{
|
||||||
_licensingService = licensingService;
|
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_organizationService = organizationService;
|
_cloudGetOrganizationLicenseQuery = cloudGetOrganizationLicenseQuery;
|
||||||
|
_validateBillingSyncKeyCommand = validateBillingSyncKeyCommand;
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,21 +58,30 @@ public class LicensesController : Controller
|
|||||||
return license;
|
return license;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used by self-hosted installations to get an updated license file
|
||||||
|
/// </summary>
|
||||||
[HttpGet("organization/{id}")]
|
[HttpGet("organization/{id}")]
|
||||||
public async Task<OrganizationLicense> GetOrganization(string id, [FromQuery] string key)
|
public async Task<OrganizationLicense> OrganizationSync(string id, [FromBody] SelfHostedOrganizationLicenseRequestModel model)
|
||||||
{
|
{
|
||||||
var org = await _organizationRepository.GetByIdAsync(new Guid(id));
|
var organization = await _organizationRepository.GetByIdAsync(new Guid(id));
|
||||||
if (org == null)
|
if (organization == null)
|
||||||
{
|
{
|
||||||
return null;
|
throw new NotFoundException("Organization not found.");
|
||||||
}
|
}
|
||||||
else if (!org.LicenseKey.Equals(key))
|
|
||||||
|
if (!organization.LicenseKey.Equals(model.LicenseKey))
|
||||||
{
|
{
|
||||||
await Task.Delay(2000);
|
await Task.Delay(2000);
|
||||||
throw new BadRequestException("Invalid license key.");
|
throw new BadRequestException("Invalid license key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var license = await _organizationService.GenerateLicenseAsync(org, _currentContext.InstallationId.Value);
|
if (!await _validateBillingSyncKeyCommand.ValidateBillingSyncKeyAsync(organization, model.BillingSyncKey))
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Invalid Billing Sync Key");
|
||||||
|
}
|
||||||
|
|
||||||
|
var license = await _cloudGetOrganizationLicenseQuery.GetLicenseAsync(organization, _currentContext.InstallationId.Value);
|
||||||
return license;
|
return license;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,7 @@ public class OrganizationConnectionsController : Controller
|
|||||||
Guid? organizationConnectionId,
|
Guid? organizationConnectionId,
|
||||||
OrganizationConnectionRequestModel model,
|
OrganizationConnectionRequestModel model,
|
||||||
Func<OrganizationConnectionRequestModel<T>, Task> validateAction = null)
|
Func<OrganizationConnectionRequestModel<T>, Task> validateAction = null)
|
||||||
where T : new()
|
where T : IConnectionConfig
|
||||||
{
|
{
|
||||||
var typedModel = new OrganizationConnectionRequestModel<T>(model);
|
var typedModel = new OrganizationConnectionRequestModel<T>(model);
|
||||||
if (validateAction != null)
|
if (validateAction != null)
|
||||||
|
@ -5,6 +5,7 @@ using Bit.Core.Entities;
|
|||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Api.Request.OrganizationSponsorships;
|
using Bit.Core.Models.Api.Request.OrganizationSponsorships;
|
||||||
using Bit.Core.Models.Api.Response.OrganizationSponsorships;
|
using Bit.Core.Models.Api.Response.OrganizationSponsorships;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
|
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
@ -11,6 +11,7 @@ using Bit.Core.Exceptions;
|
|||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.Models.Data.Organizations.Policies;
|
using Bit.Core.Models.Data.Organizations.Policies;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationApiKeys.Interfaces;
|
using Bit.Core.OrganizationFeatures.OrganizationApiKeys.Interfaces;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
@ -37,6 +38,7 @@ public class OrganizationsController : Controller
|
|||||||
private readonly IRotateOrganizationApiKeyCommand _rotateOrganizationApiKeyCommand;
|
private readonly IRotateOrganizationApiKeyCommand _rotateOrganizationApiKeyCommand;
|
||||||
private readonly ICreateOrganizationApiKeyCommand _createOrganizationApiKeyCommand;
|
private readonly ICreateOrganizationApiKeyCommand _createOrganizationApiKeyCommand;
|
||||||
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
|
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
|
||||||
|
private readonly ICloudGetOrganizationLicenseQuery _cloudGetOrganizationLicenseQuery;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
|
||||||
public OrganizationsController(
|
public OrganizationsController(
|
||||||
@ -53,6 +55,7 @@ public class OrganizationsController : Controller
|
|||||||
IRotateOrganizationApiKeyCommand rotateOrganizationApiKeyCommand,
|
IRotateOrganizationApiKeyCommand rotateOrganizationApiKeyCommand,
|
||||||
ICreateOrganizationApiKeyCommand createOrganizationApiKeyCommand,
|
ICreateOrganizationApiKeyCommand createOrganizationApiKeyCommand,
|
||||||
IOrganizationApiKeyRepository organizationApiKeyRepository,
|
IOrganizationApiKeyRepository organizationApiKeyRepository,
|
||||||
|
ICloudGetOrganizationLicenseQuery cloudGetOrganizationLicenseQuery,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
@ -68,6 +71,7 @@ public class OrganizationsController : Controller
|
|||||||
_rotateOrganizationApiKeyCommand = rotateOrganizationApiKeyCommand;
|
_rotateOrganizationApiKeyCommand = rotateOrganizationApiKeyCommand;
|
||||||
_createOrganizationApiKeyCommand = createOrganizationApiKeyCommand;
|
_createOrganizationApiKeyCommand = createOrganizationApiKeyCommand;
|
||||||
_organizationApiKeyRepository = organizationApiKeyRepository;
|
_organizationApiKeyRepository = organizationApiKeyRepository;
|
||||||
|
_cloudGetOrganizationLicenseQuery = cloudGetOrganizationLicenseQuery;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +153,8 @@ public class OrganizationsController : Controller
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var license = await _organizationService.GenerateLicenseAsync(orgIdGuid, installationId);
|
var org = await _organizationRepository.GetByIdAsync(new Guid(id));
|
||||||
|
var license = await _cloudGetOrganizationLicenseQuery.GetLicenseAsync(org, installationId);
|
||||||
if (license == null)
|
if (license == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -215,6 +220,7 @@ 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")]
|
[HttpPost("license")]
|
||||||
[SelfHosted(SelfHostedOnly = true)]
|
[SelfHosted(SelfHostedOnly = true)]
|
||||||
public async Task<OrganizationResponseModel> PostLicense(OrganizationCreateLicenseRequestModel model)
|
public async Task<OrganizationResponseModel> PostLicense(OrganizationCreateLicenseRequestModel model)
|
||||||
@ -448,6 +454,7 @@ public class OrganizationsController : Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("2022-12-7 Moved to SelfHostedOrganizationLicensesController, to be removed in EC-815")]
|
||||||
[HttpPost("{id}/license")]
|
[HttpPost("{id}/license")]
|
||||||
[SelfHosted(SelfHostedOnly = true)]
|
[SelfHosted(SelfHostedOnly = true)]
|
||||||
public async Task PostLicense(string id, LicenseRequestModel model)
|
public async Task PostLicense(string id, LicenseRequestModel model)
|
||||||
|
@ -0,0 +1,117 @@
|
|||||||
|
using Bit.Api.Models.Request;
|
||||||
|
using Bit.Api.Models.Request.Organizations;
|
||||||
|
using Bit.Api.Models.Response.Organizations;
|
||||||
|
using Bit.Api.Utilities;
|
||||||
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Business;
|
||||||
|
using Bit.Core.Models.OrganizationConnectionConfigs;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Bit.Api.Controllers.SelfHosted;
|
||||||
|
|
||||||
|
[Route("organizations/licenses/self-hosted")]
|
||||||
|
[Authorize("Application")]
|
||||||
|
[SelfHosted(SelfHostedOnly = true)]
|
||||||
|
public class SelfHostedOrganizationLicensesController : Controller
|
||||||
|
{
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
|
private readonly ISelfHostedGetOrganizationLicenseQuery _selfHostedGetOrganizationLicenseQuery;
|
||||||
|
private readonly IOrganizationConnectionRepository _organizationConnectionRepository;
|
||||||
|
private readonly IOrganizationService _organizationService;
|
||||||
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
|
public SelfHostedOrganizationLicensesController(
|
||||||
|
ICurrentContext currentContext,
|
||||||
|
ISelfHostedGetOrganizationLicenseQuery selfHostedGetOrganizationLicenseQuery,
|
||||||
|
IOrganizationConnectionRepository organizationConnectionRepository,
|
||||||
|
IOrganizationService organizationService,
|
||||||
|
IOrganizationRepository organizationRepository,
|
||||||
|
IUserService userService)
|
||||||
|
{
|
||||||
|
_currentContext = currentContext;
|
||||||
|
_selfHostedGetOrganizationLicenseQuery = selfHostedGetOrganizationLicenseQuery;
|
||||||
|
_organizationConnectionRepository = organizationConnectionRepository;
|
||||||
|
_organizationService = organizationService;
|
||||||
|
_organizationRepository = organizationRepository;
|
||||||
|
_userService = userService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("")]
|
||||||
|
public async Task<OrganizationResponseModel> PostLicenseAsync(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("{id}")]
|
||||||
|
public async Task PostLicenseAsync(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");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _organizationService.UpdateLicenseAsync(new Guid(id), license);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("{id}/sync")]
|
||||||
|
public async Task SyncLicenseAsync(string id)
|
||||||
|
{
|
||||||
|
var organization = await _organizationRepository.GetByIdAsync(new Guid(id));
|
||||||
|
if (organization == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await _currentContext.OrganizationOwner(organization.Id))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var billingSyncConnection =
|
||||||
|
(await _organizationConnectionRepository.GetByOrganizationIdTypeAsync(organization.Id,
|
||||||
|
OrganizationConnectionType.CloudBillingSync)).FirstOrDefault();
|
||||||
|
if (billingSyncConnection == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException("Unable to get Cloud Billing Sync connection");
|
||||||
|
}
|
||||||
|
|
||||||
|
var license =
|
||||||
|
await _selfHostedGetOrganizationLicenseQuery.GetLicenseAsync(organization, billingSyncConnection);
|
||||||
|
|
||||||
|
await _organizationService.UpdateLicenseAsync(organization.Id, license);
|
||||||
|
|
||||||
|
var config = billingSyncConnection.GetConfig<BillingSyncConfig>();
|
||||||
|
config.LastLicenseSync = DateTime.Now;
|
||||||
|
billingSyncConnection.SetConfig(config);
|
||||||
|
await _organizationConnectionRepository.ReplaceAsync(billingSyncConnection);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationConnections;
|
using Bit.Core.Models.Data.Organizations.OrganizationConnections;
|
||||||
|
using Bit.Core.Models.OrganizationConnectionConfigs;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Api.Models.Request.Organizations;
|
namespace Bit.Api.Models.Request.Organizations;
|
||||||
@ -17,7 +18,7 @@ public class OrganizationConnectionRequestModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class OrganizationConnectionRequestModel<T> : OrganizationConnectionRequestModel where T : new()
|
public class OrganizationConnectionRequestModel<T> : OrganizationConnectionRequestModel where T : IConnectionConfig
|
||||||
{
|
{
|
||||||
public T ParsedConfig { get; private set; }
|
public T ParsedConfig { get; private set; }
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ public class OrganizationUserInviteRequestModel
|
|||||||
[Required]
|
[Required]
|
||||||
public OrganizationUserType? Type { get; set; }
|
public OrganizationUserType? Type { get; set; }
|
||||||
public bool AccessAll { get; set; }
|
public bool AccessAll { get; set; }
|
||||||
|
public bool AccessSecretsManager { get; set; }
|
||||||
public Permissions Permissions { get; set; }
|
public Permissions Permissions { get; set; }
|
||||||
public IEnumerable<SelectionReadOnlyRequestModel> Collections { get; set; }
|
public IEnumerable<SelectionReadOnlyRequestModel> Collections { get; set; }
|
||||||
public IEnumerable<Guid> Groups { get; set; }
|
public IEnumerable<Guid> Groups { get; set; }
|
||||||
@ -28,6 +29,7 @@ public class OrganizationUserInviteRequestModel
|
|||||||
Emails = Emails,
|
Emails = Emails,
|
||||||
Type = Type,
|
Type = Type,
|
||||||
AccessAll = AccessAll,
|
AccessAll = AccessAll,
|
||||||
|
AccessSecretsManager = AccessSecretsManager,
|
||||||
Collections = Collections?.Select(c => c.ToSelectionReadOnly()),
|
Collections = Collections?.Select(c => c.ToSelectionReadOnly()),
|
||||||
Groups = Groups,
|
Groups = Groups,
|
||||||
Permissions = Permissions,
|
Permissions = Permissions,
|
||||||
@ -73,6 +75,7 @@ public class OrganizationUserUpdateRequestModel
|
|||||||
[Required]
|
[Required]
|
||||||
public OrganizationUserType? Type { get; set; }
|
public OrganizationUserType? Type { get; set; }
|
||||||
public bool AccessAll { get; set; }
|
public bool AccessAll { get; set; }
|
||||||
|
public bool AccessSecretsManager { get; set; }
|
||||||
public Permissions Permissions { get; set; }
|
public Permissions Permissions { get; set; }
|
||||||
public IEnumerable<SelectionReadOnlyRequestModel> Collections { get; set; }
|
public IEnumerable<SelectionReadOnlyRequestModel> Collections { get; set; }
|
||||||
public IEnumerable<Guid> Groups { get; set; }
|
public IEnumerable<Guid> Groups { get; set; }
|
||||||
@ -85,6 +88,7 @@ public class OrganizationUserUpdateRequestModel
|
|||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
});
|
});
|
||||||
existingUser.AccessAll = AccessAll;
|
existingUser.AccessAll = AccessAll;
|
||||||
|
existingUser.AccessSecretsManager = AccessSecretsManager;
|
||||||
return existingUser;
|
return existingUser;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ public class OrganizationUserResponseModel : ResponseModel
|
|||||||
Type = organizationUser.Type;
|
Type = organizationUser.Type;
|
||||||
Status = organizationUser.Status;
|
Status = organizationUser.Status;
|
||||||
AccessAll = organizationUser.AccessAll;
|
AccessAll = organizationUser.AccessAll;
|
||||||
|
AccessSecretsManager = organizationUser.AccessSecretsManager;
|
||||||
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(organizationUser.Permissions);
|
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(organizationUser.Permissions);
|
||||||
ResetPasswordEnrolled = !string.IsNullOrEmpty(organizationUser.ResetPasswordKey);
|
ResetPasswordEnrolled = !string.IsNullOrEmpty(organizationUser.ResetPasswordKey);
|
||||||
}
|
}
|
||||||
@ -40,6 +41,7 @@ public class OrganizationUserResponseModel : ResponseModel
|
|||||||
Type = organizationUser.Type;
|
Type = organizationUser.Type;
|
||||||
Status = organizationUser.Status;
|
Status = organizationUser.Status;
|
||||||
AccessAll = organizationUser.AccessAll;
|
AccessAll = organizationUser.AccessAll;
|
||||||
|
AccessSecretsManager = organizationUser.AccessSecretsManager;
|
||||||
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(organizationUser.Permissions);
|
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(organizationUser.Permissions);
|
||||||
ResetPasswordEnrolled = !string.IsNullOrEmpty(organizationUser.ResetPasswordKey);
|
ResetPasswordEnrolled = !string.IsNullOrEmpty(organizationUser.ResetPasswordKey);
|
||||||
UsesKeyConnector = organizationUser.UsesKeyConnector;
|
UsesKeyConnector = organizationUser.UsesKeyConnector;
|
||||||
@ -50,6 +52,7 @@ public class OrganizationUserResponseModel : ResponseModel
|
|||||||
public OrganizationUserType Type { get; set; }
|
public OrganizationUserType Type { get; set; }
|
||||||
public OrganizationUserStatusType Status { get; set; }
|
public OrganizationUserStatusType Status { get; set; }
|
||||||
public bool AccessAll { get; set; }
|
public bool AccessAll { get; set; }
|
||||||
|
public bool AccessSecretsManager { get; set; }
|
||||||
public Permissions Permissions { get; set; }
|
public Permissions Permissions { get; set; }
|
||||||
public bool ResetPasswordEnrolled { get; set; }
|
public bool ResetPasswordEnrolled { get; set; }
|
||||||
public bool UsesKeyConnector { get; set; }
|
public bool UsesKeyConnector { get; set; }
|
||||||
|
@ -52,6 +52,7 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
|||||||
FamilySponsorshipLastSyncDate = organization.FamilySponsorshipLastSyncDate;
|
FamilySponsorshipLastSyncDate = organization.FamilySponsorshipLastSyncDate;
|
||||||
FamilySponsorshipToDelete = organization.FamilySponsorshipToDelete;
|
FamilySponsorshipToDelete = organization.FamilySponsorshipToDelete;
|
||||||
FamilySponsorshipValidUntil = organization.FamilySponsorshipValidUntil;
|
FamilySponsorshipValidUntil = organization.FamilySponsorshipValidUntil;
|
||||||
|
AccessSecretsManager = organization.AccessSecretsManager;
|
||||||
|
|
||||||
if (organization.SsoConfig != null)
|
if (organization.SsoConfig != null)
|
||||||
{
|
{
|
||||||
@ -101,4 +102,5 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
|||||||
public DateTime? FamilySponsorshipLastSyncDate { get; set; }
|
public DateTime? FamilySponsorshipLastSyncDate { get; set; }
|
||||||
public DateTime? FamilySponsorshipValidUntil { get; set; }
|
public DateTime? FamilySponsorshipValidUntil { get; set; }
|
||||||
public bool? FamilySponsorshipToDelete { get; set; }
|
public bool? FamilySponsorshipToDelete { get; set; }
|
||||||
|
public bool AccessSecretsManager { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Api.Models.Response;
|
namespace Bit.Api.Models.Response;
|
||||||
|
|
||||||
@ -39,5 +40,6 @@ public class ProfileProviderOrganizationResponseModel : ProfileOrganizationRespo
|
|||||||
UserId = organization.UserId?.ToString();
|
UserId = organization.UserId?.ToString();
|
||||||
ProviderId = organization.ProviderId?.ToString();
|
ProviderId = organization.ProviderId?.ToString();
|
||||||
ProviderName = organization.ProviderName;
|
ProviderName = organization.ProviderName;
|
||||||
|
PlanProductType = StaticStore.GetPlan(organization.PlanType).Product;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ public class AccessPoliciesController : Controller
|
|||||||
[HttpGet("/projects/{id}/access-policies")]
|
[HttpGet("/projects/{id}/access-policies")]
|
||||||
public async Task<ProjectAccessPoliciesResponseModel> GetProjectAccessPoliciesAsync([FromRoute] Guid id)
|
public async Task<ProjectAccessPoliciesResponseModel> GetProjectAccessPoliciesAsync([FromRoute] Guid id)
|
||||||
{
|
{
|
||||||
var results = await _accessPolicyRepository.GetManyByProjectId(id);
|
var results = await _accessPolicyRepository.GetManyByGrantedProjectIdAsync(id);
|
||||||
return new ProjectAccessPoliciesResponseModel(results);
|
return new ProjectAccessPoliciesResponseModel(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,61 +7,46 @@ using Bit.Core.Exceptions;
|
|||||||
using Bit.Core.SecretsManager.Commands.Projects.Interfaces;
|
using Bit.Core.SecretsManager.Commands.Projects.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Bit.Api.SecretsManager.Controllers;
|
namespace Bit.Api.SecretsManager.Controllers;
|
||||||
|
|
||||||
[SecretsManager]
|
[SecretsManager]
|
||||||
|
[Authorize("secrets")]
|
||||||
public class ProjectsController : Controller
|
public class ProjectsController : Controller
|
||||||
{
|
{
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly IProjectRepository _projectRepository;
|
private readonly IProjectRepository _projectRepository;
|
||||||
private readonly ICreateProjectCommand _createProjectCommand;
|
private readonly ICreateProjectCommand _createProjectCommand;
|
||||||
private readonly IUpdateProjectCommand _updateProjectCommand;
|
private readonly IUpdateProjectCommand _updateProjectCommand;
|
||||||
private readonly IDeleteProjectCommand _deleteProjectCommand;
|
private readonly IDeleteProjectCommand _deleteProjectCommand;
|
||||||
private readonly ICurrentContext _currentContext;
|
|
||||||
|
|
||||||
public ProjectsController(
|
public ProjectsController(
|
||||||
|
ICurrentContext currentContext,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
IProjectRepository projectRepository,
|
IProjectRepository projectRepository,
|
||||||
ICreateProjectCommand createProjectCommand,
|
ICreateProjectCommand createProjectCommand,
|
||||||
IUpdateProjectCommand updateProjectCommand,
|
IUpdateProjectCommand updateProjectCommand,
|
||||||
IDeleteProjectCommand deleteProjectCommand,
|
IDeleteProjectCommand deleteProjectCommand)
|
||||||
ICurrentContext currentContext)
|
|
||||||
{
|
{
|
||||||
|
_currentContext = currentContext;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_projectRepository = projectRepository;
|
_projectRepository = projectRepository;
|
||||||
_createProjectCommand = createProjectCommand;
|
_createProjectCommand = createProjectCommand;
|
||||||
_updateProjectCommand = updateProjectCommand;
|
_updateProjectCommand = updateProjectCommand;
|
||||||
_deleteProjectCommand = deleteProjectCommand;
|
_deleteProjectCommand = deleteProjectCommand;
|
||||||
_currentContext = currentContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("organizations/{organizationId}/projects")]
|
[HttpGet("organizations/{organizationId}/projects")]
|
||||||
public async Task<ProjectResponseModel> CreateAsync([FromRoute] Guid organizationId, [FromBody] ProjectCreateRequestModel createRequest)
|
public async Task<ListResponseModel<ProjectResponseModel>> ListByOrganizationAsync([FromRoute] Guid organizationId)
|
||||||
{
|
{
|
||||||
if (!await _currentContext.OrganizationUser(organizationId))
|
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await _createProjectCommand.CreateAsync(createRequest.ToProject(organizationId));
|
|
||||||
return new ProjectResponseModel(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPut("projects/{id}")]
|
|
||||||
public async Task<ProjectResponseModel> UpdateProjectAsync([FromRoute] Guid id, [FromBody] ProjectUpdateRequestModel updateRequest)
|
|
||||||
{
|
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
|
||||||
|
|
||||||
var result = await _updateProjectCommand.UpdateAsync(updateRequest.ToProject(id), userId);
|
|
||||||
return new ProjectResponseModel(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("organizations/{organizationId}/projects")]
|
|
||||||
public async Task<ListResponseModel<ProjectResponseModel>> GetProjectsByOrganizationAsync(
|
|
||||||
[FromRoute] Guid organizationId)
|
|
||||||
{
|
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
||||||
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
@ -72,8 +57,29 @@ public class ProjectsController : Controller
|
|||||||
return new ListResponseModel<ProjectResponseModel>(responses);
|
return new ListResponseModel<ProjectResponseModel>(responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("organizations/{organizationId}/projects")]
|
||||||
|
public async Task<ProjectResponseModel> CreateAsync([FromRoute] Guid organizationId, [FromBody] ProjectCreateRequestModel createRequest)
|
||||||
|
{
|
||||||
|
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _createProjectCommand.CreateAsync(createRequest.ToProject(organizationId));
|
||||||
|
return new ProjectResponseModel(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPut("projects/{id}")]
|
||||||
|
public async Task<ProjectResponseModel> UpdateAsync([FromRoute] Guid id, [FromBody] ProjectUpdateRequestModel updateRequest)
|
||||||
|
{
|
||||||
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
|
||||||
|
var result = await _updateProjectCommand.UpdateAsync(updateRequest.ToProject(id), userId);
|
||||||
|
return new ProjectResponseModel(result);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("projects/{id}")]
|
[HttpGet("projects/{id}")]
|
||||||
public async Task<ProjectResponseModel> GetProjectAsync([FromRoute] Guid id)
|
public async Task<ProjectResponseModel> GetAsync([FromRoute] Guid id)
|
||||||
{
|
{
|
||||||
var project = await _projectRepository.GetByIdAsync(id);
|
var project = await _projectRepository.GetByIdAsync(id);
|
||||||
if (project == null)
|
if (project == null)
|
||||||
@ -81,6 +87,11 @@ public class ProjectsController : Controller
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_currentContext.AccessSecretsManager(project.OrganizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var orgAdmin = await _currentContext.OrganizationAdmin(project.OrganizationId);
|
var orgAdmin = await _currentContext.OrganizationAdmin(project.OrganizationId);
|
||||||
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
@ -101,7 +112,7 @@ public class ProjectsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("projects/delete")]
|
[HttpPost("projects/delete")]
|
||||||
public async Task<ListResponseModel<BulkDeleteResponseModel>> BulkDeleteProjectsAsync([FromBody] List<Guid> ids)
|
public async Task<ListResponseModel<BulkDeleteResponseModel>> BulkDeleteAsync([FromBody] List<Guid> ids)
|
||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using Bit.Api.Models.Response;
|
using Bit.Api.Models.Response;
|
||||||
using Bit.Api.SecretsManager.Models.Request;
|
using Bit.Api.SecretsManager.Models.Request;
|
||||||
using Bit.Api.SecretsManager.Models.Response;
|
using Bit.Api.SecretsManager.Models.Response;
|
||||||
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
@ -13,30 +14,52 @@ namespace Bit.Api.SecretsManager.Controllers;
|
|||||||
[Authorize("secrets")]
|
[Authorize("secrets")]
|
||||||
public class SecretsController : Controller
|
public class SecretsController : Controller
|
||||||
{
|
{
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly ISecretRepository _secretRepository;
|
private readonly ISecretRepository _secretRepository;
|
||||||
private readonly IProjectRepository _projectRepository;
|
|
||||||
private readonly ICreateSecretCommand _createSecretCommand;
|
private readonly ICreateSecretCommand _createSecretCommand;
|
||||||
private readonly IUpdateSecretCommand _updateSecretCommand;
|
private readonly IUpdateSecretCommand _updateSecretCommand;
|
||||||
private readonly IDeleteSecretCommand _deleteSecretCommand;
|
private readonly IDeleteSecretCommand _deleteSecretCommand;
|
||||||
|
|
||||||
public SecretsController(ISecretRepository secretRepository, IProjectRepository projectRepository, ICreateSecretCommand createSecretCommand, IUpdateSecretCommand updateSecretCommand, IDeleteSecretCommand deleteSecretCommand)
|
public SecretsController(
|
||||||
|
ICurrentContext currentContext,
|
||||||
|
ISecretRepository secretRepository,
|
||||||
|
ICreateSecretCommand createSecretCommand,
|
||||||
|
IUpdateSecretCommand updateSecretCommand,
|
||||||
|
IDeleteSecretCommand deleteSecretCommand)
|
||||||
{
|
{
|
||||||
|
_currentContext = currentContext;
|
||||||
_secretRepository = secretRepository;
|
_secretRepository = secretRepository;
|
||||||
_projectRepository = projectRepository;
|
|
||||||
_createSecretCommand = createSecretCommand;
|
_createSecretCommand = createSecretCommand;
|
||||||
_updateSecretCommand = updateSecretCommand;
|
_updateSecretCommand = updateSecretCommand;
|
||||||
_deleteSecretCommand = deleteSecretCommand;
|
_deleteSecretCommand = deleteSecretCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("organizations/{organizationId}/secrets")]
|
[HttpGet("organizations/{organizationId}/secrets")]
|
||||||
public async Task<SecretWithProjectsListResponseModel> GetSecretsByOrganizationAsync([FromRoute] Guid organizationId)
|
public async Task<SecretWithProjectsListResponseModel> ListByOrganizationAsync([FromRoute] Guid organizationId)
|
||||||
{
|
{
|
||||||
|
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
var secrets = await _secretRepository.GetManyByOrganizationIdAsync(organizationId);
|
var secrets = await _secretRepository.GetManyByOrganizationIdAsync(organizationId);
|
||||||
return new SecretWithProjectsListResponseModel(secrets);
|
return new SecretWithProjectsListResponseModel(secrets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("organizations/{organizationId}/secrets")]
|
||||||
|
public async Task<SecretResponseModel> CreateAsync([FromRoute] Guid organizationId, [FromBody] SecretCreateRequestModel createRequest)
|
||||||
|
{
|
||||||
|
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _createSecretCommand.CreateAsync(createRequest.ToSecret(organizationId));
|
||||||
|
return new SecretResponseModel(result);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("secrets/{id}")]
|
[HttpGet("secrets/{id}")]
|
||||||
public async Task<SecretResponseModel> GetSecretAsync([FromRoute] Guid id)
|
public async Task<SecretResponseModel> GetAsync([FromRoute] Guid id)
|
||||||
{
|
{
|
||||||
var secret = await _secretRepository.GetByIdAsync(id);
|
var secret = await _secretRepository.GetByIdAsync(id);
|
||||||
if (secret == null)
|
if (secret == null)
|
||||||
@ -54,15 +77,8 @@ public class SecretsController : Controller
|
|||||||
return new SecretWithProjectsListResponseModel(secrets);
|
return new SecretWithProjectsListResponseModel(secrets);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("organizations/{organizationId}/secrets")]
|
|
||||||
public async Task<SecretResponseModel> CreateSecretAsync([FromRoute] Guid organizationId, [FromBody] SecretCreateRequestModel createRequest)
|
|
||||||
{
|
|
||||||
var result = await _createSecretCommand.CreateAsync(createRequest.ToSecret(organizationId));
|
|
||||||
return new SecretResponseModel(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPut("secrets/{id}")]
|
[HttpPut("secrets/{id}")]
|
||||||
public async Task<SecretResponseModel> UpdateSecretAsync([FromRoute] Guid id, [FromBody] SecretUpdateRequestModel updateRequest)
|
public async Task<SecretResponseModel> UpdateAsync([FromRoute] Guid id, [FromBody] SecretUpdateRequestModel updateRequest)
|
||||||
{
|
{
|
||||||
var result = await _updateSecretCommand.UpdateAsync(updateRequest.ToSecret(id));
|
var result = await _updateSecretCommand.UpdateAsync(updateRequest.ToSecret(id));
|
||||||
return new SecretResponseModel(result);
|
return new SecretResponseModel(result);
|
||||||
|
@ -8,43 +8,50 @@ using Bit.Core.SecretsManager.Commands.AccessTokens.Interfaces;
|
|||||||
using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
|
using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Bit.Api.SecretsManager.Controllers;
|
namespace Bit.Api.SecretsManager.Controllers;
|
||||||
|
|
||||||
[SecretsManager]
|
[SecretsManager]
|
||||||
|
[Authorize("secrets")]
|
||||||
[Route("service-accounts")]
|
[Route("service-accounts")]
|
||||||
public class ServiceAccountsController : Controller
|
public class ServiceAccountsController : Controller
|
||||||
{
|
{
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly IApiKeyRepository _apiKeyRepository;
|
private readonly IApiKeyRepository _apiKeyRepository;
|
||||||
private readonly ICreateAccessTokenCommand _createAccessTokenCommand;
|
private readonly ICreateAccessTokenCommand _createAccessTokenCommand;
|
||||||
private readonly ICreateServiceAccountCommand _createServiceAccountCommand;
|
private readonly ICreateServiceAccountCommand _createServiceAccountCommand;
|
||||||
private readonly ICurrentContext _currentContext;
|
|
||||||
private readonly IServiceAccountRepository _serviceAccountRepository;
|
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||||
private readonly IUpdateServiceAccountCommand _updateServiceAccountCommand;
|
private readonly IUpdateServiceAccountCommand _updateServiceAccountCommand;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
public ServiceAccountsController(
|
public ServiceAccountsController(
|
||||||
|
ICurrentContext currentContext,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
IServiceAccountRepository serviceAccountRepository,
|
IServiceAccountRepository serviceAccountRepository,
|
||||||
ICreateAccessTokenCommand createAccessTokenCommand,
|
ICreateAccessTokenCommand createAccessTokenCommand,
|
||||||
IApiKeyRepository apiKeyRepository, ICreateServiceAccountCommand createServiceAccountCommand,
|
IApiKeyRepository apiKeyRepository, ICreateServiceAccountCommand createServiceAccountCommand,
|
||||||
IUpdateServiceAccountCommand updateServiceAccountCommand,
|
IUpdateServiceAccountCommand updateServiceAccountCommand)
|
||||||
ICurrentContext currentContext)
|
|
||||||
{
|
{
|
||||||
|
_currentContext = currentContext;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_serviceAccountRepository = serviceAccountRepository;
|
_serviceAccountRepository = serviceAccountRepository;
|
||||||
_apiKeyRepository = apiKeyRepository;
|
_apiKeyRepository = apiKeyRepository;
|
||||||
_createServiceAccountCommand = createServiceAccountCommand;
|
_createServiceAccountCommand = createServiceAccountCommand;
|
||||||
_updateServiceAccountCommand = updateServiceAccountCommand;
|
_updateServiceAccountCommand = updateServiceAccountCommand;
|
||||||
_createAccessTokenCommand = createAccessTokenCommand;
|
_createAccessTokenCommand = createAccessTokenCommand;
|
||||||
_currentContext = currentContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("/organizations/{organizationId}/service-accounts")]
|
[HttpGet("/organizations/{organizationId}/service-accounts")]
|
||||||
public async Task<ListResponseModel<ServiceAccountResponseModel>> GetServiceAccountsByOrganizationAsync(
|
public async Task<ListResponseModel<ServiceAccountResponseModel>> ListByOrganizationAsync(
|
||||||
[FromRoute] Guid organizationId)
|
[FromRoute] Guid organizationId)
|
||||||
{
|
{
|
||||||
|
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
||||||
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
@ -57,20 +64,20 @@ public class ServiceAccountsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("/organizations/{organizationId}/service-accounts")]
|
[HttpPost("/organizations/{organizationId}/service-accounts")]
|
||||||
public async Task<ServiceAccountResponseModel> CreateServiceAccountAsync([FromRoute] Guid organizationId,
|
public async Task<ServiceAccountResponseModel> CreateAsync([FromRoute] Guid organizationId,
|
||||||
[FromBody] ServiceAccountCreateRequestModel createRequest)
|
[FromBody] ServiceAccountCreateRequestModel createRequest)
|
||||||
{
|
{
|
||||||
if (!await _currentContext.OrganizationUser(organizationId))
|
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var result = await _createServiceAccountCommand.CreateAsync(createRequest.ToServiceAccount(organizationId));
|
var result = await _createServiceAccountCommand.CreateAsync(createRequest.ToServiceAccount(organizationId), userId);
|
||||||
return new ServiceAccountResponseModel(result);
|
return new ServiceAccountResponseModel(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPut("{id}")]
|
[HttpPut("{id}")]
|
||||||
public async Task<ServiceAccountResponseModel> UpdateServiceAccountAsync([FromRoute] Guid id,
|
public async Task<ServiceAccountResponseModel> UpdateAsync([FromRoute] Guid id,
|
||||||
[FromBody] ServiceAccountUpdateRequestModel updateRequest)
|
[FromBody] ServiceAccountUpdateRequestModel updateRequest)
|
||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
@ -89,6 +96,11 @@ public class ServiceAccountsController : Controller
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_currentContext.AccessSecretsManager(serviceAccount.OrganizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
var orgAdmin = await _currentContext.OrganizationAdmin(serviceAccount.OrganizationId);
|
var orgAdmin = await _currentContext.OrganizationAdmin(serviceAccount.OrganizationId);
|
||||||
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||||
|
|
||||||
|
@ -10,11 +10,6 @@ public class ProjectAccessPoliciesResponseModel : ResponseModel
|
|||||||
public ProjectAccessPoliciesResponseModel(IEnumerable<BaseAccessPolicy> baseAccessPolicies)
|
public ProjectAccessPoliciesResponseModel(IEnumerable<BaseAccessPolicy> baseAccessPolicies)
|
||||||
: base(_objectName)
|
: base(_objectName)
|
||||||
{
|
{
|
||||||
if (baseAccessPolicies == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var baseAccessPolicy in baseAccessPolicies)
|
foreach (var baseAccessPolicy in baseAccessPolicies)
|
||||||
switch (baseAccessPolicy)
|
switch (baseAccessPolicy)
|
||||||
{
|
{
|
||||||
|
@ -33,6 +33,9 @@ public class Startup
|
|||||||
StripeConfiguration.ApiKey = globalSettings.Stripe.ApiKey;
|
StripeConfiguration.ApiKey = globalSettings.Stripe.ApiKey;
|
||||||
StripeConfiguration.MaxNetworkRetries = globalSettings.Stripe.MaxNetworkRetries;
|
StripeConfiguration.MaxNetworkRetries = globalSettings.Stripe.MaxNetworkRetries;
|
||||||
|
|
||||||
|
// Data Protection
|
||||||
|
services.AddCustomDataProtectionServices(Environment, globalSettings);
|
||||||
|
|
||||||
// Repositories
|
// Repositories
|
||||||
services.AddDatabaseRepositories(globalSettings);
|
services.AddDatabaseRepositories(globalSettings);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Core.Context;
|
namespace Bit.Core.Context;
|
||||||
@ -9,14 +9,16 @@ public class CurrentContentOrganization
|
|||||||
{
|
{
|
||||||
public CurrentContentOrganization() { }
|
public CurrentContentOrganization() { }
|
||||||
|
|
||||||
public CurrentContentOrganization(OrganizationUser orgUser)
|
public CurrentContentOrganization(OrganizationUserOrganizationDetails orgUser)
|
||||||
{
|
{
|
||||||
Id = orgUser.OrganizationId;
|
Id = orgUser.OrganizationId;
|
||||||
Type = orgUser.Type;
|
Type = orgUser.Type;
|
||||||
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(orgUser.Permissions);
|
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(orgUser.Permissions);
|
||||||
|
AccessSecretsManager = orgUser.AccessSecretsManager && orgUser.UseSecretsManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public OrganizationUserType Type { get; set; }
|
public OrganizationUserType Type { get; set; }
|
||||||
public Permissions Permissions { get; set; }
|
public Permissions Permissions { get; set; }
|
||||||
|
public bool AccessSecretsManager { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -157,6 +157,10 @@ public class CurrentContext : ICurrentContext
|
|||||||
|
|
||||||
private List<CurrentContentOrganization> GetOrganizations(Dictionary<string, IEnumerable<Claim>> claimsDict, bool orgApi)
|
private List<CurrentContentOrganization> GetOrganizations(Dictionary<string, IEnumerable<Claim>> claimsDict, bool orgApi)
|
||||||
{
|
{
|
||||||
|
var accessSecretsManager = claimsDict.ContainsKey(Claims.SecretsManagerAccess)
|
||||||
|
? claimsDict[Claims.SecretsManagerAccess].ToDictionary(s => s.Value, _ => true)
|
||||||
|
: new Dictionary<string, bool>();
|
||||||
|
|
||||||
var organizations = new List<CurrentContentOrganization>();
|
var organizations = new List<CurrentContentOrganization>();
|
||||||
if (claimsDict.ContainsKey(Claims.OrganizationOwner))
|
if (claimsDict.ContainsKey(Claims.OrganizationOwner))
|
||||||
{
|
{
|
||||||
@ -164,7 +168,8 @@ public class CurrentContext : ICurrentContext
|
|||||||
new CurrentContentOrganization
|
new CurrentContentOrganization
|
||||||
{
|
{
|
||||||
Id = new Guid(c.Value),
|
Id = new Guid(c.Value),
|
||||||
Type = OrganizationUserType.Owner
|
Type = OrganizationUserType.Owner,
|
||||||
|
AccessSecretsManager = accessSecretsManager.ContainsKey(c.Value),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
else if (orgApi && OrganizationId.HasValue)
|
else if (orgApi && OrganizationId.HasValue)
|
||||||
@ -172,7 +177,7 @@ public class CurrentContext : ICurrentContext
|
|||||||
organizations.Add(new CurrentContentOrganization
|
organizations.Add(new CurrentContentOrganization
|
||||||
{
|
{
|
||||||
Id = OrganizationId.Value,
|
Id = OrganizationId.Value,
|
||||||
Type = OrganizationUserType.Owner
|
Type = OrganizationUserType.Owner,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +187,8 @@ public class CurrentContext : ICurrentContext
|
|||||||
new CurrentContentOrganization
|
new CurrentContentOrganization
|
||||||
{
|
{
|
||||||
Id = new Guid(c.Value),
|
Id = new Guid(c.Value),
|
||||||
Type = OrganizationUserType.Admin
|
Type = OrganizationUserType.Admin,
|
||||||
|
AccessSecretsManager = accessSecretsManager.ContainsKey(c.Value),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +198,8 @@ public class CurrentContext : ICurrentContext
|
|||||||
new CurrentContentOrganization
|
new CurrentContentOrganization
|
||||||
{
|
{
|
||||||
Id = new Guid(c.Value),
|
Id = new Guid(c.Value),
|
||||||
Type = OrganizationUserType.User
|
Type = OrganizationUserType.User,
|
||||||
|
AccessSecretsManager = accessSecretsManager.ContainsKey(c.Value),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,7 +209,8 @@ public class CurrentContext : ICurrentContext
|
|||||||
new CurrentContentOrganization
|
new CurrentContentOrganization
|
||||||
{
|
{
|
||||||
Id = new Guid(c.Value),
|
Id = new Guid(c.Value),
|
||||||
Type = OrganizationUserType.Manager
|
Type = OrganizationUserType.Manager,
|
||||||
|
AccessSecretsManager = accessSecretsManager.ContainsKey(c.Value),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,7 +221,8 @@ public class CurrentContext : ICurrentContext
|
|||||||
{
|
{
|
||||||
Id = new Guid(c.Value),
|
Id = new Guid(c.Value),
|
||||||
Type = OrganizationUserType.Custom,
|
Type = OrganizationUserType.Custom,
|
||||||
Permissions = SetOrganizationPermissionsFromClaims(c.Value, claimsDict)
|
Permissions = SetOrganizationPermissionsFromClaims(c.Value, claimsDict),
|
||||||
|
AccessSecretsManager = accessSecretsManager.ContainsKey(c.Value),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,12 +443,17 @@ public class CurrentContext : ICurrentContext
|
|||||||
return po?.ProviderId;
|
return po?.ProviderId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool AccessSecretsManager(Guid orgId)
|
||||||
|
{
|
||||||
|
return Organizations?.Any(o => o.Id == orgId && o.AccessSecretsManager) ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<ICollection<CurrentContentOrganization>> OrganizationMembershipAsync(
|
public async Task<ICollection<CurrentContentOrganization>> OrganizationMembershipAsync(
|
||||||
IOrganizationUserRepository organizationUserRepository, Guid userId)
|
IOrganizationUserRepository organizationUserRepository, Guid userId)
|
||||||
{
|
{
|
||||||
if (Organizations == null)
|
if (Organizations == null)
|
||||||
{
|
{
|
||||||
var userOrgs = await organizationUserRepository.GetManyByUserAsync(userId);
|
var userOrgs = await organizationUserRepository.GetManyDetailsByUserAsync(userId);
|
||||||
Organizations = userOrgs.Where(ou => ou.Status == OrganizationUserStatusType.Confirmed)
|
Organizations = userOrgs.Where(ou => ou.Status == OrganizationUserStatusType.Confirmed)
|
||||||
.Select(ou => new CurrentContentOrganization(ou)).ToList();
|
.Select(ou => new CurrentContentOrganization(ou)).ToList();
|
||||||
}
|
}
|
||||||
|
@ -68,4 +68,5 @@ public interface ICurrentContext
|
|||||||
IProviderUserRepository providerUserRepository, Guid userId);
|
IProviderUserRepository providerUserRepository, Guid userId);
|
||||||
|
|
||||||
Task<Guid?> ProviderIdForOrg(Guid orgId);
|
Task<Guid?> ProviderIdForOrg(Guid orgId);
|
||||||
|
bool AccessSecretsManager(Guid organizationId);
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.OrganizationConnectionConfigs;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Core.Entities;
|
namespace Bit.Core.Entities;
|
||||||
|
|
||||||
public class OrganizationConnection<T> : OrganizationConnection where T : new()
|
public class OrganizationConnection<T> : OrganizationConnection where T : IConnectionConfig
|
||||||
{
|
{
|
||||||
public new T Config
|
public new T Config
|
||||||
{
|
{
|
||||||
get => base.GetConfig<T>();
|
get => base.GetConfig<T>();
|
||||||
set => base.SetConfig<T>(value);
|
set => base.SetConfig(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ public class OrganizationConnection : ITableObject<Guid>
|
|||||||
Id = CoreHelpers.GenerateComb();
|
Id = CoreHelpers.GenerateComb();
|
||||||
}
|
}
|
||||||
|
|
||||||
public T GetConfig<T>() where T : new()
|
public T GetConfig<T>() where T : IConnectionConfig
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -38,8 +39,32 @@ public class OrganizationConnection : ITableObject<Guid>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetConfig<T>(T config) where T : new()
|
public void SetConfig<T>(T config) where T : IConnectionConfig
|
||||||
{
|
{
|
||||||
Config = JsonSerializer.Serialize(config);
|
Config = JsonSerializer.Serialize(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Validate<T>(out string exception) where T : IConnectionConfig
|
||||||
|
{
|
||||||
|
if (!Enabled)
|
||||||
|
{
|
||||||
|
exception = $"Connection disabled for organization {OrganizationId}";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(Config))
|
||||||
|
{
|
||||||
|
exception = $"No saved Connection config for organization {OrganizationId}";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = GetConfig<T>();
|
||||||
|
if (config == null)
|
||||||
|
{
|
||||||
|
exception = $"Error parsing Connection config for organization {OrganizationId}";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config.Validate(out exception);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ public class OrganizationUser : ITableObject<Guid>, IExternal
|
|||||||
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
||||||
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;
|
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;
|
||||||
public string Permissions { get; set; }
|
public string Permissions { get; set; }
|
||||||
|
public bool AccessSecretsManager { get; set; }
|
||||||
|
|
||||||
public void SetNewId()
|
public void SetNewId()
|
||||||
{
|
{
|
||||||
|
@ -65,6 +65,10 @@ public class User : ITableObject<Guid>, ISubscriber, IStorable, IStorableSubscri
|
|||||||
public bool UnknownDeviceVerificationEnabled { get; set; }
|
public bool UnknownDeviceVerificationEnabled { get; set; }
|
||||||
[MaxLength(7)]
|
[MaxLength(7)]
|
||||||
public string AvatarColor { get; set; }
|
public string AvatarColor { get; set; }
|
||||||
|
public DateTime? LastPasswordChangeDate { get; set; }
|
||||||
|
public DateTime? LastKdfChangeDate { get; set; }
|
||||||
|
public DateTime? LastKeyRotationDate { get; set; }
|
||||||
|
public DateTime? LastEmailChangeDate { get; set; }
|
||||||
|
|
||||||
public void SetNewId()
|
public void SetNewId()
|
||||||
{
|
{
|
||||||
|
@ -6,6 +6,7 @@ public static class Claims
|
|||||||
public const string SecurityStamp = "sstamp";
|
public const string SecurityStamp = "sstamp";
|
||||||
public const string Premium = "premium";
|
public const string Premium = "premium";
|
||||||
public const string Device = "device";
|
public const string Device = "device";
|
||||||
|
|
||||||
public const string OrganizationOwner = "orgowner";
|
public const string OrganizationOwner = "orgowner";
|
||||||
public const string OrganizationAdmin = "orgadmin";
|
public const string OrganizationAdmin = "orgadmin";
|
||||||
public const string OrganizationManager = "orgmanager";
|
public const string OrganizationManager = "orgmanager";
|
||||||
@ -14,6 +15,8 @@ public static class Claims
|
|||||||
public const string ProviderAdmin = "providerprovideradmin";
|
public const string ProviderAdmin = "providerprovideradmin";
|
||||||
public const string ProviderServiceUser = "providerserviceuser";
|
public const string ProviderServiceUser = "providerserviceuser";
|
||||||
|
|
||||||
|
public const string SecretsManagerAccess = "accesssecretsmanager";
|
||||||
|
|
||||||
// Service Account
|
// Service Account
|
||||||
public const string Organization = "organization";
|
public const string Organization = "organization";
|
||||||
|
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace Bit.Core.Models.Api.OrganizationLicenses;
|
||||||
|
|
||||||
|
public class SelfHostedOrganizationLicenseRequestModel
|
||||||
|
{
|
||||||
|
public string LicenseKey { get; set; }
|
||||||
|
public string BillingSyncKey { get; set; }
|
||||||
|
}
|
@ -8,6 +8,7 @@ public class OrganizationUserInvite
|
|||||||
public IEnumerable<string> Emails { get; set; }
|
public IEnumerable<string> Emails { get; set; }
|
||||||
public Enums.OrganizationUserType? Type { get; set; }
|
public Enums.OrganizationUserType? Type { get; set; }
|
||||||
public bool AccessAll { get; set; }
|
public bool AccessAll { get; set; }
|
||||||
|
public bool AccessSecretsManager { get; set; }
|
||||||
public Permissions Permissions { get; set; }
|
public Permissions Permissions { get; set; }
|
||||||
public IEnumerable<CollectionAccessSelection> Collections { get; set; }
|
public IEnumerable<CollectionAccessSelection> Collections { get; set; }
|
||||||
public IEnumerable<Guid> Groups { get; set; }
|
public IEnumerable<Guid> Groups { get; set; }
|
||||||
@ -19,6 +20,7 @@ public class OrganizationUserInvite
|
|||||||
Emails = requestModel.Emails;
|
Emails = requestModel.Emails;
|
||||||
Type = requestModel.Type;
|
Type = requestModel.Type;
|
||||||
AccessAll = requestModel.AccessAll;
|
AccessAll = requestModel.AccessAll;
|
||||||
|
AccessSecretsManager = requestModel.AccessSecretsManager;
|
||||||
Collections = requestModel.Collections;
|
Collections = requestModel.Collections;
|
||||||
Groups = requestModel.Groups;
|
Groups = requestModel.Groups;
|
||||||
Permissions = requestModel.Permissions;
|
Permissions = requestModel.Permissions;
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.OrganizationConnectionConfigs;
|
||||||
|
|
||||||
namespace Bit.Core.Models.Data.Organizations.OrganizationConnections;
|
namespace Bit.Core.Models.Data.Organizations.OrganizationConnections;
|
||||||
|
|
||||||
public class OrganizationConnectionData<T> where T : new()
|
public class OrganizationConnectionData<T> where T : IConnectionConfig
|
||||||
{
|
{
|
||||||
public Guid? Id { get; set; }
|
public Guid? Id { get; set; }
|
||||||
public OrganizationConnectionType Type { get; set; }
|
public OrganizationConnectionType Type { get; set; }
|
||||||
|
@ -7,6 +7,7 @@ public class OrganizationUserInviteData
|
|||||||
public IEnumerable<string> Emails { get; set; }
|
public IEnumerable<string> Emails { get; set; }
|
||||||
public OrganizationUserType? Type { get; set; }
|
public OrganizationUserType? Type { get; set; }
|
||||||
public bool AccessAll { get; set; }
|
public bool AccessAll { get; set; }
|
||||||
|
public bool AccessSecretsManager { get; set; }
|
||||||
public IEnumerable<CollectionAccessSelection> Collections { get; set; }
|
public IEnumerable<CollectionAccessSelection> Collections { get; set; }
|
||||||
public IEnumerable<Guid> Groups { get; set; }
|
public IEnumerable<Guid> Groups { get; set; }
|
||||||
public Permissions Permissions { get; set; }
|
public Permissions Permissions { get; set; }
|
||||||
|
@ -41,4 +41,5 @@ public class OrganizationUserOrganizationDetails
|
|||||||
public DateTime? FamilySponsorshipLastSyncDate { get; set; }
|
public DateTime? FamilySponsorshipLastSyncDate { get; set; }
|
||||||
public DateTime? FamilySponsorshipValidUntil { get; set; }
|
public DateTime? FamilySponsorshipValidUntil { get; set; }
|
||||||
public bool? FamilySponsorshipToDelete { get; set; }
|
public bool? FamilySponsorshipToDelete { get; set; }
|
||||||
|
public bool AccessSecretsManager { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ public class OrganizationUserUserDetails : IExternal, ITwoFactorProvidersUser
|
|||||||
public OrganizationUserStatusType Status { get; set; }
|
public OrganizationUserStatusType Status { get; set; }
|
||||||
public OrganizationUserType Type { get; set; }
|
public OrganizationUserType Type { get; set; }
|
||||||
public bool AccessAll { get; set; }
|
public bool AccessAll { get; set; }
|
||||||
|
public bool AccessSecretsManager { get; set; }
|
||||||
public string ExternalId { get; set; }
|
public string ExternalId { get; set; }
|
||||||
public string SsoExternalId { get; set; }
|
public string SsoExternalId { get; set; }
|
||||||
public string Permissions { get; set; }
|
public string Permissions { get; set; }
|
||||||
|
@ -34,4 +34,5 @@ public class ProviderUserOrganizationDetails
|
|||||||
public Guid? ProviderId { get; set; }
|
public Guid? ProviderId { get; set; }
|
||||||
public Guid? ProviderUserId { get; set; }
|
public Guid? ProviderUserId { get; set; }
|
||||||
public string ProviderName { get; set; }
|
public string ProviderName { get; set; }
|
||||||
|
public Enums.PlanType PlanType { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,20 @@
|
|||||||
namespace Bit.Core.Models.OrganizationConnectionConfigs;
|
namespace Bit.Core.Models.OrganizationConnectionConfigs;
|
||||||
|
|
||||||
public class BillingSyncConfig
|
public class BillingSyncConfig : IConnectionConfig
|
||||||
{
|
{
|
||||||
public string BillingSyncKey { get; set; }
|
public string BillingSyncKey { get; set; }
|
||||||
public Guid CloudOrganizationId { get; set; }
|
public Guid CloudOrganizationId { get; set; }
|
||||||
|
public DateTime? LastLicenseSync { get; set; }
|
||||||
|
|
||||||
|
public bool Validate(out string exception)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(BillingSyncKey))
|
||||||
|
{
|
||||||
|
exception = "Failed to get Billing Sync Key";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
exception = "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace Bit.Core.Models.OrganizationConnectionConfigs;
|
||||||
|
|
||||||
|
public interface IConnectionConfig
|
||||||
|
{
|
||||||
|
bool Validate(out string exception);
|
||||||
|
}
|
@ -3,9 +3,21 @@ using Bit.Core.Enums;
|
|||||||
|
|
||||||
namespace Bit.Core.Models.OrganizationConnectionConfigs;
|
namespace Bit.Core.Models.OrganizationConnectionConfigs;
|
||||||
|
|
||||||
public class ScimConfig
|
public class ScimConfig : IConnectionConfig
|
||||||
{
|
{
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
public ScimProviderType? ScimProvider { get; set; }
|
public ScimProviderType? ScimProvider { get; set; }
|
||||||
|
|
||||||
|
public bool Validate(out string exception)
|
||||||
|
{
|
||||||
|
if (!Enabled)
|
||||||
|
{
|
||||||
|
exception = "Scim Config is disabled";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
exception = "";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationConnections;
|
using Bit.Core.Models.Data.Organizations.OrganizationConnections;
|
||||||
|
using Bit.Core.Models.OrganizationConnectionConfigs;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces;
|
using Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
|
||||||
@ -14,7 +15,7 @@ public class CreateOrganizationConnectionCommand : ICreateOrganizationConnection
|
|||||||
_organizationConnectionRepository = organizationConnectionRepository;
|
_organizationConnectionRepository = organizationConnectionRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<OrganizationConnection> CreateAsync<T>(OrganizationConnectionData<T> connectionData) where T : new()
|
public async Task<OrganizationConnection> CreateAsync<T>(OrganizationConnectionData<T> connectionData) where T : IConnectionConfig
|
||||||
{
|
{
|
||||||
return await _organizationConnectionRepository.CreateAsync(connectionData.ToEntity());
|
return await _organizationConnectionRepository.CreateAsync(connectionData.ToEntity());
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationConnections;
|
using Bit.Core.Models.Data.Organizations.OrganizationConnections;
|
||||||
|
using Bit.Core.Models.OrganizationConnectionConfigs;
|
||||||
|
|
||||||
namespace Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces;
|
namespace Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces;
|
||||||
|
|
||||||
public interface ICreateOrganizationConnectionCommand
|
public interface ICreateOrganizationConnectionCommand
|
||||||
{
|
{
|
||||||
Task<OrganizationConnection> CreateAsync<T>(OrganizationConnectionData<T> connectionData) where T : new();
|
Task<OrganizationConnection> CreateAsync<T>(OrganizationConnectionData<T> connectionData) where T : IConnectionConfig;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationConnections;
|
using Bit.Core.Models.Data.Organizations.OrganizationConnections;
|
||||||
|
using Bit.Core.Models.OrganizationConnectionConfigs;
|
||||||
|
|
||||||
namespace Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces;
|
namespace Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces;
|
||||||
|
|
||||||
public interface IUpdateOrganizationConnectionCommand
|
public interface IUpdateOrganizationConnectionCommand
|
||||||
{
|
{
|
||||||
Task<OrganizationConnection> UpdateAsync<T>(OrganizationConnectionData<T> connectionData) where T : new();
|
Task<OrganizationConnection> UpdateAsync<T>(OrganizationConnectionData<T> connectionData) where T : IConnectionConfig;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
|
|
||||||
namespace Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
|
namespace Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces;
|
||||||
|
|
||||||
public interface IValidateBillingSyncKeyCommand
|
public interface IValidateBillingSyncKeyCommand
|
||||||
{
|
{
|
@ -1,6 +1,7 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationConnections;
|
using Bit.Core.Models.Data.Organizations.OrganizationConnections;
|
||||||
|
using Bit.Core.Models.OrganizationConnectionConfigs;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces;
|
using Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
|
||||||
@ -15,7 +16,7 @@ public class UpdateOrganizationConnectionCommand : IUpdateOrganizationConnection
|
|||||||
_organizationConnectionRepository = organizationConnectionRepository;
|
_organizationConnectionRepository = organizationConnectionRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<OrganizationConnection> UpdateAsync<T>(OrganizationConnectionData<T> connectionData) where T : new()
|
public async Task<OrganizationConnection> UpdateAsync<T>(OrganizationConnectionData<T> connectionData) where T : IConnectionConfig
|
||||||
{
|
{
|
||||||
if (!connectionData.Id.HasValue)
|
if (!connectionData.Id.HasValue)
|
||||||
{
|
{
|
||||||
|
@ -1,20 +1,17 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
|
using Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
|
||||||
namespace Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
|
namespace Bit.Core.OrganizationFeatures.OrganizationConnections;
|
||||||
|
|
||||||
public class ValidateBillingSyncKeyCommand : IValidateBillingSyncKeyCommand
|
public class ValidateBillingSyncKeyCommand : IValidateBillingSyncKeyCommand
|
||||||
{
|
{
|
||||||
private readonly IOrganizationSponsorshipRepository _organizationSponsorshipRepository;
|
|
||||||
private readonly IOrganizationApiKeyRepository _apiKeyRepository;
|
private readonly IOrganizationApiKeyRepository _apiKeyRepository;
|
||||||
|
|
||||||
public ValidateBillingSyncKeyCommand(
|
public ValidateBillingSyncKeyCommand(
|
||||||
IOrganizationSponsorshipRepository organizationSponsorshipRepository,
|
|
||||||
IOrganizationApiKeyRepository organizationApiKeyRepository)
|
IOrganizationApiKeyRepository organizationApiKeyRepository)
|
||||||
{
|
{
|
||||||
_organizationSponsorshipRepository = organizationSponsorshipRepository;
|
|
||||||
_apiKeyRepository = organizationApiKeyRepository;
|
_apiKeyRepository = organizationApiKeyRepository;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Business;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
|
||||||
|
namespace Bit.Core.OrganizationFeatures.OrganizationLicenses;
|
||||||
|
|
||||||
|
public class CloudGetOrganizationLicenseQuery : ICloudGetOrganizationLicenseQuery
|
||||||
|
{
|
||||||
|
private readonly IInstallationRepository _installationRepository;
|
||||||
|
private readonly IPaymentService _paymentService;
|
||||||
|
private readonly ILicensingService _licensingService;
|
||||||
|
|
||||||
|
public CloudGetOrganizationLicenseQuery(
|
||||||
|
IInstallationRepository installationRepository,
|
||||||
|
IPaymentService paymentService,
|
||||||
|
ILicensingService licensingService)
|
||||||
|
{
|
||||||
|
_installationRepository = installationRepository;
|
||||||
|
_paymentService = paymentService;
|
||||||
|
_licensingService = licensingService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<OrganizationLicense> GetLicenseAsync(Organization organization, Guid installationId,
|
||||||
|
int? version = null)
|
||||||
|
{
|
||||||
|
var installation = await _installationRepository.GetByIdAsync(installationId);
|
||||||
|
if (installation is not { Enabled: true })
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Invalid installation id");
|
||||||
|
}
|
||||||
|
|
||||||
|
var subInfo = await _paymentService.GetSubscriptionAsync(organization);
|
||||||
|
return new OrganizationLicense(organization, subInfo, installationId, _licensingService, version);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Models.Business;
|
||||||
|
|
||||||
|
namespace Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces;
|
||||||
|
|
||||||
|
public interface ICloudGetOrganizationLicenseQuery
|
||||||
|
{
|
||||||
|
Task<OrganizationLicense> GetLicenseAsync(Organization organization, Guid installationId,
|
||||||
|
int? version = null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ISelfHostedGetOrganizationLicenseQuery
|
||||||
|
{
|
||||||
|
Task<OrganizationLicense> GetLicenseAsync(Organization organization, OrganizationConnection billingSyncConnection);
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Api.OrganizationLicenses;
|
||||||
|
using Bit.Core.Models.Business;
|
||||||
|
using Bit.Core.Models.OrganizationConnectionConfigs;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Settings;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Bit.Core.OrganizationFeatures.OrganizationLicenses;
|
||||||
|
|
||||||
|
public class SelfHostedGetOrganizationLicenseQuery : BaseIdentityClientService, ISelfHostedGetOrganizationLicenseQuery
|
||||||
|
{
|
||||||
|
private readonly IGlobalSettings _globalSettings;
|
||||||
|
|
||||||
|
public SelfHostedGetOrganizationLicenseQuery(IHttpClientFactory httpFactory, IGlobalSettings globalSettings, ILogger<SelfHostedGetOrganizationLicenseQuery> logger, ICurrentContext currentContext)
|
||||||
|
: base(
|
||||||
|
httpFactory,
|
||||||
|
globalSettings.Installation.ApiUri,
|
||||||
|
globalSettings.Installation.IdentityUri,
|
||||||
|
"api.licensing",
|
||||||
|
$"installation.{globalSettings.Installation.Id}",
|
||||||
|
globalSettings.Installation.Key,
|
||||||
|
logger)
|
||||||
|
{
|
||||||
|
_globalSettings = globalSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<OrganizationLicense> GetLicenseAsync(Organization organization, OrganizationConnection billingSyncConnection)
|
||||||
|
{
|
||||||
|
if (!_globalSettings.SelfHosted)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("This action is only available for self-hosted.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_globalSettings.EnableCloudCommunication)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Cloud communication is disabled in global settings");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!billingSyncConnection.Validate<BillingSyncConfig>(out var exception))
|
||||||
|
{
|
||||||
|
throw new BadRequestException(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
var billingSyncConfig = billingSyncConnection.GetConfig<BillingSyncConfig>();
|
||||||
|
var cloudOrganizationId = billingSyncConfig.CloudOrganizationId;
|
||||||
|
|
||||||
|
var response = await SendAsync<SelfHostedOrganizationLicenseRequestModel, OrganizationLicense>(
|
||||||
|
HttpMethod.Get, $"licenses/organization/{cloudOrganizationId}", new SelfHostedOrganizationLicenseRequestModel()
|
||||||
|
{
|
||||||
|
BillingSyncKey = billingSyncConfig.BillingSyncKey,
|
||||||
|
LicenseKey = organization.LicenseKey,
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
if (response == null)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("Organization License sync failed for '{OrgId}'", organization.Id);
|
||||||
|
throw new BadRequestException("An error has occurred. Check your internet connection and ensure the billing token is correct.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,8 @@ using Bit.Core.OrganizationFeatures.OrganizationCollections;
|
|||||||
using Bit.Core.OrganizationFeatures.OrganizationCollections.Interfaces;
|
using Bit.Core.OrganizationFeatures.OrganizationCollections.Interfaces;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationConnections;
|
using Bit.Core.OrganizationFeatures.OrganizationConnections;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces;
|
using Bit.Core.OrganizationFeatures.OrganizationConnections.Interfaces;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationLicenses;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationLicenses.Interfaces;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
|
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
|
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
|
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
|
||||||
@ -32,6 +34,7 @@ public static class OrganizationServiceCollectionExtensions
|
|||||||
services.AddOrganizationApiKeyCommandsQueries();
|
services.AddOrganizationApiKeyCommandsQueries();
|
||||||
services.AddOrganizationCollectionCommands();
|
services.AddOrganizationCollectionCommands();
|
||||||
services.AddOrganizationGroupCommands();
|
services.AddOrganizationGroupCommands();
|
||||||
|
services.AddOrganizationLicenseCommandQueries();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddOrganizationConnectionCommands(this IServiceCollection services)
|
private static void AddOrganizationConnectionCommands(this IServiceCollection services)
|
||||||
@ -85,6 +88,12 @@ public static class OrganizationServiceCollectionExtensions
|
|||||||
services.AddScoped<IUpdateGroupCommand, UpdateGroupCommand>();
|
services.AddScoped<IUpdateGroupCommand, UpdateGroupCommand>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void AddOrganizationLicenseCommandQueries(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddScoped<ICloudGetOrganizationLicenseQuery, CloudGetOrganizationLicenseQuery>();
|
||||||
|
services.AddScoped<ISelfHostedGetOrganizationLicenseQuery, SelfHostedGetOrganizationLicenseQuery>();
|
||||||
|
}
|
||||||
|
|
||||||
private static void AddTokenizers(this IServiceCollection services)
|
private static void AddTokenizers(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddSingleton<IDataProtectorTokenFactory<OrganizationSponsorshipOfferTokenable>>(serviceProvider =>
|
services.AddSingleton<IDataProtectorTokenFactory<OrganizationSponsorshipOfferTokenable>>(serviceProvider =>
|
||||||
|
@ -48,20 +48,13 @@ public class SelfHostedSyncSponsorshipsCommand : BaseIdentityClientService, ISel
|
|||||||
{
|
{
|
||||||
throw new BadRequestException("Failed to sync instance with cloud - Cloud communication is disabled in global settings");
|
throw new BadRequestException("Failed to sync instance with cloud - Cloud communication is disabled in global settings");
|
||||||
}
|
}
|
||||||
if (!billingSyncConnection.Enabled)
|
|
||||||
|
if (!billingSyncConnection.Validate<BillingSyncConfig>(out var exception))
|
||||||
{
|
{
|
||||||
throw new BadRequestException($"Billing Sync Key disabled for organization {organizationId}");
|
throw new BadRequestException(exception);
|
||||||
}
|
|
||||||
if (string.IsNullOrWhiteSpace(billingSyncConnection.Config))
|
|
||||||
{
|
|
||||||
throw new BadRequestException($"No Billing Sync Key known for organization {organizationId}");
|
|
||||||
}
|
|
||||||
var billingSyncConfig = billingSyncConnection.GetConfig<BillingSyncConfig>();
|
|
||||||
if (billingSyncConfig == null || string.IsNullOrWhiteSpace(billingSyncConfig.BillingSyncKey))
|
|
||||||
{
|
|
||||||
throw new BadRequestException($"Failed to get Billing Sync Key for organization {organizationId}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var billingSyncConfig = billingSyncConnection.GetConfig<BillingSyncConfig>();
|
||||||
var organizationSponsorshipsDict = (await _organizationSponsorshipRepository.GetManyBySponsoringOrganizationAsync(organizationId))
|
var organizationSponsorshipsDict = (await _organizationSponsorshipRepository.GetManyBySponsoringOrganizationAsync(organizationId))
|
||||||
.ToDictionary(i => i.SponsoringOrganizationUserId);
|
.ToDictionary(i => i.SponsoringOrganizationUserId);
|
||||||
if (!organizationSponsorshipsDict.Any())
|
if (!organizationSponsorshipsDict.Any())
|
||||||
|
@ -11,7 +11,7 @@ public interface IProviderUserRepository : IRepository<ProviderUser, Guid>
|
|||||||
Task<ICollection<ProviderUser>> GetManyByUserAsync(Guid userId);
|
Task<ICollection<ProviderUser>> GetManyByUserAsync(Guid userId);
|
||||||
Task<ProviderUser> GetByProviderUserAsync(Guid providerId, Guid userId);
|
Task<ProviderUser> GetByProviderUserAsync(Guid providerId, Guid userId);
|
||||||
Task<ICollection<ProviderUser>> GetManyByProviderAsync(Guid providerId, ProviderUserType? type = null);
|
Task<ICollection<ProviderUser>> GetManyByProviderAsync(Guid providerId, ProviderUserType? type = null);
|
||||||
Task<ICollection<ProviderUserUserDetails>> GetManyDetailsByProviderAsync(Guid providerId);
|
Task<ICollection<ProviderUserUserDetails>> GetManyDetailsByProviderAsync(Guid providerId, ProviderUserStatusType? status = null);
|
||||||
Task<ICollection<ProviderUserProviderDetails>> GetManyDetailsByUserAsync(Guid userId,
|
Task<ICollection<ProviderUserProviderDetails>> GetManyDetailsByUserAsync(Guid userId,
|
||||||
ProviderUserStatusType? status = null);
|
ProviderUserStatusType? status = null);
|
||||||
Task<IEnumerable<ProviderUserOrganizationDetails>> GetManyOrganizationDetailsByUserAsync(Guid userId, ProviderUserStatusType? status = null);
|
Task<IEnumerable<ProviderUserOrganizationDetails>> GetManyOrganizationDetailsByUserAsync(Guid userId, ProviderUserStatusType? status = null);
|
||||||
|
@ -4,5 +4,5 @@ namespace Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
|
|||||||
|
|
||||||
public interface ICreateServiceAccountCommand
|
public interface ICreateServiceAccountCommand
|
||||||
{
|
{
|
||||||
Task<ServiceAccount> CreateAsync(ServiceAccount serviceAccount);
|
Task<ServiceAccount> CreateAsync(ServiceAccount serviceAccount, Guid userId);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,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>?> GetManyByProjectId(Guid id);
|
Task<IEnumerable<BaseAccessPolicy>> GetManyByGrantedProjectIdAsync(Guid id);
|
||||||
|
Task<IEnumerable<BaseAccessPolicy>> GetManyByGrantedServiceAccountIdAsync(Guid id);
|
||||||
Task ReplaceAsync(BaseAccessPolicy baseAccessPolicy);
|
Task ReplaceAsync(BaseAccessPolicy baseAccessPolicy);
|
||||||
Task DeleteAsync(Guid id);
|
Task DeleteAsync(Guid id);
|
||||||
}
|
}
|
||||||
|
@ -56,9 +56,6 @@ public interface IOrganizationService
|
|||||||
IEnumerable<Guid> organizationUserIds, Guid? deletingUserId);
|
IEnumerable<Guid> organizationUserIds, Guid? deletingUserId);
|
||||||
Task UpdateUserGroupsAsync(OrganizationUser organizationUser, IEnumerable<Guid> groupIds, Guid? loggedInUserId);
|
Task UpdateUserGroupsAsync(OrganizationUser organizationUser, IEnumerable<Guid> groupIds, Guid? loggedInUserId);
|
||||||
Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId);
|
Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId);
|
||||||
Task<OrganizationLicense> GenerateLicenseAsync(Guid organizationId, Guid installationId);
|
|
||||||
Task<OrganizationLicense> GenerateLicenseAsync(Organization organization, Guid installationId,
|
|
||||||
int? version = null);
|
|
||||||
Task ImportAsync(Guid organizationId, Guid? importingUserId, IEnumerable<ImportedGroup> groups,
|
Task ImportAsync(Guid organizationId, Guid? importingUserId, IEnumerable<ImportedGroup> groups,
|
||||||
IEnumerable<ImportedOrganizationUser> newUsers, IEnumerable<string> removeUserExternalIds,
|
IEnumerable<ImportedOrganizationUser> newUsers, IEnumerable<string> removeUserExternalIds,
|
||||||
bool overwriteExisting);
|
bool overwriteExisting);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Enums.Provider;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
@ -43,6 +44,8 @@ public class OrganizationService : IOrganizationService
|
|||||||
private readonly IOrganizationConnectionRepository _organizationConnectionRepository;
|
private readonly IOrganizationConnectionRepository _organizationConnectionRepository;
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly ILogger<OrganizationService> _logger;
|
private readonly ILogger<OrganizationService> _logger;
|
||||||
|
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
|
||||||
|
private readonly IProviderUserRepository _providerUserRepository;
|
||||||
|
|
||||||
public OrganizationService(
|
public OrganizationService(
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
@ -69,7 +72,9 @@ public class OrganizationService : IOrganizationService
|
|||||||
IOrganizationApiKeyRepository organizationApiKeyRepository,
|
IOrganizationApiKeyRepository organizationApiKeyRepository,
|
||||||
IOrganizationConnectionRepository organizationConnectionRepository,
|
IOrganizationConnectionRepository organizationConnectionRepository,
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
ILogger<OrganizationService> logger)
|
ILogger<OrganizationService> logger,
|
||||||
|
IProviderOrganizationRepository providerOrganizationRepository,
|
||||||
|
IProviderUserRepository providerUserRepository)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
@ -96,6 +101,8 @@ public class OrganizationService : IOrganizationService
|
|||||||
_organizationConnectionRepository = organizationConnectionRepository;
|
_organizationConnectionRepository = organizationConnectionRepository;
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_providerOrganizationRepository = providerOrganizationRepository;
|
||||||
|
_providerUserRepository = providerUserRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken,
|
public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken,
|
||||||
@ -1236,6 +1243,7 @@ public class OrganizationService : IOrganizationService
|
|||||||
Type = invite.Type.Value,
|
Type = invite.Type.Value,
|
||||||
Status = OrganizationUserStatusType.Invited,
|
Status = OrganizationUserStatusType.Invited,
|
||||||
AccessAll = invite.AccessAll,
|
AccessAll = invite.AccessAll,
|
||||||
|
AccessSecretsManager = invite.AccessSecretsManager,
|
||||||
ExternalId = externalId,
|
ExternalId = externalId,
|
||||||
CreationDate = DateTime.UtcNow,
|
CreationDate = DateTime.UtcNow,
|
||||||
RevisionDate = DateTime.UtcNow,
|
RevisionDate = DateTime.UtcNow,
|
||||||
@ -1637,8 +1645,19 @@ public class OrganizationService : IOrganizationService
|
|||||||
throw new BadRequestException(failureMessage);
|
throw new BadRequestException(failureMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
var ownerEmails = (await _organizationUserRepository.GetManyByMinimumRoleAsync(organization.Id,
|
var providerOrg = await this._providerOrganizationRepository.GetByOrganizationId(organization.Id);
|
||||||
|
|
||||||
|
IEnumerable<string> ownerEmails;
|
||||||
|
if (providerOrg != null)
|
||||||
|
{
|
||||||
|
ownerEmails = (await _providerUserRepository.GetManyDetailsByProviderAsync(providerOrg.ProviderId, ProviderUserStatusType.Confirmed))
|
||||||
|
.Select(u => u.Email).Distinct();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ownerEmails = (await _organizationUserRepository.GetManyByMinimumRoleAsync(organization.Id,
|
||||||
OrganizationUserType.Owner)).Select(u => u.Email).Distinct();
|
OrganizationUserType.Owner)).Select(u => u.Email).Distinct();
|
||||||
|
}
|
||||||
var initialSeatCount = organization.Seats.Value;
|
var initialSeatCount = organization.Seats.Value;
|
||||||
|
|
||||||
await AdjustSeatsAsync(organization, seatsToAdd, prorationDate, ownerEmails);
|
await AdjustSeatsAsync(organization, seatsToAdd, prorationDate, ownerEmails);
|
||||||
@ -1910,30 +1929,6 @@ public class OrganizationService : IOrganizationService
|
|||||||
EventType.OrganizationUser_ResetPassword_Enroll : EventType.OrganizationUser_ResetPassword_Withdraw);
|
EventType.OrganizationUser_ResetPassword_Enroll : EventType.OrganizationUser_ResetPassword_Withdraw);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<OrganizationLicense> GenerateLicenseAsync(Guid organizationId, Guid installationId)
|
|
||||||
{
|
|
||||||
var organization = await GetOrgById(organizationId);
|
|
||||||
return await GenerateLicenseAsync(organization, installationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<OrganizationLicense> GenerateLicenseAsync(Organization organization, Guid installationId,
|
|
||||||
int? version = null)
|
|
||||||
{
|
|
||||||
if (organization == null)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var installation = await _installationRepository.GetByIdAsync(installationId);
|
|
||||||
if (installation == null || !installation.Enabled)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Invalid installation id");
|
|
||||||
}
|
|
||||||
|
|
||||||
var subInfo = await _paymentService.GetSubscriptionAsync(organization);
|
|
||||||
return new OrganizationLicense(organization, subInfo, installationId, _licensingService, version);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<OrganizationUser> InviteUserAsync(Guid organizationId, Guid? invitingUserId, string email,
|
public async Task<OrganizationUser> InviteUserAsync(Guid organizationId, Guid? invitingUserId, string email,
|
||||||
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<CollectionAccessSelection> collections,
|
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<CollectionAccessSelection> collections,
|
||||||
IEnumerable<Guid> groups)
|
IEnumerable<Guid> groups)
|
||||||
|
@ -561,10 +561,13 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
user.Key = key;
|
user.Key = key;
|
||||||
user.Email = newEmail;
|
user.Email = newEmail;
|
||||||
user.EmailVerified = true;
|
user.EmailVerified = true;
|
||||||
user.RevisionDate = user.AccountRevisionDate = DateTime.UtcNow;
|
user.RevisionDate = user.AccountRevisionDate = now;
|
||||||
|
user.LastEmailChangeDate = now;
|
||||||
await _userRepository.ReplaceAsync(user);
|
await _userRepository.ReplaceAsync(user);
|
||||||
|
|
||||||
if (user.Gateway == GatewayType.Stripe)
|
if (user.Gateway == GatewayType.Stripe)
|
||||||
@ -618,7 +621,9 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
user.RevisionDate = user.AccountRevisionDate = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
|
user.RevisionDate = user.AccountRevisionDate = now;
|
||||||
|
user.LastPasswordChangeDate = now;
|
||||||
user.Key = key;
|
user.Key = key;
|
||||||
user.MasterPasswordHint = passwordHint;
|
user.MasterPasswordHint = passwordHint;
|
||||||
|
|
||||||
@ -845,7 +850,9 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
user.RevisionDate = user.AccountRevisionDate = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
|
user.RevisionDate = user.AccountRevisionDate = now;
|
||||||
|
user.LastKdfChangeDate = now;
|
||||||
user.Key = key;
|
user.Key = key;
|
||||||
user.Kdf = kdf;
|
user.Kdf = kdf;
|
||||||
user.KdfIterations = kdfIterations;
|
user.KdfIterations = kdfIterations;
|
||||||
@ -870,7 +877,9 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
|
|
||||||
if (await CheckPasswordAsync(user, masterPassword))
|
if (await CheckPasswordAsync(user, masterPassword))
|
||||||
{
|
{
|
||||||
user.RevisionDate = user.AccountRevisionDate = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
|
user.RevisionDate = user.AccountRevisionDate = now;
|
||||||
|
user.LastKeyRotationDate = now;
|
||||||
user.SecurityStamp = Guid.NewGuid().ToString();
|
user.SecurityStamp = Guid.NewGuid().ToString();
|
||||||
user.Key = key;
|
user.Key = key;
|
||||||
user.PrivateKey = privateKey;
|
user.PrivateKey = privateKey;
|
||||||
|
@ -692,6 +692,15 @@ public static class CoreHelpers
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Secrets Manager
|
||||||
|
foreach (var org in group)
|
||||||
|
{
|
||||||
|
if (org.AccessSecretsManager)
|
||||||
|
{
|
||||||
|
claims.Add(new KeyValuePair<string, string>(Claims.SecretsManagerAccess, org.Id.ToString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,9 @@ public class Startup
|
|||||||
// Settings
|
// Settings
|
||||||
var globalSettings = services.AddGlobalSettingsServices(Configuration, Environment);
|
var globalSettings = services.AddGlobalSettingsServices(Configuration, Environment);
|
||||||
|
|
||||||
|
// Data Protection
|
||||||
|
services.AddCustomDataProtectionServices(Environment, globalSettings);
|
||||||
|
|
||||||
// Repositories
|
// Repositories
|
||||||
services.AddDatabaseRepositories(globalSettings);
|
services.AddDatabaseRepositories(globalSettings);
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ public class ApiResources
|
|||||||
Claims.OrganizationCustom,
|
Claims.OrganizationCustom,
|
||||||
Claims.ProviderAdmin,
|
Claims.ProviderAdmin,
|
||||||
Claims.ProviderServiceUser,
|
Claims.ProviderServiceUser,
|
||||||
|
Claims.SecretsManagerAccess,
|
||||||
}),
|
}),
|
||||||
new(ApiScopes.Internal, new[] { JwtClaimTypes.Subject }),
|
new(ApiScopes.Internal, new[] { JwtClaimTypes.Subject }),
|
||||||
new(ApiScopes.ApiPush, new[] { JwtClaimTypes.Subject }),
|
new(ApiScopes.ApiPush, new[] { JwtClaimTypes.Subject }),
|
||||||
|
@ -59,7 +59,7 @@ public static class DapperHelpers
|
|||||||
public static DataTable ToTvp(this IEnumerable<OrganizationUser> orgUsers)
|
public static DataTable ToTvp(this IEnumerable<OrganizationUser> orgUsers)
|
||||||
{
|
{
|
||||||
var table = new DataTable();
|
var table = new DataTable();
|
||||||
table.SetTypeName("[dbo].[OrganizationUserType]");
|
table.SetTypeName("[dbo].[OrganizationUserType2]");
|
||||||
|
|
||||||
var columnData = new List<(string name, Type type, Func<OrganizationUser, object> getter)>
|
var columnData = new List<(string name, Type type, Func<OrganizationUser, object> getter)>
|
||||||
{
|
{
|
||||||
@ -76,6 +76,7 @@ public static class DapperHelpers
|
|||||||
(nameof(OrganizationUser.RevisionDate), typeof(DateTime), ou => ou.RevisionDate),
|
(nameof(OrganizationUser.RevisionDate), typeof(DateTime), ou => ou.RevisionDate),
|
||||||
(nameof(OrganizationUser.Permissions), typeof(string), ou => ou.Permissions),
|
(nameof(OrganizationUser.Permissions), typeof(string), ou => ou.Permissions),
|
||||||
(nameof(OrganizationUser.ResetPasswordKey), typeof(string), ou => ou.ResetPasswordKey),
|
(nameof(OrganizationUser.ResetPasswordKey), typeof(string), ou => ou.ResetPasswordKey),
|
||||||
|
(nameof(OrganizationUser.AccessSecretsManager), typeof(bool), ou => ou.AccessSecretsManager),
|
||||||
};
|
};
|
||||||
|
|
||||||
return orgUsers.BuildTable(table, columnData);
|
return orgUsers.BuildTable(table, columnData);
|
||||||
|
@ -321,6 +321,8 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd.Parameters.Add("@RevisionDate", SqlDbType.DateTime2).Value = user.RevisionDate;
|
cmd.Parameters.Add("@RevisionDate", SqlDbType.DateTime2).Value = user.RevisionDate;
|
||||||
|
cmd.Parameters.Add("@AccountRevisionDate", SqlDbType.DateTime2).Value = user.AccountRevisionDate;
|
||||||
|
cmd.Parameters.Add("@LastKeyRotationDate", SqlDbType.DateTime2).Value = user.LastKeyRotationDate;
|
||||||
cmd.ExecuteNonQuery();
|
cmd.ExecuteNonQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,7 +405,7 @@ public class OrganizationUserRepository : Repository<OrganizationUser, Guid>, IO
|
|||||||
using (var connection = new SqlConnection(_marsConnectionString))
|
using (var connection = new SqlConnection(_marsConnectionString))
|
||||||
{
|
{
|
||||||
var results = await connection.ExecuteAsync(
|
var results = await connection.ExecuteAsync(
|
||||||
$"[{Schema}].[{Table}_CreateMany]",
|
$"[{Schema}].[{Table}_CreateMany2]",
|
||||||
new { OrganizationUsersInput = orgUsersTVP },
|
new { OrganizationUsersInput = orgUsersTVP },
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
}
|
}
|
||||||
@ -424,7 +424,7 @@ public class OrganizationUserRepository : Repository<OrganizationUser, Guid>, IO
|
|||||||
using (var connection = new SqlConnection(_marsConnectionString))
|
using (var connection = new SqlConnection(_marsConnectionString))
|
||||||
{
|
{
|
||||||
var results = await connection.ExecuteAsync(
|
var results = await connection.ExecuteAsync(
|
||||||
$"[{Schema}].[{Table}_UpdateMany]",
|
$"[{Schema}].[{Table}_UpdateMany2]",
|
||||||
new { OrganizationUsersInput = orgUsersTVP },
|
new { OrganizationUsersInput = orgUsersTVP },
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
}
|
}
|
||||||
|
@ -84,13 +84,13 @@ public class ProviderUserRepository : Repository<ProviderUser, Guid>, IProviderU
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ICollection<ProviderUserUserDetails>> GetManyDetailsByProviderAsync(Guid providerId)
|
public async Task<ICollection<ProviderUserUserDetails>> GetManyDetailsByProviderAsync(Guid providerId, ProviderUserStatusType? status)
|
||||||
{
|
{
|
||||||
using (var connection = new SqlConnection(ConnectionString))
|
using (var connection = new SqlConnection(ConnectionString))
|
||||||
{
|
{
|
||||||
var results = await connection.QueryAsync<ProviderUserUserDetails>(
|
var results = await connection.QueryAsync<ProviderUserUserDetails>(
|
||||||
"[dbo].[ProviderUserUserDetails_ReadByProviderId]",
|
"[dbo].[ProviderUserUserDetails_ReadByProviderId]",
|
||||||
new { ProviderId = providerId },
|
new { ProviderId = providerId, Status = status },
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
return results.ToList();
|
return results.ToList();
|
||||||
|
@ -128,7 +128,9 @@ public abstract class BaseEntityFrameworkRepository
|
|||||||
entity.SecurityStamp = user.SecurityStamp;
|
entity.SecurityStamp = user.SecurityStamp;
|
||||||
entity.Key = user.Key;
|
entity.Key = user.Key;
|
||||||
entity.PrivateKey = user.PrivateKey;
|
entity.PrivateKey = user.PrivateKey;
|
||||||
entity.RevisionDate = DateTime.UtcNow;
|
entity.LastKeyRotationDate = user.LastKeyRotationDate;
|
||||||
|
entity.AccountRevisionDate = user.AccountRevisionDate;
|
||||||
|
entity.RevisionDate = user.RevisionDate;
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
var dbContext = GetDatabaseContext(scope);
|
var dbContext = GetDatabaseContext(scope);
|
||||||
if (cipher.OrganizationId.HasValue)
|
if (cipher.OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId);
|
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId.Value);
|
||||||
}
|
}
|
||||||
else if (cipher.UserId.HasValue)
|
else if (cipher.UserId.HasValue)
|
||||||
{
|
{
|
||||||
@ -59,7 +59,7 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
await OrganizationUpdateStorage(cipherInfo.OrganizationId.Value);
|
await OrganizationUpdateStorage(cipherInfo.OrganizationId.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipherInfo.OrganizationId);
|
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipherInfo.OrganizationId.Value);
|
||||||
}
|
}
|
||||||
else if (cipherInfo?.UserId != null)
|
else if (cipherInfo?.UserId != null)
|
||||||
{
|
{
|
||||||
@ -107,7 +107,16 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
null;
|
null;
|
||||||
var entity = Mapper.Map<Cipher>((Core.Entities.Cipher)cipher);
|
var entity = Mapper.Map<Cipher>((Core.Entities.Cipher)cipher);
|
||||||
await dbContext.AddAsync(entity);
|
await dbContext.AddAsync(entity);
|
||||||
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId.GetValueOrDefault());
|
|
||||||
|
if (cipher.OrganizationId.HasValue)
|
||||||
|
{
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId.Value);
|
||||||
|
}
|
||||||
|
else if (cipher.UserId.HasValue)
|
||||||
|
{
|
||||||
|
await dbContext.UserBumpAccountRevisionDateAsync(cipher.UserId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
return cipher;
|
return cipher;
|
||||||
@ -458,7 +467,16 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
}
|
}
|
||||||
var mappedEntity = Mapper.Map<Cipher>((Core.Entities.Cipher)cipher);
|
var mappedEntity = Mapper.Map<Cipher>((Core.Entities.Cipher)cipher);
|
||||||
dbContext.Entry(entity).CurrentValues.SetValues(mappedEntity);
|
dbContext.Entry(entity).CurrentValues.SetValues(mappedEntity);
|
||||||
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId.GetValueOrDefault());
|
|
||||||
|
if (cipher.OrganizationId.HasValue)
|
||||||
|
{
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId.Value);
|
||||||
|
}
|
||||||
|
else if (cipher.UserId.HasValue)
|
||||||
|
{
|
||||||
|
await dbContext.UserBumpAccountRevisionDateAsync(cipher.UserId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -566,7 +584,15 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId.GetValueOrDefault());
|
if (cipher.OrganizationId.HasValue)
|
||||||
|
{
|
||||||
|
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId.Value);
|
||||||
|
}
|
||||||
|
else if (cipher.UserId.HasValue)
|
||||||
|
{
|
||||||
|
await dbContext.UserBumpAccountRevisionDateAsync(cipher.UserId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -677,7 +703,7 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
|||||||
if (attachment.OrganizationId.HasValue)
|
if (attachment.OrganizationId.HasValue)
|
||||||
{
|
{
|
||||||
await OrganizationUpdateStorage(cipher.OrganizationId.Value);
|
await OrganizationUpdateStorage(cipher.OrganizationId.Value);
|
||||||
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId);
|
await dbContext.UserBumpAccountRevisionDateByCipherIdAsync(cipher.Id, cipher.OrganizationId.Value);
|
||||||
}
|
}
|
||||||
else if (attachment.UserId.HasValue)
|
else if (attachment.UserId.HasValue)
|
||||||
{
|
{
|
||||||
|
@ -34,7 +34,7 @@ public static class DatabaseContextExtensions
|
|||||||
UpdateUserRevisionDate(users);
|
UpdateUserRevisionDate(users);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task UserBumpAccountRevisionDateByCipherIdAsync(this DatabaseContext context, Guid cipherId, Guid? organizationId)
|
public static async Task UserBumpAccountRevisionDateByCipherIdAsync(this DatabaseContext context, Guid cipherId, Guid organizationId)
|
||||||
{
|
{
|
||||||
var query = new UserBumpAccountRevisionDateByCipherIdQuery(cipherId, organizationId);
|
var query = new UserBumpAccountRevisionDateByCipherIdQuery(cipherId, organizationId);
|
||||||
var users = await query.Run(context).ToListAsync();
|
var users = await query.Run(context).ToListAsync();
|
||||||
|
@ -103,7 +103,7 @@ public class ProviderUserRepository :
|
|||||||
return await query.FirstOrDefaultAsync();
|
return await query.FirstOrDefaultAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public async Task<ICollection<ProviderUserUserDetails>> GetManyDetailsByProviderAsync(Guid providerId)
|
public async Task<ICollection<ProviderUserUserDetails>> GetManyDetailsByProviderAsync(Guid providerId, ProviderUserStatusType? status)
|
||||||
{
|
{
|
||||||
using (var scope = ServiceScopeFactory.CreateScope())
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
@ -113,7 +113,9 @@ public class ProviderUserRepository :
|
|||||||
on pu.UserId equals u.Id into u_g
|
on pu.UserId equals u.Id into u_g
|
||||||
from u in u_g.DefaultIfEmpty()
|
from u in u_g.DefaultIfEmpty()
|
||||||
select new { pu, u };
|
select new { pu, u };
|
||||||
var data = await view.Where(e => e.pu.ProviderId == providerId).Select(e => new ProviderUserUserDetails
|
var data = await view
|
||||||
|
.Where(e => e.pu.ProviderId == providerId && (status == null || e.pu.Status == status))
|
||||||
|
.Select(e => new ProviderUserUserDetails
|
||||||
{
|
{
|
||||||
Id = e.pu.Id,
|
Id = e.pu.Id,
|
||||||
UserId = e.pu.UserId,
|
UserId = e.pu.UserId,
|
||||||
|
@ -37,6 +37,7 @@ public class OrganizationUserOrganizationDetailsViewQuery : IQuery<OrganizationU
|
|||||||
Use2fa = o.Use2fa,
|
Use2fa = o.Use2fa,
|
||||||
UseApi = o.UseApi,
|
UseApi = o.UseApi,
|
||||||
UseResetPassword = o.UseResetPassword,
|
UseResetPassword = o.UseResetPassword,
|
||||||
|
UseSecretsManager = o.UseSecretsManager,
|
||||||
SelfHost = o.SelfHost,
|
SelfHost = o.SelfHost,
|
||||||
UsersGetPremium = o.UsersGetPremium,
|
UsersGetPremium = o.UsersGetPremium,
|
||||||
UseCustomPermissions = o.UseCustomPermissions,
|
UseCustomPermissions = o.UseCustomPermissions,
|
||||||
@ -58,7 +59,8 @@ public class OrganizationUserOrganizationDetailsViewQuery : IQuery<OrganizationU
|
|||||||
FamilySponsorshipFriendlyName = os.FriendlyName,
|
FamilySponsorshipFriendlyName = os.FriendlyName,
|
||||||
FamilySponsorshipLastSyncDate = os.LastSyncDate,
|
FamilySponsorshipLastSyncDate = os.LastSyncDate,
|
||||||
FamilySponsorshipToDelete = os.ToDelete,
|
FamilySponsorshipToDelete = os.ToDelete,
|
||||||
FamilySponsorshipValidUntil = os.ValidUntil
|
FamilySponsorshipValidUntil = os.ValidUntil,
|
||||||
|
AccessSecretsManager = ou.AccessSecretsManager,
|
||||||
};
|
};
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ public class OrganizationUserUserDetailsViewQuery : IQuery<OrganizationUserUserD
|
|||||||
Permissions = x.ou.Permissions,
|
Permissions = x.ou.Permissions,
|
||||||
ResetPasswordKey = x.ou.ResetPasswordKey,
|
ResetPasswordKey = x.ou.ResetPasswordKey,
|
||||||
UsesKeyConnector = x.u != null && x.u.UsesKeyConnector,
|
UsesKeyConnector = x.u != null && x.u.UsesKeyConnector,
|
||||||
|
AccessSecretsManager = x.ou.AccessSecretsManager,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ public class ProviderUserOrganizationDetailsViewQuery : IQuery<ProviderUserOrgan
|
|||||||
PrivateKey = x.o.PrivateKey,
|
PrivateKey = x.o.PrivateKey,
|
||||||
ProviderId = x.p.Id,
|
ProviderId = x.p.Id,
|
||||||
ProviderName = x.p.Name,
|
ProviderName = x.p.Name,
|
||||||
|
PlanType = x.o.PlanType
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Enums;
|
|
||||||
using User = Bit.Infrastructure.EntityFramework.Models.User;
|
using User = Bit.Infrastructure.EntityFramework.Models.User;
|
||||||
|
|
||||||
namespace Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
namespace Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
||||||
@ -7,15 +6,9 @@ namespace Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
|||||||
public class UserBumpAccountRevisionDateByCipherIdQuery : IQuery<User>
|
public class UserBumpAccountRevisionDateByCipherIdQuery : IQuery<User>
|
||||||
{
|
{
|
||||||
private readonly Guid _cipherId;
|
private readonly Guid _cipherId;
|
||||||
private readonly Guid? _organizationId;
|
private readonly Guid _organizationId;
|
||||||
|
|
||||||
public UserBumpAccountRevisionDateByCipherIdQuery(Cipher cipher)
|
public UserBumpAccountRevisionDateByCipherIdQuery(Guid cipherId, Guid organizationId)
|
||||||
{
|
|
||||||
_cipherId = cipher.Id;
|
|
||||||
_organizationId = cipher.OrganizationId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserBumpAccountRevisionDateByCipherIdQuery(Guid cipherId, Guid? organizationId)
|
|
||||||
{
|
{
|
||||||
_cipherId = cipherId;
|
_cipherId = cipherId;
|
||||||
_organizationId = organizationId;
|
_organizationId = organizationId;
|
||||||
|
@ -13,7 +13,7 @@ public class HeartbeatHostedService : IHostedService, IDisposable
|
|||||||
private CancellationTokenSource _cts;
|
private CancellationTokenSource _cts;
|
||||||
|
|
||||||
public HeartbeatHostedService(
|
public HeartbeatHostedService(
|
||||||
ILogger<AzureQueueHostedService> logger,
|
ILogger<HeartbeatHostedService> logger,
|
||||||
IHubContext<NotificationsHub> hubContext,
|
IHubContext<NotificationsHub> hubContext,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
@ -49,7 +49,7 @@ public class HeartbeatHostedService : IHostedService, IDisposable
|
|||||||
while (!cancellationToken.IsCancellationRequested)
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
await _hubContext.Clients.All.SendAsync("Heartbeat");
|
await _hubContext.Clients.All.SendAsync("Heartbeat");
|
||||||
await Task.Delay(120000);
|
await Task.Delay(120000, cancellationToken);
|
||||||
}
|
}
|
||||||
_logger.LogWarning("Done with heartbeat.");
|
_logger.LogWarning("Done with heartbeat.");
|
||||||
}
|
}
|
||||||
|
@ -238,6 +238,7 @@
|
|||||||
<Build Include="dbo\Stored Procedures\OrganizationUser_Activate.sql" />
|
<Build Include="dbo\Stored Procedures\OrganizationUser_Activate.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\OrganizationUser_Create.sql" />
|
<Build Include="dbo\Stored Procedures\OrganizationUser_Create.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\OrganizationUser_CreateMany.sql" />
|
<Build Include="dbo\Stored Procedures\OrganizationUser_CreateMany.sql" />
|
||||||
|
<Build Include="dbo\Stored Procedures\OrganizationUser_CreateMany2.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\OrganizationUser_CreateWithCollections.sql" />
|
<Build Include="dbo\Stored Procedures\OrganizationUser_CreateWithCollections.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\OrganizationUser_Deactivate.sql" />
|
<Build Include="dbo\Stored Procedures\OrganizationUser_Deactivate.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\OrganizationUser_DeleteById.sql" />
|
<Build Include="dbo\Stored Procedures\OrganizationUser_DeleteById.sql" />
|
||||||
@ -258,6 +259,7 @@
|
|||||||
<Build Include="dbo\Stored Procedures\OrganizationUser_SelectKnownEmails.sql" />
|
<Build Include="dbo\Stored Procedures\OrganizationUser_SelectKnownEmails.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\OrganizationUser_Update.sql" />
|
<Build Include="dbo\Stored Procedures\OrganizationUser_Update.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\OrganizationUser_UpdateMany.sql" />
|
<Build Include="dbo\Stored Procedures\OrganizationUser_UpdateMany.sql" />
|
||||||
|
<Build Include="dbo\Stored Procedures\OrganizationUser_UpdateMany2.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\OrganizationUser_UpdateWithCollections.sql" />
|
<Build Include="dbo\Stored Procedures\OrganizationUser_UpdateWithCollections.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\Organization_Create.sql" />
|
<Build Include="dbo\Stored Procedures\Organization_Create.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\Organization_DeleteById.sql" />
|
<Build Include="dbo\Stored Procedures\Organization_DeleteById.sql" />
|
||||||
@ -400,6 +402,7 @@
|
|||||||
<Build Include="dbo\User Defined Types\GuidIdArray.sql" />
|
<Build Include="dbo\User Defined Types\GuidIdArray.sql" />
|
||||||
<Build Include="dbo\User Defined Types\OrganizationSponsorshipType.sql" />
|
<Build Include="dbo\User Defined Types\OrganizationSponsorshipType.sql" />
|
||||||
<Build Include="dbo\User Defined Types\OrganizationUserType.sql" />
|
<Build Include="dbo\User Defined Types\OrganizationUserType.sql" />
|
||||||
|
<Build Include="dbo\User Defined Types\OrganizationUserType2.sql" />
|
||||||
<Build Include="dbo\User Defined Types\SelectionReadOnlyArray.sql" />
|
<Build Include="dbo\User Defined Types\SelectionReadOnlyArray.sql" />
|
||||||
<Build Include="dbo\User Defined Types\TwoGuidIdArray.sql" />
|
<Build Include="dbo\User Defined Types\TwoGuidIdArray.sql" />
|
||||||
<Build Include="dbo\Views\AuthRequestView.sql" />
|
<Build Include="dbo\Views\AuthRequestView.sql" />
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
@CreationDate DATETIME2(7),
|
@CreationDate DATETIME2(7),
|
||||||
@RevisionDate DATETIME2(7),
|
@RevisionDate DATETIME2(7),
|
||||||
@Permissions NVARCHAR(MAX),
|
@Permissions NVARCHAR(MAX),
|
||||||
@ResetPasswordKey VARCHAR(MAX)
|
@ResetPasswordKey VARCHAR(MAX),
|
||||||
|
@AccessSecretsManager BIT = 0
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON
|
SET NOCOUNT ON
|
||||||
@ -30,7 +31,8 @@ BEGIN
|
|||||||
[CreationDate],
|
[CreationDate],
|
||||||
[RevisionDate],
|
[RevisionDate],
|
||||||
[Permissions],
|
[Permissions],
|
||||||
[ResetPasswordKey]
|
[ResetPasswordKey],
|
||||||
|
[AccessSecretsManager]
|
||||||
)
|
)
|
||||||
VALUES
|
VALUES
|
||||||
(
|
(
|
||||||
@ -46,6 +48,7 @@ BEGIN
|
|||||||
@CreationDate,
|
@CreationDate,
|
||||||
@RevisionDate,
|
@RevisionDate,
|
||||||
@Permissions,
|
@Permissions,
|
||||||
@ResetPasswordKey
|
@ResetPasswordKey,
|
||||||
|
@AccessSecretsManager
|
||||||
)
|
)
|
||||||
END
|
END
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[OrganizationUser_CreateMany2]
|
||||||
|
@OrganizationUsersInput [dbo].[OrganizationUserType2] READONLY
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
INSERT INTO [dbo].[OrganizationUser]
|
||||||
|
(
|
||||||
|
[Id],
|
||||||
|
[OrganizationId],
|
||||||
|
[UserId],
|
||||||
|
[Email],
|
||||||
|
[Key],
|
||||||
|
[Status],
|
||||||
|
[Type],
|
||||||
|
[AccessAll],
|
||||||
|
[ExternalId],
|
||||||
|
[CreationDate],
|
||||||
|
[RevisionDate],
|
||||||
|
[Permissions],
|
||||||
|
[ResetPasswordKey],
|
||||||
|
[AccessSecretsManager]
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
OU.[Id],
|
||||||
|
OU.[OrganizationId],
|
||||||
|
OU.[UserId],
|
||||||
|
OU.[Email],
|
||||||
|
OU.[Key],
|
||||||
|
OU.[Status],
|
||||||
|
OU.[Type],
|
||||||
|
OU.[AccessAll],
|
||||||
|
OU.[ExternalId],
|
||||||
|
OU.[CreationDate],
|
||||||
|
OU.[RevisionDate],
|
||||||
|
OU.[Permissions],
|
||||||
|
OU.[ResetPasswordKey],
|
||||||
|
OU.[AccessSecretsManager]
|
||||||
|
FROM
|
||||||
|
@OrganizationUsersInput OU
|
||||||
|
END
|
||||||
|
GO
|
@ -12,12 +12,13 @@
|
|||||||
@RevisionDate DATETIME2(7),
|
@RevisionDate DATETIME2(7),
|
||||||
@Permissions NVARCHAR(MAX),
|
@Permissions NVARCHAR(MAX),
|
||||||
@ResetPasswordKey VARCHAR(MAX),
|
@ResetPasswordKey VARCHAR(MAX),
|
||||||
@Collections AS [dbo].[SelectionReadOnlyArray] READONLY
|
@Collections AS [dbo].[SelectionReadOnlyArray] READONLY,
|
||||||
|
@AccessSecretsManager BIT = 0
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON
|
SET NOCOUNT ON
|
||||||
|
|
||||||
EXEC [dbo].[OrganizationUser_Create] @Id, @OrganizationId, @UserId, @Email, @Key, @Status, @Type, @AccessAll, @ExternalId, @CreationDate, @RevisionDate, @Permissions, @ResetPasswordKey
|
EXEC [dbo].[OrganizationUser_Create] @Id, @OrganizationId, @UserId, @Email, @Key, @Status, @Type, @AccessAll, @ExternalId, @CreationDate, @RevisionDate, @Permissions, @ResetPasswordKey, @AccessSecretsManager
|
||||||
|
|
||||||
;WITH [AvailableCollectionsCTE] AS(
|
;WITH [AvailableCollectionsCTE] AS(
|
||||||
SELECT
|
SELECT
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
@CreationDate DATETIME2(7),
|
@CreationDate DATETIME2(7),
|
||||||
@RevisionDate DATETIME2(7),
|
@RevisionDate DATETIME2(7),
|
||||||
@Permissions NVARCHAR(MAX),
|
@Permissions NVARCHAR(MAX),
|
||||||
@ResetPasswordKey VARCHAR(MAX)
|
@ResetPasswordKey VARCHAR(MAX),
|
||||||
|
@AccessSecretsManager BIT = 0
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON
|
SET NOCOUNT ON
|
||||||
@ -30,7 +31,8 @@ BEGIN
|
|||||||
[CreationDate] = @CreationDate,
|
[CreationDate] = @CreationDate,
|
||||||
[RevisionDate] = @RevisionDate,
|
[RevisionDate] = @RevisionDate,
|
||||||
[Permissions] = @Permissions,
|
[Permissions] = @Permissions,
|
||||||
[ResetPasswordKey] = @ResetPasswordKey
|
[ResetPasswordKey] = @ResetPasswordKey,
|
||||||
|
[AccessSecretsManager] = @AccessSecretsManager
|
||||||
WHERE
|
WHERE
|
||||||
[Id] = @Id
|
[Id] = @Id
|
||||||
|
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[OrganizationUser_UpdateMany2]
|
||||||
|
@OrganizationUsersInput [dbo].[OrganizationUserType2] READONLY
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
OU
|
||||||
|
SET
|
||||||
|
[OrganizationId] = OUI.[OrganizationId],
|
||||||
|
[UserId] = OUI.[UserId],
|
||||||
|
[Email] = OUI.[Email],
|
||||||
|
[Key] = OUI.[Key],
|
||||||
|
[Status] = OUI.[Status],
|
||||||
|
[Type] = OUI.[Type],
|
||||||
|
[AccessAll] = OUI.[AccessAll],
|
||||||
|
[ExternalId] = OUI.[ExternalId],
|
||||||
|
[CreationDate] = OUI.[CreationDate],
|
||||||
|
[RevisionDate] = OUI.[RevisionDate],
|
||||||
|
[Permissions] = OUI.[Permissions],
|
||||||
|
[ResetPasswordKey] = OUI.[ResetPasswordKey],
|
||||||
|
[AccessSecretsManager] = OUI.[AccessSecretsManager]
|
||||||
|
FROM
|
||||||
|
[dbo].[OrganizationUser] OU
|
||||||
|
INNER JOIN
|
||||||
|
@OrganizationUsersInput OUI ON OU.Id = OUI.Id
|
||||||
|
|
||||||
|
EXEC [dbo].[User_BumpManyAccountRevisionDates]
|
||||||
|
(
|
||||||
|
SELECT UserId
|
||||||
|
FROM @OrganizationUsersInput
|
||||||
|
)
|
||||||
|
END
|
||||||
|
GO
|
@ -12,12 +12,13 @@
|
|||||||
@RevisionDate DATETIME2(7),
|
@RevisionDate DATETIME2(7),
|
||||||
@Permissions NVARCHAR(MAX),
|
@Permissions NVARCHAR(MAX),
|
||||||
@ResetPasswordKey VARCHAR(MAX),
|
@ResetPasswordKey VARCHAR(MAX),
|
||||||
@Collections AS [dbo].[SelectionReadOnlyArray] READONLY
|
@Collections AS [dbo].[SelectionReadOnlyArray] READONLY,
|
||||||
|
@AccessSecretsManager BIT = 0
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON
|
SET NOCOUNT ON
|
||||||
|
|
||||||
EXEC [dbo].[OrganizationUser_Update] @Id, @OrganizationId, @UserId, @Email, @Key, @Status, @Type, @AccessAll, @ExternalId, @CreationDate, @RevisionDate, @Permissions, @ResetPasswordKey
|
EXEC [dbo].[OrganizationUser_Update] @Id, @OrganizationId, @UserId, @Email, @Key, @Status, @Type, @AccessAll, @ExternalId, @CreationDate, @RevisionDate, @Permissions, @ResetPasswordKey, @AccessSecretsManager
|
||||||
-- Update
|
-- Update
|
||||||
UPDATE
|
UPDATE
|
||||||
[Target]
|
[Target]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
CREATE PROCEDURE [dbo].[ProviderUserUserDetails_ReadByProviderId]
|
CREATE PROCEDURE [dbo].[ProviderUserUserDetails_ReadByProviderId]
|
||||||
@ProviderId UNIQUEIDENTIFIER
|
@ProviderId UNIQUEIDENTIFIER,
|
||||||
|
@Status TINYINT = NULL
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON
|
SET NOCOUNT ON
|
||||||
@ -10,4 +11,5 @@ BEGIN
|
|||||||
[dbo].[ProviderUserUserDetailsView]
|
[dbo].[ProviderUserUserDetailsView]
|
||||||
WHERE
|
WHERE
|
||||||
[ProviderId] = @ProviderId
|
[ProviderId] = @ProviderId
|
||||||
|
AND [Status] = COALESCE(@Status, [Status])
|
||||||
END
|
END
|
||||||
|
@ -37,7 +37,11 @@
|
|||||||
@FailedLoginCount INT = 0,
|
@FailedLoginCount INT = 0,
|
||||||
@LastFailedLoginDate DATETIME2(7),
|
@LastFailedLoginDate DATETIME2(7),
|
||||||
@UnknownDeviceVerificationEnabled BIT = 1,
|
@UnknownDeviceVerificationEnabled BIT = 1,
|
||||||
@AvatarColor VARCHAR(7) = NULL
|
@AvatarColor VARCHAR(7) = NULL,
|
||||||
|
@LastPasswordChangeDate DATETIME2(7) = NULL,
|
||||||
|
@LastKdfChangeDate DATETIME2(7) = NULL,
|
||||||
|
@LastKeyRotationDate DATETIME2(7) = NULL,
|
||||||
|
@LastEmailChangeDate DATETIME2(7) = NULL
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON
|
SET NOCOUNT ON
|
||||||
@ -82,7 +86,11 @@ BEGIN
|
|||||||
[UnknownDeviceVerificationEnabled],
|
[UnknownDeviceVerificationEnabled],
|
||||||
[AvatarColor],
|
[AvatarColor],
|
||||||
[KdfMemory],
|
[KdfMemory],
|
||||||
[KdfParallelism]
|
[KdfParallelism],
|
||||||
|
[LastPasswordChangeDate],
|
||||||
|
[LastKdfChangeDate],
|
||||||
|
[LastKeyRotationDate],
|
||||||
|
[LastEmailChangeDate]
|
||||||
)
|
)
|
||||||
VALUES
|
VALUES
|
||||||
(
|
(
|
||||||
@ -124,6 +132,10 @@ BEGIN
|
|||||||
@UnknownDeviceVerificationEnabled,
|
@UnknownDeviceVerificationEnabled,
|
||||||
@AvatarColor,
|
@AvatarColor,
|
||||||
@KdfMemory,
|
@KdfMemory,
|
||||||
@KdfParallelism
|
@KdfParallelism,
|
||||||
|
@LastPasswordChangeDate,
|
||||||
|
@LastKdfChangeDate,
|
||||||
|
@LastKeyRotationDate,
|
||||||
|
@LastEmailChangeDate
|
||||||
)
|
)
|
||||||
END
|
END
|
||||||
|
@ -37,7 +37,11 @@
|
|||||||
@FailedLoginCount INT,
|
@FailedLoginCount INT,
|
||||||
@LastFailedLoginDate DATETIME2(7),
|
@LastFailedLoginDate DATETIME2(7),
|
||||||
@UnknownDeviceVerificationEnabled BIT = 1,
|
@UnknownDeviceVerificationEnabled BIT = 1,
|
||||||
@AvatarColor VARCHAR(7)
|
@AvatarColor VARCHAR(7),
|
||||||
|
@LastPasswordChangeDate DATETIME2(7) = NULL,
|
||||||
|
@LastKdfChangeDate DATETIME2(7) = NULL,
|
||||||
|
@LastKeyRotationDate DATETIME2(7) = NULL,
|
||||||
|
@LastEmailChangeDate DATETIME2(7) = NULL
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON
|
SET NOCOUNT ON
|
||||||
@ -82,7 +86,11 @@ BEGIN
|
|||||||
[FailedLoginCount] = @FailedLoginCount,
|
[FailedLoginCount] = @FailedLoginCount,
|
||||||
[LastFailedLoginDate] = @LastFailedLoginDate,
|
[LastFailedLoginDate] = @LastFailedLoginDate,
|
||||||
[UnknownDeviceVerificationEnabled] = @UnknownDeviceVerificationEnabled,
|
[UnknownDeviceVerificationEnabled] = @UnknownDeviceVerificationEnabled,
|
||||||
[AvatarColor] = @AvatarColor
|
[AvatarColor] = @AvatarColor,
|
||||||
|
[LastPasswordChangeDate] = @LastPasswordChangeDate,
|
||||||
|
[LastKdfChangeDate] = @LastKdfChangeDate,
|
||||||
|
[LastKeyRotationDate] = @LastKeyRotationDate,
|
||||||
|
[LastEmailChangeDate] = @LastEmailChangeDate
|
||||||
WHERE
|
WHERE
|
||||||
[Id] = @Id
|
[Id] = @Id
|
||||||
END
|
END
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
@SecurityStamp NVARCHAR(50),
|
@SecurityStamp NVARCHAR(50),
|
||||||
@Key NVARCHAR(MAX),
|
@Key NVARCHAR(MAX),
|
||||||
@PrivateKey VARCHAR(MAX),
|
@PrivateKey VARCHAR(MAX),
|
||||||
@RevisionDate DATETIME2(7)
|
@RevisionDate DATETIME2(7),
|
||||||
|
@AccountRevisionDate DATETIME2(7) = NULL,
|
||||||
|
@LastKeyRotationDate DATETIME2(7) = NULL
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON
|
SET NOCOUNT ON
|
||||||
@ -15,7 +17,8 @@ BEGIN
|
|||||||
[Key] = @Key,
|
[Key] = @Key,
|
||||||
[PrivateKey] = @PrivateKey,
|
[PrivateKey] = @PrivateKey,
|
||||||
[RevisionDate] = @RevisionDate,
|
[RevisionDate] = @RevisionDate,
|
||||||
[AccountRevisionDate] = @RevisionDate
|
[AccountRevisionDate] = ISNULL(@AccountRevisionDate, @RevisionDate),
|
||||||
|
[LastKeyRotationDate] = @LastKeyRotationDate
|
||||||
WHERE
|
WHERE
|
||||||
[Id] = @Id
|
[Id] = @Id
|
||||||
END
|
END
|
@ -12,6 +12,7 @@
|
|||||||
[CreationDate] DATETIME2 (7) NOT NULL,
|
[CreationDate] DATETIME2 (7) NOT NULL,
|
||||||
[RevisionDate] DATETIME2 (7) NOT NULL,
|
[RevisionDate] DATETIME2 (7) NOT NULL,
|
||||||
[Permissions] NVARCHAR (MAX) NULL,
|
[Permissions] NVARCHAR (MAX) NULL,
|
||||||
|
[AccessSecretsManager] BIT NOT NULL CONSTRAINT [DF_OrganizationUser_SecretsManager] DEFAULT (0),
|
||||||
CONSTRAINT [PK_OrganizationUser] PRIMARY KEY CLUSTERED ([Id] ASC),
|
CONSTRAINT [PK_OrganizationUser] PRIMARY KEY CLUSTERED ([Id] ASC),
|
||||||
CONSTRAINT [FK_OrganizationUser_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]) ON DELETE CASCADE,
|
CONSTRAINT [FK_OrganizationUser_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]) ON DELETE CASCADE,
|
||||||
CONSTRAINT [FK_OrganizationUser_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id])
|
CONSTRAINT [FK_OrganizationUser_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id])
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user