mirror of
https://github.com/bitwarden/server.git
synced 2025-07-01 08:02:49 -05:00
Merge branch 'master' into PM-1033-org-invite-user-creation-flow-1
This commit is contained in:
188
.github/workflows/build-self-host.yml
vendored
188
.github/workflows/build-self-host.yml
vendored
@ -1,188 +0,0 @@
|
|||||||
---
|
|
||||||
name: Build Self-Host
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches-ignore:
|
|
||||||
- "l10n_master"
|
|
||||||
- "gh-pages"
|
|
||||||
paths-ignore:
|
|
||||||
- ".github/workflows/**"
|
|
||||||
workflow_dispatch:
|
|
||||||
pull_request:
|
|
||||||
branches-ignore:
|
|
||||||
- "l10n_master"
|
|
||||||
- "gh-pages"
|
|
||||||
paths:
|
|
||||||
- ".github/workflows/build-self-host.yml"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-docker:
|
|
||||||
name: Build Docker image
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- name: Checkout repo
|
|
||||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
|
||||||
|
|
||||||
- name: Check Branch to Publish
|
|
||||||
env:
|
|
||||||
PUBLISH_BRANCHES: "master,rc,hotfix-rc"
|
|
||||||
id: publish-branch-check
|
|
||||||
run: |
|
|
||||||
IFS="," read -a publish_branches <<< $PUBLISH_BRANCHES
|
|
||||||
|
|
||||||
if [[ " ${publish_branches[*]} " =~ " ${GITHUB_REF:11} " ]]; then
|
|
||||||
echo "is_publish_branch=true" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
echo "is_publish_branch=false" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
|
|
||||||
########## Set up Docker ##########
|
|
||||||
- name: Set up QEMU emulators
|
|
||||||
uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2.1.0
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2.5.0
|
|
||||||
|
|
||||||
########## Login to Docker registries ##########
|
|
||||||
- name: Login to Azure - QA Subscription
|
|
||||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
|
|
||||||
with:
|
|
||||||
creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
|
|
||||||
|
|
||||||
- name: Login to Azure ACR
|
|
||||||
run: az acr login -n bitwardenqa
|
|
||||||
|
|
||||||
- name: Login to Azure - Prod Subscription
|
|
||||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
|
|
||||||
with:
|
|
||||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
|
||||||
|
|
||||||
- name: Login to Azure ACR
|
|
||||||
run: az acr login -n bitwardenprod
|
|
||||||
|
|
||||||
- name: Login to Azure - CI Subscription
|
|
||||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
|
|
||||||
with:
|
|
||||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
|
||||||
|
|
||||||
- name: Retrieve github PAT secrets
|
|
||||||
id: retrieve-secret-pat
|
|
||||||
uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
|
|
||||||
with:
|
|
||||||
keyvault: "bitwarden-ci"
|
|
||||||
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
|
|
||||||
|
|
||||||
- name: Retrieve secrets
|
|
||||||
if: ${{ env.is_publish_branch == 'true' }}
|
|
||||||
id: retrieve-secrets
|
|
||||||
uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
|
|
||||||
with:
|
|
||||||
keyvault: "bitwarden-ci"
|
|
||||||
secrets: "docker-password,
|
|
||||||
docker-username,
|
|
||||||
dct-delegate-2-repo-passphrase,
|
|
||||||
dct-delegate-2-key"
|
|
||||||
|
|
||||||
- name: Log into Docker
|
|
||||||
if: ${{ env.is_publish_branch == 'true' }}
|
|
||||||
env:
|
|
||||||
DOCKER_USERNAME: ${{ steps.retrieve-secrets.outputs.docker-username }}
|
|
||||||
DOCKER_PASSWORD: ${{ steps.retrieve-secrets.outputs.docker-password }}
|
|
||||||
run: echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
|
||||||
|
|
||||||
- name: Setup Docker Trust
|
|
||||||
if: ${{ env.is_publish_branch == 'true' }}
|
|
||||||
env:
|
|
||||||
DCT_DELEGATION_KEY_ID: "c9bde8ec820701516491e5e03d3a6354e7bd66d05fa3df2b0062f68b116dc59c"
|
|
||||||
DCT_DELEGATE_KEY: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-key }}
|
|
||||||
DCT_REPO_PASSPHRASE: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-repo-passphrase }}
|
|
||||||
run: |
|
|
||||||
mkdir -p ~/.docker/trust/private
|
|
||||||
echo "$DCT_DELEGATE_KEY" > ~/.docker/trust/private/$DCT_DELEGATION_KEY_ID.key
|
|
||||||
echo "DOCKER_CONTENT_TRUST=1" >> $GITHUB_ENV
|
|
||||||
echo "DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=$DCT_REPO_PASSPHRASE" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
########## Generate image tag and build Docker image ##########
|
|
||||||
- name: Generate Docker image tag
|
|
||||||
id: tag
|
|
||||||
run: |
|
|
||||||
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
|
|
||||||
if [[ "$IMAGE_TAG" == "master" ]]; then
|
|
||||||
IMAGE_TAG=dev
|
|
||||||
elif [[ "$IMAGE_TAG" == "rc" ]] || [[ "$IMAGE_TAG" == "hotfix-rc" ]]; then
|
|
||||||
IMAGE_TAG=beta
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Generate tag list
|
|
||||||
id: tag-list
|
|
||||||
env:
|
|
||||||
IMAGE_TAG: ${{ steps.tag.outputs.image_tag }}
|
|
||||||
run: |
|
|
||||||
if [ "$IMAGE_TAG" = "dev" ] || [ "$IMAGE_TAG" = "beta" ]; then
|
|
||||||
echo "tags=bitwardenqa.azurecr.io/self-host:${IMAGE_TAG},bitwardenprod.azurecr.io/self-host:${IMAGE_TAG},bitwarden/self-host:${IMAGE_TAG}" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "tags=bitwardenqa.azurecr.io/self-host:${IMAGE_TAG},bitwardenprod.azurecr.io/self-host:${IMAGE_TAG}" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Build Docker image
|
|
||||||
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 # v3.2.0
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: docker-unified/Dockerfile
|
|
||||||
platforms: |
|
|
||||||
linux/amd64,
|
|
||||||
linux/arm/v7,
|
|
||||||
linux/arm64/v8
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.tag-list.outputs.tags }}
|
|
||||||
secrets: |
|
|
||||||
"GH_PAT=${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }}"
|
|
||||||
|
|
||||||
- name: Log out of Docker and disable Docker Notary
|
|
||||||
if: ${{ env.is_publish_branch == 'true' }}
|
|
||||||
run: |
|
|
||||||
docker logout
|
|
||||||
echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
check-failures:
|
|
||||||
name: Check for failures
|
|
||||||
if: always()
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
needs: build-docker
|
|
||||||
steps:
|
|
||||||
- name: Check if any job failed
|
|
||||||
if: |
|
|
||||||
github.ref == 'refs/heads/master'
|
|
||||||
|| github.ref == 'refs/heads/rc'
|
|
||||||
|| github.ref == 'refs/heads/hotfix-rc'
|
|
||||||
env:
|
|
||||||
BUILD_DOCKER_STATUS: ${{ needs.build-docker.result }}
|
|
||||||
run: |
|
|
||||||
if [ "$BUILD_DOCKER_STATUS" = "failure" ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Login to Azure - CI subscription
|
|
||||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
|
|
||||||
if: failure()
|
|
||||||
with:
|
|
||||||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
|
||||||
|
|
||||||
- name: Retrieve secrets
|
|
||||||
id: retrieve-secrets
|
|
||||||
uses: bitwarden/gh-actions/get-keyvault-secrets@c86ced0dc8c9daeecf057a6333e6f318db9c5a2b
|
|
||||||
if: failure()
|
|
||||||
with:
|
|
||||||
keyvault: "bitwarden-ci"
|
|
||||||
secrets: "devops-alerts-slack-webhook-url"
|
|
||||||
|
|
||||||
- name: Notify Slack on failure
|
|
||||||
uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0
|
|
||||||
if: failure()
|
|
||||||
env:
|
|
||||||
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
|
|
||||||
with:
|
|
||||||
status: ${{ job.status }}
|
|
27
.github/workflows/build.yml
vendored
27
.github/workflows/build.yml
vendored
@ -317,17 +317,12 @@ jobs:
|
|||||||
DOCKER_PASSWORD: ${{ steps.retrieve-secrets.outputs.docker-password }}
|
DOCKER_PASSWORD: ${{ steps.retrieve-secrets.outputs.docker-password }}
|
||||||
run: echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
run: echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
||||||
|
|
||||||
- name: Setup Docker Trust
|
- name: Setup Docker Content Trust (DCT)
|
||||||
if: ${{ env.is_publish_branch == 'true' }}
|
if: ${{ env.is_publish_branch == 'true' }}
|
||||||
env:
|
uses: bitwarden/gh-actions/setup-docker-trust@f955298c7a982b3fb5dbb73afd582c584fd5beec
|
||||||
DCT_DELEGATION_KEY_ID: "c9bde8ec820701516491e5e03d3a6354e7bd66d05fa3df2b0062f68b116dc59c"
|
with:
|
||||||
DCT_DELEGATE_KEY: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-key }}
|
azure-creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||||
DCT_REPO_PASSPHRASE: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-repo-passphrase }}
|
azure-keyvault-name: "bitwarden-ci"
|
||||||
run: |
|
|
||||||
mkdir -p ~/.docker/trust/private
|
|
||||||
echo "$DCT_DELEGATE_KEY" > ~/.docker/trust/private/$DCT_DELEGATION_KEY_ID.key
|
|
||||||
echo "DOCKER_CONTENT_TRUST=1" >> $GITHUB_ENV
|
|
||||||
echo "DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=$DCT_REPO_PASSPHRASE" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
########## Generate image tag and build Docker image ##########
|
########## Generate image tag and build Docker image ##########
|
||||||
- name: Generate Docker image tag
|
- name: Generate Docker image tag
|
||||||
@ -528,6 +523,14 @@ jobs:
|
|||||||
path: util/MsSqlMigratorUtility/obj/build-output/publish/MsSqlMigratorUtility
|
path: util/MsSqlMigratorUtility/obj/build-output/publish/MsSqlMigratorUtility
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
self-host-build:
|
||||||
|
name: Self-host build
|
||||||
|
needs: build-docker
|
||||||
|
uses: bitwarden/self-host/.github/workflows/build-unified.yml@master
|
||||||
|
with:
|
||||||
|
server_branch: ${{ github.ref_name }}
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
check-failures:
|
check-failures:
|
||||||
name: Check for failures
|
name: Check for failures
|
||||||
if: always()
|
if: always()
|
||||||
@ -540,6 +543,7 @@ jobs:
|
|||||||
- build-docker
|
- build-docker
|
||||||
- upload
|
- upload
|
||||||
- build-mssqlmigratorutility
|
- build-mssqlmigratorutility
|
||||||
|
- self-host-build
|
||||||
steps:
|
steps:
|
||||||
- name: Check if any job failed
|
- name: Check if any job failed
|
||||||
if: |
|
if: |
|
||||||
@ -554,6 +558,7 @@ jobs:
|
|||||||
BUILD_DOCKER_STATUS: ${{ needs.build-docker.result }}
|
BUILD_DOCKER_STATUS: ${{ needs.build-docker.result }}
|
||||||
UPLOAD_STATUS: ${{ needs.upload.result }}
|
UPLOAD_STATUS: ${{ needs.upload.result }}
|
||||||
BUILD_MSSQLMIGRATORUTILITY_STATUS: ${{ needs.build-mssqlmigratorutility.result }}
|
BUILD_MSSQLMIGRATORUTILITY_STATUS: ${{ needs.build-mssqlmigratorutility.result }}
|
||||||
|
TRIGGER_SELF_HOST_BUILD_STATUS: ${{ needs.self-host-build.result }}
|
||||||
run: |
|
run: |
|
||||||
if [ "$CLOC_STATUS" = "failure" ]; then
|
if [ "$CLOC_STATUS" = "failure" ]; then
|
||||||
exit 1
|
exit 1
|
||||||
@ -569,6 +574,8 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
elif [ "$BUILD_MSSQLMIGRATORUTILITY_STATUS" = "failure" ]; then
|
elif [ "$BUILD_MSSQLMIGRATORUTILITY_STATUS" = "failure" ]; then
|
||||||
exit 1
|
exit 1
|
||||||
|
elif [ "$TRIGGER_SELF_HOST_BUILD_STATUS" = "failure" ]; then
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Login to Azure - CI subscription
|
- name: Login to Azure - CI subscription
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<!--2022.6.2-->
|
<!--2022.6.2-->
|
||||||
<Version>2023.5.0</Version>
|
<Version>2023.5.1</Version>
|
||||||
<RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace>
|
<RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace>
|
||||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
@ -41,6 +41,9 @@ public class SecretAuthorizationHandler : AuthorizationHandler<SecretOperationRe
|
|||||||
case not null when requirement == SecretOperations.Update:
|
case not null when requirement == SecretOperations.Update:
|
||||||
await CanUpdateSecretAsync(context, requirement, resource);
|
await CanUpdateSecretAsync(context, requirement, resource);
|
||||||
break;
|
break;
|
||||||
|
case not null when requirement == SecretOperations.Delete:
|
||||||
|
await CanDeleteSecretAsync(context, requirement, resource);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException("Unsupported operation requirement type provided.", nameof(requirement));
|
throw new ArgumentException("Unsupported operation requirement type provided.", nameof(requirement));
|
||||||
}
|
}
|
||||||
@ -120,4 +123,22 @@ public class SecretAuthorizationHandler : AuthorizationHandler<SecretOperationRe
|
|||||||
context.Succeed(requirement);
|
context.Succeed(requirement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task CanDeleteSecretAsync(AuthorizationHandlerContext context,
|
||||||
|
SecretOperationRequirement requirement, Secret resource)
|
||||||
|
{
|
||||||
|
var (accessClient, userId) = await _accessClientQuery.GetAccessClientAsync(context.User, resource.OrganizationId);
|
||||||
|
|
||||||
|
if (accessClient == AccessClientType.ServiceAccount)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var access = await _secretRepository.AccessToSecretAsync(resource.Id, userId, accessClient);
|
||||||
|
|
||||||
|
if (access.Write)
|
||||||
|
{
|
||||||
|
context.Succeed(requirement);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
using Bit.Core.Exceptions;
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.SecretsManager.Commands.AccessTokens.Interfaces;
|
using Bit.Core.SecretsManager.Commands.AccessTokens.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
using Bit.Core.SecretsManager.Models.Data;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
@ -16,14 +19,24 @@ public class CreateAccessTokenCommand : ICreateAccessTokenCommand
|
|||||||
_apiKeyRepository = apiKeyRepository;
|
_apiKeyRepository = apiKeyRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ApiKey> CreateAsync(ApiKey apiKey)
|
public async Task<ApiKeyClientSecretDetails> CreateAsync(ApiKey apiKey)
|
||||||
{
|
{
|
||||||
if (apiKey.ServiceAccountId == null)
|
if (apiKey.ServiceAccountId == null)
|
||||||
{
|
{
|
||||||
throw new BadRequestException();
|
throw new BadRequestException();
|
||||||
}
|
}
|
||||||
|
|
||||||
apiKey.ClientSecret = CoreHelpers.SecureRandomString(_clientSecretMaxLength);
|
var clientSecret = CoreHelpers.SecureRandomString(_clientSecretMaxLength);
|
||||||
return await _apiKeyRepository.CreateAsync(apiKey);
|
apiKey.ClientSecretHash = GetHash(clientSecret);
|
||||||
|
var result = await _apiKeyRepository.CreateAsync(apiKey);
|
||||||
|
return new ApiKeyClientSecretDetails { ApiKey = result, ClientSecret = clientSecret };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetHash(string input)
|
||||||
|
{
|
||||||
|
using var sha = SHA256.Create();
|
||||||
|
var bytes = Encoding.UTF8.GetBytes(input);
|
||||||
|
var hash = sha.ComputeHash(bytes);
|
||||||
|
return Convert.ToBase64String(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
using Bit.Core.Context;
|
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Exceptions;
|
|
||||||
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;
|
||||||
|
|
||||||
@ -9,73 +6,16 @@ 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;
|
||||||
private readonly IProjectRepository _projectRepository;
|
|
||||||
|
|
||||||
public DeleteSecretCommand(ISecretRepository secretRepository, IProjectRepository projectRepository, ICurrentContext currentContext)
|
public DeleteSecretCommand(ISecretRepository secretRepository)
|
||||||
{
|
{
|
||||||
_currentContext = currentContext;
|
|
||||||
_secretRepository = secretRepository;
|
_secretRepository = secretRepository;
|
||||||
_projectRepository = projectRepository;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<Tuple<Secret, string>>> DeleteSecrets(List<Guid> ids, Guid userId)
|
public async Task DeleteSecrets(IEnumerable<Secret> secrets)
|
||||||
{
|
{
|
||||||
var secrets = (await _secretRepository.GetManyByIds(ids)).ToList();
|
await _secretRepository.SoftDeleteManyByIdAsync(secrets.Select(s => s.Id));
|
||||||
|
|
||||||
if (secrets.Any() != true)
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure all secrets belongs to the same organization
|
|
||||||
var organizationId = secrets.First().OrganizationId;
|
|
||||||
if (secrets.Any(secret => secret.OrganizationId != organizationId))
|
|
||||||
{
|
|
||||||
throw new BadRequestException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_currentContext.AccessSecretsManager(organizationId))
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
|
||||||
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
|
||||||
|
|
||||||
var results = new List<Tuple<Secret, string>>();
|
|
||||||
var deleteIds = new List<Guid>();
|
|
||||||
|
|
||||||
foreach (var secret in secrets)
|
|
||||||
{
|
|
||||||
var hasAccess = orgAdmin;
|
|
||||||
|
|
||||||
if (secret.Projects != null && secret.Projects?.Count > 0)
|
|
||||||
{
|
|
||||||
var projectId = secret.Projects.First().Id;
|
|
||||||
hasAccess = (await _projectRepository.AccessToProjectAsync(projectId, userId, accessClient)).Write;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasAccess || accessClient == AccessClientType.ServiceAccount)
|
|
||||||
{
|
|
||||||
results.Add(new Tuple<Secret, string>(secret, "access denied"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
deleteIds.Add(secret.Id);
|
|
||||||
results.Add(new Tuple<Secret, string>(secret, ""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (deleteIds.Count > 0)
|
|
||||||
{
|
|
||||||
await _secretRepository.SoftDeleteManyByIdAsync(deleteIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2783,7 +2783,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
|
@ -2786,7 +2786,7 @@
|
|||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2794,7 +2794,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -2806,9 +2806,9 @@
|
|||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2945,7 +2945,7 @@
|
|||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2953,7 +2953,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -2965,9 +2965,9 @@
|
|||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -374,4 +374,83 @@ public class SecretAuthorizationHandlerTests
|
|||||||
|
|
||||||
Assert.True(authzContext.HasSucceeded);
|
Assert.True(authzContext.HasSucceeded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task CanDeleteSecret_AccessToSecretsManagerFalse_DoesNotSucceed(
|
||||||
|
SutProvider<SecretAuthorizationHandler> sutProvider, Secret secret,
|
||||||
|
ClaimsPrincipal claimsPrincipal)
|
||||||
|
{
|
||||||
|
var requirement = SecretOperations.Delete;
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(secret.OrganizationId)
|
||||||
|
.Returns(false);
|
||||||
|
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||||
|
claimsPrincipal, secret);
|
||||||
|
|
||||||
|
await sutProvider.Sut.HandleAsync(authzContext);
|
||||||
|
|
||||||
|
Assert.False(authzContext.HasSucceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task CanDeleteSecret_NullResource_DoesNotSucceed(
|
||||||
|
SutProvider<SecretAuthorizationHandler> sutProvider, Secret secret,
|
||||||
|
ClaimsPrincipal claimsPrincipal,
|
||||||
|
Guid userId)
|
||||||
|
{
|
||||||
|
var requirement = SecretOperations.Delete;
|
||||||
|
SetupPermission(sutProvider, PermissionType.RunAsAdmin, secret.OrganizationId, userId);
|
||||||
|
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||||
|
claimsPrincipal, null);
|
||||||
|
|
||||||
|
await sutProvider.Sut.HandleAsync(authzContext);
|
||||||
|
|
||||||
|
Assert.False(authzContext.HasSucceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task CanDeleteSecret_ServiceAccountClient_DoesNotSucceed(
|
||||||
|
SutProvider<SecretAuthorizationHandler> sutProvider, Secret secret, Guid userId,
|
||||||
|
ClaimsPrincipal claimsPrincipal)
|
||||||
|
{
|
||||||
|
var requirement = SecretOperations.Delete;
|
||||||
|
SetupPermission(sutProvider, PermissionType.RunAsUserWithPermission, secret.OrganizationId, userId,
|
||||||
|
AccessClientType.ServiceAccount);
|
||||||
|
sutProvider.GetDependency<ISecretRepository>()
|
||||||
|
.AccessToSecretAsync(secret.Id, userId, Arg.Any<AccessClientType>())
|
||||||
|
.Returns((true, true));
|
||||||
|
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||||
|
claimsPrincipal, secret);
|
||||||
|
|
||||||
|
await sutProvider.Sut.HandleAsync(authzContext);
|
||||||
|
|
||||||
|
Assert.False(authzContext.HasSucceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(PermissionType.RunAsAdmin, true, true, true)]
|
||||||
|
[BitAutoData(PermissionType.RunAsUserWithPermission, false, false, false)]
|
||||||
|
[BitAutoData(PermissionType.RunAsUserWithPermission, false, true, true)]
|
||||||
|
[BitAutoData(PermissionType.RunAsUserWithPermission, true, false, false)]
|
||||||
|
[BitAutoData(PermissionType.RunAsUserWithPermission, true, true, true)]
|
||||||
|
public async Task CanDeleteProject_AccessCheck(PermissionType permissionType, bool read, bool write,
|
||||||
|
bool expected,
|
||||||
|
SutProvider<SecretAuthorizationHandler> sutProvider, Secret secret,
|
||||||
|
ClaimsPrincipal claimsPrincipal,
|
||||||
|
Guid userId)
|
||||||
|
{
|
||||||
|
var requirement = SecretOperations.Delete;
|
||||||
|
SetupPermission(sutProvider, permissionType, secret.OrganizationId, userId);
|
||||||
|
sutProvider.GetDependency<ISecretRepository>()
|
||||||
|
.AccessToSecretAsync(secret.Id, userId, Arg.Any<AccessClientType>())
|
||||||
|
.Returns((read, write));
|
||||||
|
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||||
|
claimsPrincipal, secret);
|
||||||
|
|
||||||
|
await sutProvider.Sut.HandleAsync(authzContext);
|
||||||
|
|
||||||
|
Assert.Equal(expected, authzContext.HasSucceeded);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
using Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
using Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
||||||
using Bit.Commercial.Core.Test.SecretsManager.Enums;
|
|
||||||
using Bit.Core.Context;
|
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.Exceptions;
|
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture;
|
using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture;
|
||||||
@ -20,75 +16,12 @@ public class DeleteSecretCommandTests
|
|||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task DeleteSecrets_Throws_NotFoundException(List<Guid> data,
|
public async Task DeleteSecrets_Success(SutProvider<DeleteSecretCommand> sutProvider, List<Secret> data)
|
||||||
SutProvider<DeleteSecretCommand> sutProvider)
|
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(data).Returns(new List<Secret>());
|
await sutProvider.Sut.DeleteSecrets(data);
|
||||||
|
await sutProvider.GetDependency<ISecretRepository>()
|
||||||
var exception = await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteSecrets(data, default));
|
.Received(1)
|
||||||
|
.SoftDeleteManyByIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data.Select(d => d.Id))));
|
||||||
await sutProvider.GetDependency<ISecretRepository>().DidNotReceiveWithAnyArgs().SoftDeleteManyByIdAsync(default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData]
|
|
||||||
public async Task DeleteSecrets_OneIdNotFound_Throws_NotFoundException(List<Guid> data,
|
|
||||||
SutProvider<DeleteSecretCommand> sutProvider)
|
|
||||||
{
|
|
||||||
var secret = new Secret()
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid()
|
|
||||||
};
|
|
||||||
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(data).Returns(new List<Secret>() { secret });
|
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteSecrets(data, default));
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<ISecretRepository>().DidNotReceiveWithAnyArgs().SoftDeleteManyByIdAsync(default);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(PermissionType.RunAsAdmin)]
|
|
||||||
[BitAutoData(PermissionType.RunAsUserWithPermission)]
|
|
||||||
public async Task DeleteSecrets_Success(PermissionType permissionType, List<Guid> data,
|
|
||||||
SutProvider<DeleteSecretCommand> sutProvider, Guid userId, Guid organizationId, Project mockProject)
|
|
||||||
{
|
|
||||||
List<Project> projects = null;
|
|
||||||
|
|
||||||
if (permissionType == PermissionType.RunAsAdmin)
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(organizationId).Returns(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(organizationId).Returns(false);
|
|
||||||
sutProvider.GetDependency<IProjectRepository>().AccessToProjectAsync(mockProject.Id, userId, AccessClientType.User)
|
|
||||||
.Returns((true, true));
|
|
||||||
projects = new List<Project>() { mockProject };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var secrets = new List<Secret>();
|
|
||||||
foreach (Guid id in data)
|
|
||||||
{
|
|
||||||
var secret = new Secret()
|
|
||||||
{
|
|
||||||
Id = id,
|
|
||||||
OrganizationId = organizationId,
|
|
||||||
Projects = projects
|
|
||||||
};
|
|
||||||
secrets.Add(secret);
|
|
||||||
}
|
|
||||||
|
|
||||||
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(data).Returns(secrets);
|
|
||||||
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(default).ReturnsForAnyArgs(true);
|
|
||||||
|
|
||||||
var results = await sutProvider.Sut.DeleteSecrets(data, userId);
|
|
||||||
await sutProvider.GetDependency<ISecretRepository>().Received(1).SoftDeleteManyByIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data)));
|
|
||||||
|
|
||||||
foreach (var result in results)
|
|
||||||
{
|
|
||||||
Assert.Equal("", result.Item2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2818,7 +2818,7 @@
|
|||||||
"commercial.core": {
|
"commercial.core": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0"
|
"Core": "2023.4.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
@ -2826,7 +2826,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoFixture.AutoNSubstitute": "4.17.0",
|
"AutoFixture.AutoNSubstitute": "4.17.0",
|
||||||
"AutoFixture.Xunit2": "4.17.0",
|
"AutoFixture.Xunit2": "4.17.0",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
||||||
"Microsoft.NET.Test.Sdk": "17.1.0",
|
"Microsoft.NET.Test.Sdk": "17.1.0",
|
||||||
"NSubstitute": "4.3.0",
|
"NSubstitute": "4.3.0",
|
||||||
@ -2880,8 +2880,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoFixture.AutoNSubstitute": "4.17.0",
|
"AutoFixture.AutoNSubstitute": "4.17.0",
|
||||||
"AutoFixture.Xunit2": "4.17.0",
|
"AutoFixture.Xunit2": "4.17.0",
|
||||||
"Common": "2023.3.0",
|
"Common": "2023.4.3",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
||||||
"Microsoft.NET.Test.Sdk": "17.1.0",
|
"Microsoft.NET.Test.Sdk": "17.1.0",
|
||||||
"Moq": "4.17.2",
|
"Moq": "4.17.2",
|
||||||
|
@ -3144,7 +3144,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoFixture.AutoNSubstitute": "4.17.0",
|
"AutoFixture.AutoNSubstitute": "4.17.0",
|
||||||
"AutoFixture.Xunit2": "4.17.0",
|
"AutoFixture.Xunit2": "4.17.0",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
||||||
"Microsoft.NET.Test.Sdk": "17.1.0",
|
"Microsoft.NET.Test.Sdk": "17.1.0",
|
||||||
"NSubstitute": "4.3.0",
|
"NSubstitute": "4.3.0",
|
||||||
@ -3196,15 +3196,15 @@
|
|||||||
"identity": {
|
"identity": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"SharedWeb": "2023.3.0",
|
"SharedWeb": "2023.4.3",
|
||||||
"Swashbuckle.AspNetCore.SwaggerGen": "6.5.0"
|
"Swashbuckle.AspNetCore.SwaggerGen": "6.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3212,7 +3212,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -3224,8 +3224,8 @@
|
|||||||
"integrationtestcommon": {
|
"integrationtestcommon": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Common": "2023.3.0",
|
"Common": "2023.4.3",
|
||||||
"Identity": "2023.3.0",
|
"Identity": "2023.4.3",
|
||||||
"Microsoft.AspNetCore.Mvc.Testing": "6.0.5",
|
"Microsoft.AspNetCore.Mvc.Testing": "6.0.5",
|
||||||
"Microsoft.EntityFrameworkCore.InMemory": "6.0.5",
|
"Microsoft.EntityFrameworkCore.InMemory": "6.0.5",
|
||||||
"Microsoft.Extensions.Configuration": "6.0.1"
|
"Microsoft.Extensions.Configuration": "6.0.1"
|
||||||
@ -3234,16 +3234,16 @@
|
|||||||
"scim": {
|
"scim": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"SharedWeb": "2023.3.0"
|
"SharedWeb": "2023.4.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2989,7 +2989,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoFixture.AutoNSubstitute": "4.17.0",
|
"AutoFixture.AutoNSubstitute": "4.17.0",
|
||||||
"AutoFixture.Xunit2": "4.17.0",
|
"AutoFixture.Xunit2": "4.17.0",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
||||||
"Microsoft.NET.Test.Sdk": "17.1.0",
|
"Microsoft.NET.Test.Sdk": "17.1.0",
|
||||||
"NSubstitute": "4.3.0",
|
"NSubstitute": "4.3.0",
|
||||||
@ -3041,7 +3041,7 @@
|
|||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3049,7 +3049,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -3061,16 +3061,16 @@
|
|||||||
"scim": {
|
"scim": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"SharedWeb": "2023.3.0"
|
"SharedWeb": "2023.4.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
COMPOSE_PROJECT_NAME=bitwarden
|
|
||||||
REGISTRY=bitwarden
|
|
||||||
TAG=dev
|
|
@ -1,283 +0,0 @@
|
|||||||
# syntax = docker/dockerfile:1.2
|
|
||||||
###############################################
|
|
||||||
# Build stage #
|
|
||||||
###############################################
|
|
||||||
FROM --platform=$BUILDPLATFORM debian AS web-setup
|
|
||||||
|
|
||||||
# Add packages
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
curl \
|
|
||||||
jq \
|
|
||||||
unzip \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
WORKDIR /tmp
|
|
||||||
|
|
||||||
# Download tags from 'clients' repository
|
|
||||||
RUN --mount=type=secret,id=GH_PAT,target=/etc/secrets/GH_PAT if [ -e "/etc/secrets/GH_PAT" ]; then \
|
|
||||||
curl --header "Authorization: token $(cat /etc/secrets/GH_PAT)" \
|
|
||||||
https://api.github.com/repos/bitwarden/clients/git/refs/tags --output tags.json ; else \
|
|
||||||
curl https://api.github.com/repos/bitwarden/clients/git/refs/tags --output tags.json ; fi
|
|
||||||
|
|
||||||
RUN cat tags.json
|
|
||||||
|
|
||||||
# Grab last tag/release of the 'web' client
|
|
||||||
RUN cat tags.json | jq -r 'last(.[] | select(.ref|test("refs/tags/web-v[0-9]{4}.[0-9]{1,2}.[0-9]+"))) | .ref | split("/")[2]' > tag.txt
|
|
||||||
|
|
||||||
# Extract the version of the 'web' client
|
|
||||||
RUN cat tag.txt | grep -o -E "[0-9]{4}\.[0-9]{1,2}\.[0-9]+" > version.txt
|
|
||||||
|
|
||||||
# Download the built release artifact for the 'web' client
|
|
||||||
RUN TAG=$(cat tag.txt) \
|
|
||||||
&& VERSION=$(cat version.txt) \
|
|
||||||
&& curl -L https://github.com/bitwarden/clients/releases/download/$TAG/web-$VERSION-selfhosted-COMMERCIAL.zip -O
|
|
||||||
|
|
||||||
# Unzip the 'web' client to /tmp/build
|
|
||||||
RUN VERSION=$(cat version.txt) \
|
|
||||||
&& unzip web-$VERSION-selfhosted-COMMERCIAL.zip
|
|
||||||
|
|
||||||
###############################################
|
|
||||||
# Build stage #
|
|
||||||
###############################################
|
|
||||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:6.0 AS dotnet-build
|
|
||||||
|
|
||||||
# Docker buildx supplies the value for this arg
|
|
||||||
ARG TARGETPLATFORM
|
|
||||||
|
|
||||||
# Determine proper runtime value for .NET
|
|
||||||
# We put the value in a file to be read by later layers.
|
|
||||||
RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \
|
|
||||||
RID=linux-x64 ; \
|
|
||||||
elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
|
|
||||||
RID=linux-arm64 ; \
|
|
||||||
elif [ "$TARGETPLATFORM" = "linux/arm/v7" ]; then \
|
|
||||||
RID=linux-arm ; \
|
|
||||||
fi \
|
|
||||||
&& echo "RID=$RID" > /tmp/rid.txt
|
|
||||||
|
|
||||||
# Add packages
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
npm \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN npm install -g gulp
|
|
||||||
|
|
||||||
# Copy csproj files as distinct layers
|
|
||||||
WORKDIR /source
|
|
||||||
COPY src/Admin/*.csproj ./src/Admin/
|
|
||||||
COPY src/Api/*.csproj ./src/Api/
|
|
||||||
COPY src/Events/*.csproj ./src/Events/
|
|
||||||
COPY src/Icons/*.csproj ./src/Icons/
|
|
||||||
COPY src/Identity/*.csproj ./src/Identity/
|
|
||||||
COPY src/Notifications/*.csproj ./src/Notifications/
|
|
||||||
COPY bitwarden_license/src/Sso/*.csproj ./bitwarden_license/src/Sso/
|
|
||||||
COPY bitwarden_license/src/Scim/*.csproj ./bitwarden_license/src/Scim/
|
|
||||||
COPY src/Core/*.csproj ./src/Core/
|
|
||||||
COPY src/Infrastructure.Dapper/*.csproj ./src/Infrastructure.Dapper/
|
|
||||||
COPY src/Infrastructure.EntityFramework/*.csproj ./src/Infrastructure.EntityFramework/
|
|
||||||
COPY src/SharedWeb/*.csproj ./src/SharedWeb/
|
|
||||||
COPY util/Migrator/*.csproj ./util/Migrator/
|
|
||||||
COPY util/MySqlMigrations/*.csproj ./util/MySqlMigrations/
|
|
||||||
COPY util/PostgresMigrations/*.csproj ./util/PostgresMigrations/
|
|
||||||
COPY util/SqliteMigrations/*.csproj ./util/SqliteMigrations/
|
|
||||||
COPY bitwarden_license/src/Commercial.Core/*.csproj ./bitwarden_license/src/Commercial.Core/
|
|
||||||
COPY bitwarden_license/src/Commercial.Infrastructure.EntityFramework/*.csproj ./bitwarden_license/src/Commercial.Infrastructure.EntityFramework/
|
|
||||||
COPY Directory.Build.props .
|
|
||||||
|
|
||||||
# Restore Admin project dependencies and tools
|
|
||||||
WORKDIR /source/src/Admin
|
|
||||||
RUN . /tmp/rid.txt && dotnet restore -r $RID
|
|
||||||
|
|
||||||
# Restore Api project dependencies and tools
|
|
||||||
WORKDIR /source/src/Api
|
|
||||||
RUN . /tmp/rid.txt && dotnet restore -r $RID
|
|
||||||
|
|
||||||
# Restore Events project dependencies and tools
|
|
||||||
WORKDIR /source/src/Events
|
|
||||||
RUN . /tmp/rid.txt && dotnet restore -r $RID
|
|
||||||
|
|
||||||
# Restore Icons project dependencies and tools
|
|
||||||
WORKDIR /source/src/Icons
|
|
||||||
RUN . /tmp/rid.txt && dotnet restore -r $RID
|
|
||||||
|
|
||||||
# Restore Identity project dependencies and tools
|
|
||||||
WORKDIR /source/src/Identity
|
|
||||||
RUN . /tmp/rid.txt && dotnet restore -r $RID
|
|
||||||
|
|
||||||
# Restore Notifications project dependencies and tools
|
|
||||||
WORKDIR /source/src/Notifications
|
|
||||||
RUN . /tmp/rid.txt && dotnet restore -r $RID
|
|
||||||
|
|
||||||
# Restore Sso project dependencies and tools
|
|
||||||
WORKDIR /source/bitwarden_license/src/Sso
|
|
||||||
RUN . /tmp/rid.txt && dotnet restore -r $RID
|
|
||||||
|
|
||||||
# Restore Scim project dependencies and tools
|
|
||||||
WORKDIR /source/bitwarden_license/src/Scim
|
|
||||||
RUN . /tmp/rid.txt && dotnet restore -r $RID
|
|
||||||
|
|
||||||
# Copy required project files
|
|
||||||
WORKDIR /source
|
|
||||||
COPY src/Admin/. ./src/Admin/
|
|
||||||
COPY src/Api/. ./src/Api/
|
|
||||||
COPY src/Events/. ./src/Events/
|
|
||||||
COPY src/Icons/. ./src/Icons/
|
|
||||||
COPY src/Identity/. ./src/Identity/
|
|
||||||
COPY src/Notifications/. ./src/Notifications/
|
|
||||||
COPY bitwarden_license/src/Sso/. ./bitwarden_license/src/Sso/
|
|
||||||
COPY bitwarden_license/src/Scim/. ./bitwarden_license/src/Scim/
|
|
||||||
COPY src/Core/. ./src/Core/
|
|
||||||
COPY src/Infrastructure.Dapper/. ./src/Infrastructure.Dapper/
|
|
||||||
COPY src/Infrastructure.EntityFramework/. ./src/Infrastructure.EntityFramework/
|
|
||||||
COPY src/SharedWeb/. ./src/SharedWeb/
|
|
||||||
COPY util/Migrator/. ./util/Migrator/
|
|
||||||
COPY util/MySqlMigrations/. ./util/MySqlMigrations/
|
|
||||||
COPY util/PostgresMigrations/. ./util/PostgresMigrations/
|
|
||||||
COPY util/SqliteMigrations/. ./util/SqliteMigrations/
|
|
||||||
COPY util/EfShared/. ./util/EfShared/
|
|
||||||
COPY bitwarden_license/src/Commercial.Core/. ./bitwarden_license/src/Commercial.Core/
|
|
||||||
COPY bitwarden_license/src/Commercial.Infrastructure.EntityFramework/. ./bitwarden_license/src/Commercial.Infrastructure.EntityFramework/
|
|
||||||
COPY .git/. ./.git/
|
|
||||||
|
|
||||||
# Build Admin app
|
|
||||||
WORKDIR /source/src/Admin
|
|
||||||
RUN npm install
|
|
||||||
RUN gulp --gulpfile "gulpfile.js" build
|
|
||||||
RUN . /tmp/rid.txt && dotnet publish -c release -o /app/Admin --no-restore --no-self-contained -r $RID
|
|
||||||
|
|
||||||
# Build Api app
|
|
||||||
WORKDIR /source/src/Api
|
|
||||||
RUN . /tmp/rid.txt && dotnet publish -c release -o /app/Api --no-restore --no-self-contained -r $RID
|
|
||||||
|
|
||||||
# Build Events app
|
|
||||||
WORKDIR /source/src/Events
|
|
||||||
RUN . /tmp/rid.txt && dotnet publish -c release -o /app/Events --no-restore --no-self-contained -r $RID
|
|
||||||
|
|
||||||
# Build Icons app
|
|
||||||
WORKDIR /source/src/Icons
|
|
||||||
RUN . /tmp/rid.txt && dotnet publish -c release -o /app/Icons --no-restore --no-self-contained -r $RID
|
|
||||||
|
|
||||||
# Build Identity app
|
|
||||||
WORKDIR /source/src/Identity
|
|
||||||
RUN . /tmp/rid.txt && dotnet publish -c release -o /app/Identity --no-restore --no-self-contained -r $RID
|
|
||||||
|
|
||||||
# Build Notifications app
|
|
||||||
WORKDIR /source/src/Notifications
|
|
||||||
RUN . /tmp/rid.txt && dotnet publish -c release -o /app/Notifications --no-restore --no-self-contained -r $RID
|
|
||||||
|
|
||||||
# Build Sso app
|
|
||||||
WORKDIR /source/bitwarden_license/src/Sso
|
|
||||||
RUN npm install
|
|
||||||
RUN gulp --gulpfile "gulpfile.js" build
|
|
||||||
RUN . /tmp/rid.txt && dotnet publish -c release -o /app/Sso --no-restore --no-self-contained -r $RID
|
|
||||||
|
|
||||||
# Build Scim app
|
|
||||||
WORKDIR /source/bitwarden_license/src/Scim
|
|
||||||
RUN . /tmp/rid.txt && dotnet publish -c release -o /app/Scim --no-restore --no-self-contained -r $RID
|
|
||||||
|
|
||||||
###############################################
|
|
||||||
# App stage #
|
|
||||||
###############################################
|
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0
|
|
||||||
ARG TARGETPLATFORM
|
|
||||||
LABEL com.bitwarden.product="bitwarden"
|
|
||||||
LABEL com.bitwarden.project="unified"
|
|
||||||
ENV ASPNETCORE_ENVIRONMENT=Production
|
|
||||||
ENV BW_ENABLE_ADMIN=true
|
|
||||||
ENV BW_ENABLE_API=true
|
|
||||||
ENV BW_ENABLE_EVENTS=false
|
|
||||||
ENV BW_ENABLE_ICONS=true
|
|
||||||
ENV BW_ENABLE_IDENTITY=true
|
|
||||||
ENV BW_ENABLE_NOTIFICATIONS=true
|
|
||||||
ENV BW_ENABLE_SCIM=false
|
|
||||||
ENV BW_ENABLE_SSO=false
|
|
||||||
ENV BW_DB_FILE="/etc/bitwarden/vault.db"
|
|
||||||
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
|
|
||||||
ENV globalSettings__selfHosted="true"
|
|
||||||
ENV globalSettings__unifiedDeployment="true"
|
|
||||||
ENV globalSettings__pushRelayBaseUri="https://push.bitwarden.com"
|
|
||||||
ENV globalSettings__baseServiceUri__internalAdmin="http://localhost:5000"
|
|
||||||
ENV globalSettings__baseServiceUri__internalApi="http://localhost:5001"
|
|
||||||
ENV globalSettings__baseServiceUri__internalEvents="http://localhost:5003"
|
|
||||||
ENV globalSettings__baseServiceUri__internalIcons="http://localhost:5004"
|
|
||||||
ENV globalSettings__baseServiceUri__internalIdentity="http://localhost:5005"
|
|
||||||
ENV globalSettings__baseServiceUri__internalNotifications="http://localhost:5006"
|
|
||||||
ENV globalSettings__baseServiceUri__internalSso="http://localhost:5007"
|
|
||||||
ENV globalSettings__baseServiceUri__internalScim="http://localhost:5002"
|
|
||||||
ENV globalSettings__baseServiceUri__internalVault="http://localhost:8080"
|
|
||||||
ENV globalSettings__identityServer__certificatePassword="default_cert_password"
|
|
||||||
ENV globalSettings__dataProtection__directory="/etc/bitwarden/data-protection"
|
|
||||||
ENV globalSettings__attachment__baseDirectory="/etc/bitwarden/attachments"
|
|
||||||
ENV globalSettings__send__baseDirectory="/etc/bitwarden/attachments/send"
|
|
||||||
ENV globalSettings__licenseDirectory="/etc/bitwarden/licenses"
|
|
||||||
ENV globalSettings__logDirectoryByProject="false"
|
|
||||||
ENV globalSettings__logRollBySizeLimit="1073741824"
|
|
||||||
|
|
||||||
# Add packages
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
curl \
|
|
||||||
nginx \
|
|
||||||
openssl \
|
|
||||||
supervisor \
|
|
||||||
tzdata \
|
|
||||||
unzip \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Create required directories
|
|
||||||
RUN mkdir -p /etc/bitwarden/attachments/send
|
|
||||||
RUN mkdir -p /etc/bitwarden/data-protection
|
|
||||||
RUN mkdir -p /etc/bitwarden/licenses
|
|
||||||
RUN mkdir -p /etc/bitwarden/logs
|
|
||||||
RUN mkdir -p /etc/supervisor
|
|
||||||
RUN mkdir -p /etc/supervisor.d
|
|
||||||
RUN mkdir -p /var/log/bitwarden
|
|
||||||
RUN mkdir -p /var/log/nginx/logs
|
|
||||||
RUN mkdir -p /etc/nginx/http.d
|
|
||||||
RUN mkdir -p /var/run/nginx
|
|
||||||
RUN mkdir -p /var/lib/nginx/tmp
|
|
||||||
RUN touch /var/run/nginx/nginx.pid
|
|
||||||
RUN mkdir -p /app
|
|
||||||
|
|
||||||
# Copy all apps from dotnet-build stage
|
|
||||||
WORKDIR /app
|
|
||||||
COPY --from=dotnet-build /app ./
|
|
||||||
|
|
||||||
# Copy Web files from web-setup stage
|
|
||||||
COPY --from=web-setup /tmp/build /app/Web
|
|
||||||
|
|
||||||
# Set up supervisord
|
|
||||||
COPY docker-unified/supervisord/*.ini /etc/supervisor.d/
|
|
||||||
COPY docker-unified/supervisord/supervisord.conf /etc/supervisor/supervisord.conf
|
|
||||||
RUN rm -f /etc/supervisord.conf
|
|
||||||
|
|
||||||
# Set up nginx
|
|
||||||
COPY docker-unified/nginx/nginx.conf /etc/nginx
|
|
||||||
COPY docker-unified/nginx/proxy.conf /etc/nginx
|
|
||||||
COPY docker-unified/nginx/mime.types /etc/nginx
|
|
||||||
COPY docker-unified/nginx/security-headers.conf /etc/nginx
|
|
||||||
COPY docker-unified/nginx/security-headers-ssl.conf /etc/nginx
|
|
||||||
COPY docker-unified/nginx/logrotate.sh /
|
|
||||||
RUN chmod +x /logrotate.sh
|
|
||||||
|
|
||||||
# Copy configuration templates
|
|
||||||
COPY docker-unified/hbs/nginx-config.hbs /etc/hbs/
|
|
||||||
COPY docker-unified/hbs/app-id.hbs /etc/hbs/
|
|
||||||
COPY docker-unified/hbs/config.yaml /etc/hbs/
|
|
||||||
|
|
||||||
# Download hbs tool for generating final configurations
|
|
||||||
RUN if [ "$TARGETPLATFORM" = "linux/amd64" ] ; then curl -L --output hbs.zip https://github.com/bitwarden/Handlebars.conf/releases/download/v1.3.0/hbs_linux-x64_dotnet.zip; fi
|
|
||||||
RUN if [ "$TARGETPLATFORM" = "linux/arm/v7" ] ; then curl -L --output hbs.zip https://github.com/bitwarden/Handlebars.conf/releases/download/v1.3.0/hbs_linux-armv7_dotnet.zip; fi
|
|
||||||
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ] ; then curl -L --output hbs.zip https://github.com/bitwarden/Handlebars.conf/releases/download/v1.3.0/hbs_linux-arm64_dotnet.zip; fi
|
|
||||||
|
|
||||||
# Extract hbs
|
|
||||||
RUN unzip hbs.zip -d /usr/local/bin && rm hbs.zip
|
|
||||||
RUN chmod +x /usr/local/bin/hbs
|
|
||||||
|
|
||||||
# Copy entrypoint script and make it executable
|
|
||||||
COPY docker-unified/entrypoint.sh /entrypoint.sh
|
|
||||||
RUN chmod +x /entrypoint.sh
|
|
||||||
|
|
||||||
VOLUME ["/etc/bitwarden"]
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
ENTRYPOINT ["/entrypoint.sh"]
|
|
@ -1,56 +0,0 @@
|
|||||||
---
|
|
||||||
version: "3.8"
|
|
||||||
|
|
||||||
services:
|
|
||||||
bitwarden:
|
|
||||||
depends_on:
|
|
||||||
- db
|
|
||||||
env_file:
|
|
||||||
- settings.env
|
|
||||||
image: ${REGISTRY:-bitwarden}/self-host:${TAG:-beta}
|
|
||||||
restart: always
|
|
||||||
ports:
|
|
||||||
- "80:8080"
|
|
||||||
- "443:8443"
|
|
||||||
volumes:
|
|
||||||
- bitwarden:/etc/bitwarden
|
|
||||||
- logs:/var/log/bitwarden
|
|
||||||
|
|
||||||
# MariaDB Example
|
|
||||||
db:
|
|
||||||
environment:
|
|
||||||
MARIADB_USER: "bitwarden"
|
|
||||||
MARIADB_PASSWORD: "super_strong_password"
|
|
||||||
MARIADB_DATABASE: "bitwarden_vault"
|
|
||||||
MARIADB_RANDOM_ROOT_PASSWORD: "true"
|
|
||||||
image: mariadb:10
|
|
||||||
restart: always
|
|
||||||
volumes:
|
|
||||||
- data:/var/lib/mysql
|
|
||||||
|
|
||||||
# PostgreSQL Example
|
|
||||||
# db:
|
|
||||||
# environment:
|
|
||||||
# POSTGRES_USER: "bitwarden"
|
|
||||||
# POSTGRES_PASSWORD: "super_strong_password"
|
|
||||||
# POSTGRES_DB: "bitwarden_vault"
|
|
||||||
# image: postgres:14
|
|
||||||
# restart: always
|
|
||||||
# volumes:
|
|
||||||
# - data:/var/lib/postgresql/data
|
|
||||||
|
|
||||||
# MS SQL Server Example
|
|
||||||
# Docs: https://learn.microsoft.com/en-us/sql/linux/sql-server-linux-docker-container-deployment
|
|
||||||
# db:
|
|
||||||
# environment:
|
|
||||||
# MSSQL_SA_PASSWORD: "super_strong_password"
|
|
||||||
# ACCEPT_EULA: Y
|
|
||||||
# image: mcr.microsoft.com/mssql/server:2019-latest
|
|
||||||
# restart: always
|
|
||||||
# volumes:
|
|
||||||
# - data:/var/opt/mssql
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
bitwarden:
|
|
||||||
logs:
|
|
||||||
data:
|
|
@ -1,108 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Set up user group
|
|
||||||
PGID="${PGID:-1000}"
|
|
||||||
addgroup --gid $PGID bitwarden
|
|
||||||
|
|
||||||
# Set up user
|
|
||||||
PUID="${PUID:-1000}"
|
|
||||||
adduser --no-create-home --shell /bin/bash --disabled-password --uid $PUID --gid $PGID --gecos "" bitwarden
|
|
||||||
|
|
||||||
# Translate environment variables for application settings
|
|
||||||
VAULT_SERVICE_URI=https://$BW_DOMAIN
|
|
||||||
MYSQL_CONNECTION_STRING="server=$BW_DB_SERVER;port=${BW_DB_PORT:-3306};database=$BW_DB_DATABASE;user=$BW_DB_USERNAME;password=$BW_DB_PASSWORD"
|
|
||||||
POSTGRESQL_CONNECTION_STRING="Host=$BW_DB_SERVER;Port=${BW_DB_PORT:-5432};Database=$BW_DB_DATABASE;Username=$BW_DB_USERNAME;Password=$BW_DB_PASSWORD"
|
|
||||||
SQLSERVER_CONNECTION_STRING="Server=$BW_DB_SERVER,${BW_DB_PORT:-1433};Database=$BW_DB_DATABASE;User Id=$BW_DB_USERNAME;Password=$BW_DB_PASSWORD;Encrypt=True;TrustServerCertificate=True"
|
|
||||||
SQLITE_CONNECTION_STRING="Data Source=$BW_DB_FILE;"
|
|
||||||
INTERNAL_IDENTITY_KEY=$(openssl rand -hex 30)
|
|
||||||
OIDC_IDENTITY_CLIENT_KEY=$(openssl rand -hex 30)
|
|
||||||
DUO_AKEY=$(openssl rand -hex 30)
|
|
||||||
|
|
||||||
export globalSettings__baseServiceUri__vault=${globalSettings__baseServiceUri__vault:-$VAULT_SERVICE_URI}
|
|
||||||
export globalSettings__installation__id=$BW_INSTALLATION_ID
|
|
||||||
export globalSettings__installation__key=$BW_INSTALLATION_KEY
|
|
||||||
export globalSettings__internalIdentityKey=${globalSettings__internalIdentityKey:-$INTERNAL_IDENTITY_KEY}
|
|
||||||
export globalSettings__oidcIdentityClientKey=${globalSettings__oidcIdentityClientKey:-$OIDC_IDENTITY_CLIENT_KEY}
|
|
||||||
export globalSettings__duo__aKey=${globalSettings__duo__aKey:-$DUO_AKEY}
|
|
||||||
|
|
||||||
export globalSettings__databaseProvider=$BW_DB_PROVIDER
|
|
||||||
export globalSettings__mysql__connectionString=${globalSettings__mysql__connectionString:-$MYSQL_CONNECTION_STRING}
|
|
||||||
export globalSettings__postgreSql__connectionString=${globalSettings__postgreSql__connectionString:-$POSTGRESQL_CONNECTION_STRING}
|
|
||||||
export globalSettings__sqlServer__connectionString=${globalSettings__sqlServer__connectionString:-$SQLSERVER_CONNECTION_STRING}
|
|
||||||
export globalSettings__sqlite__connectionString=${globalSettings__sqlite__connectionString:-$SQLITE_CONNECTION_STRING}
|
|
||||||
|
|
||||||
if [ "$BW_ENABLE_SSL" = "true" ]; then
|
|
||||||
export globalSettings__baseServiceUri__internalVault=https://localhost:${BW_PORT_HTTPS:-8443}
|
|
||||||
else
|
|
||||||
export globalSettings__baseServiceUri__internalVault=http://localhost:${BW_PORT_HTTP:-8080}
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Generate Identity certificate
|
|
||||||
if [ ! -f /etc/bitwarden/identity.pfx ]; then
|
|
||||||
openssl req \
|
|
||||||
-x509 \
|
|
||||||
-newkey rsa:4096 \
|
|
||||||
-sha256 \
|
|
||||||
-nodes \
|
|
||||||
-keyout /etc/bitwarden/identity.key \
|
|
||||||
-out /etc/bitwarden/identity.crt \
|
|
||||||
-subj "/CN=Bitwarden IdentityServer" \
|
|
||||||
-days 36500
|
|
||||||
|
|
||||||
openssl pkcs12 \
|
|
||||||
-export \
|
|
||||||
-out /etc/bitwarden/identity.pfx \
|
|
||||||
-inkey /etc/bitwarden/identity.key \
|
|
||||||
-in /etc/bitwarden/identity.crt \
|
|
||||||
-passout pass:$globalSettings__identityServer__certificatePassword
|
|
||||||
|
|
||||||
rm /etc/bitwarden/identity.crt
|
|
||||||
rm /etc/bitwarden/identity.key
|
|
||||||
fi
|
|
||||||
|
|
||||||
cp /etc/bitwarden/identity.pfx /app/Identity/identity.pfx
|
|
||||||
cp /etc/bitwarden/identity.pfx /app/Sso/identity.pfx
|
|
||||||
|
|
||||||
# Generate SSL certificates
|
|
||||||
if [ "$BW_ENABLE_SSL" = "true" -a ! -f /etc/bitwarden/${BW_SSL_KEY:-ssl.key} ]; then
|
|
||||||
openssl req \
|
|
||||||
-x509 \
|
|
||||||
-newkey rsa:4096 \
|
|
||||||
-sha256 \
|
|
||||||
-nodes \
|
|
||||||
-days 36500 \
|
|
||||||
-keyout /etc/bitwarden/${BW_SSL_KEY:-ssl.key} \
|
|
||||||
-out /etc/bitwarden/${BW_SSL_CERT:-ssl.crt} \
|
|
||||||
-reqexts SAN \
|
|
||||||
-extensions SAN \
|
|
||||||
-config <(cat /usr/lib/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:${BW_DOMAIN:-localhost}\nbasicConstraints=CA:true")) \
|
|
||||||
-subj "/C=US/ST=California/L=Santa Barbara/O=Bitwarden Inc./OU=Bitwarden/CN=${BW_DOMAIN:-localhost}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Launch a loop to rotate nginx logs on a daily basis
|
|
||||||
/bin/sh -c "/logrotate.sh loop >/dev/null 2>&1 &"
|
|
||||||
|
|
||||||
/usr/local/bin/hbs
|
|
||||||
|
|
||||||
# Enable/Disable services
|
|
||||||
sed -i "s/autostart=true/autostart=${BW_ENABLE_ADMIN}/" /etc/supervisor.d/admin.ini
|
|
||||||
sed -i "s/autostart=true/autostart=${BW_ENABLE_API}/" /etc/supervisor.d/api.ini
|
|
||||||
sed -i "s/autostart=true/autostart=${BW_ENABLE_EVENTS}/" /etc/supervisor.d/events.ini
|
|
||||||
sed -i "s/autostart=true/autostart=${BW_ENABLE_ICONS}/" /etc/supervisor.d/icons.ini
|
|
||||||
sed -i "s/autostart=true/autostart=${BW_ENABLE_IDENTITY}/" /etc/supervisor.d/identity.ini
|
|
||||||
sed -i "s/autostart=true/autostart=${BW_ENABLE_NOTIFICATIONS}/" /etc/supervisor.d/notifications.ini
|
|
||||||
sed -i "s/autostart=true/autostart=${BW_ENABLE_SCIM}/" /etc/supervisor.d/scim.ini
|
|
||||||
sed -i "s/autostart=true/autostart=${BW_ENABLE_SSO}/" /etc/supervisor.d/sso.ini
|
|
||||||
|
|
||||||
chown -R $PUID:$PGID \
|
|
||||||
/app \
|
|
||||||
/etc/bitwarden \
|
|
||||||
/etc/nginx/http.d \
|
|
||||||
/etc/supervisor \
|
|
||||||
/etc/supervisor.d \
|
|
||||||
/var/lib/nginx \
|
|
||||||
/var/log \
|
|
||||||
/var/run/nginx \
|
|
||||||
/run
|
|
||||||
|
|
||||||
exec setpriv --reuid=$PUID --regid=$PGID --init-groups /usr/bin/supervisord
|
|
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"trustedFacets": [
|
|
||||||
{
|
|
||||||
"version": {
|
|
||||||
"major": 1,
|
|
||||||
"minor": 0
|
|
||||||
},
|
|
||||||
"ids": [
|
|
||||||
"{{{String.Coalesce env.globalSettings__baseServiceUri__vault "https://localhost"}}}",
|
|
||||||
"ios:bundle-id:com.8bit.bitwarden",
|
|
||||||
"android:apk-key-hash:dUGFzUzf3lmHSLBDBIv+WaFyZMI"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
helper_categories:
|
|
||||||
- String
|
|
||||||
templates:
|
|
||||||
- src: /etc/hbs/app-id.hbs
|
|
||||||
dest: /app/Web/app-id.json
|
|
||||||
- src: /etc/hbs/nginx-config.hbs
|
|
||||||
dest: /etc/nginx/http.d/bitwarden.conf
|
|
@ -1,196 +0,0 @@
|
|||||||
server {
|
|
||||||
listen {{{String.Coalesce env.BW_PORT_HTTP "8080"}}} default_server;
|
|
||||||
#listen [::]:{{{String.Coalesce env.BW_PORT_HTTP "8080"}}} default_server;
|
|
||||||
server_name {{{String.Coalesce env.BW_DOMAIN "localhost"}}};
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_SSL "true")}}
|
|
||||||
|
|
||||||
return 301 https://{{{String.Coalesce env.BW_DOMAIN "localhost"}}}:{{{String.Coalesce env.BW_PORT_HTTPS "8443"}}}$request_uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen {{{String.Coalesce env.BW_PORT_HTTPS "8443"}}} ssl http2;
|
|
||||||
#listen [::]:{{{String.Coalesce env.BW_PORT_HTTPS "8443"}}} ssl http2;
|
|
||||||
server_name {{{String.Coalesce env.BW_DOMAIN "localhost"}}};
|
|
||||||
|
|
||||||
ssl_certificate /etc/bitwarden/{{{String.Coalesce env.BW_SSL_CERT "ssl.crt"}}};
|
|
||||||
ssl_certificate_key /etc/bitwarden/{{{String.Coalesce env.BW_SSL_KEY "ssl.key"}}};
|
|
||||||
ssl_session_timeout 30m;
|
|
||||||
ssl_session_cache shared:SSL:20m;
|
|
||||||
ssl_session_tickets off;
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_SSL_DH "true")}}
|
|
||||||
|
|
||||||
# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
|
|
||||||
ssl_dhparam /etc/bitwarden/{{{String.Coalesce env.BW_SSL_DH_CERT "dh.pem"}}};
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
ssl_protocols {{{String.Coalesce env.BW_SSL_PROTOCOLS "TLSv1.2"}}};
|
|
||||||
ssl_ciphers "{{{String.Coalesce env.BW_SSL_CIPHERS "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"}}}";
|
|
||||||
# Enables server-side protection from BEAST attacks
|
|
||||||
ssl_prefer_server_ciphers on;
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_SSL_CA "true")}}
|
|
||||||
|
|
||||||
# OCSP Stapling ---
|
|
||||||
# Fetch OCSP records from URL in ssl_certificate and cache them
|
|
||||||
ssl_stapling on;
|
|
||||||
ssl_stapling_verify on;
|
|
||||||
|
|
||||||
# Verify chain of trust of OCSP response using Root CA and Intermediate certs
|
|
||||||
ssl_trusted_certificate /etc/bitwarden/{{{String.Coalesce env.BW_SSL_CA_CERT "ca.crt"}}};
|
|
||||||
resolver 1.1.1.1 1.0.0.1 9.9.9.9 149.112.112.112 valid=300s;
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
include /etc/nginx/security-headers-ssl.conf;
|
|
||||||
{{/if}}
|
|
||||||
include /etc/nginx/security-headers.conf;
|
|
||||||
{{#if (String.IsNotNullOrWhitespace env.BW_REAL_IPS)}}
|
|
||||||
|
|
||||||
{{#each (String.Split env.BW_REAL_IPS ",")}}
|
|
||||||
set_real_ip_from {{{String.Trim .}}};
|
|
||||||
{{/each}}
|
|
||||||
real_ip_header X-Forwarded-For;
|
|
||||||
real_ip_recursive on;
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
root /app/Web;
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_SSL "true")}}
|
|
||||||
include /etc/nginx/security-headers-ssl.conf;
|
|
||||||
{{/if}}
|
|
||||||
include /etc/nginx/security-headers.conf;
|
|
||||||
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-Robots-Tag "noindex, nofollow";
|
|
||||||
}
|
|
||||||
|
|
||||||
location /alive {
|
|
||||||
default_type text/plain;
|
|
||||||
return 200 $date_gmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = /app-id.json {
|
|
||||||
root /app/Web;
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_SSL "true")}}
|
|
||||||
include /etc/nginx/security-headers-ssl.conf;
|
|
||||||
{{/if}}
|
|
||||||
include /etc/nginx/security-headers.conf;
|
|
||||||
proxy_hide_header Content-Type;
|
|
||||||
add_header Content-Type $fido_content_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = /duo-connector.html {
|
|
||||||
root /app/Web;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = /webauthn-connector.html {
|
|
||||||
root /app/Web;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = /webauthn-fallback-connector.html {
|
|
||||||
root /app/Web;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = /sso-connector.html {
|
|
||||||
root /app/Web;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = /captcha-connector.html {
|
|
||||||
root /app/Web;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = /captcha-mobile-connector.html {
|
|
||||||
root /app/Web;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /attachments/ {
|
|
||||||
alias /etc/bitwarden/attachments/;
|
|
||||||
}
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_API "true")}}
|
|
||||||
|
|
||||||
location /api/ {
|
|
||||||
proxy_pass http://localhost:5001/;
|
|
||||||
}
|
|
||||||
{{/if}}
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_ICONS "true")}}
|
|
||||||
|
|
||||||
location /icons/ {
|
|
||||||
{{#if (String.Equal env.BW_ICONS_PROXY_TO_CLOUD "true")}}
|
|
||||||
proxy_pass https://icons.bitwarden.net/;
|
|
||||||
proxy_set_header Host icons.bitwarden.net;
|
|
||||||
proxy_set_header X-Forwarded-For $remote_addr;
|
|
||||||
proxy_ssl_server_name on;
|
|
||||||
{{else}}
|
|
||||||
proxy_pass http://localhost:5004/;
|
|
||||||
{{/if}}
|
|
||||||
}
|
|
||||||
{{/if}}
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_NOTIFICATIONS "true")}}
|
|
||||||
|
|
||||||
location /notifications/ {
|
|
||||||
proxy_pass http://localhost:5006/;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /notifications/hub {
|
|
||||||
proxy_pass http://localhost:5006/hub;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection $http_connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /notifications/anonymous-hub {
|
|
||||||
proxy_pass http://localhost:5006/anonymous-hub;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection $http_connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
{{/if}}
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_EVENTS "true")}}
|
|
||||||
|
|
||||||
location /events/ {
|
|
||||||
proxy_pass http://localhost:5003/;
|
|
||||||
}
|
|
||||||
{{/if}}
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_SSO "true")}}
|
|
||||||
|
|
||||||
location /sso {
|
|
||||||
proxy_pass http://localhost:5007;
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_SSL "true")}}
|
|
||||||
include /etc/nginx/security-headers-ssl.conf;
|
|
||||||
{{/if}}
|
|
||||||
include /etc/nginx/security-headers.conf;
|
|
||||||
add_header X-Frame-Options SAMEORIGIN;
|
|
||||||
}
|
|
||||||
{{/if}}
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_IDENTITY "true")}}
|
|
||||||
|
|
||||||
location /identity {
|
|
||||||
proxy_pass http://localhost:5005;
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_SSL "true")}}
|
|
||||||
include /etc/nginx/security-headers-ssl.conf;
|
|
||||||
{{/if}}
|
|
||||||
include /etc/nginx/security-headers.conf;
|
|
||||||
add_header X-Frame-Options SAMEORIGIN;
|
|
||||||
}
|
|
||||||
{{/if}}
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_ADMIN "true")}}
|
|
||||||
|
|
||||||
location /admin {
|
|
||||||
proxy_pass http://localhost:5000;
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_SSL "true")}}
|
|
||||||
include /etc/nginx/security-headers-ssl.conf;
|
|
||||||
{{/if}}
|
|
||||||
include /etc/nginx/security-headers.conf;
|
|
||||||
add_header X-Frame-Options SAMEORIGIN;
|
|
||||||
}
|
|
||||||
{{/if}}
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_SCIM "true")}}
|
|
||||||
|
|
||||||
location /scim/ {
|
|
||||||
proxy_pass http://localhost:5002/;
|
|
||||||
}
|
|
||||||
{{/if}}
|
|
||||||
{{#if (String.Equal env.BW_ENABLE_KEY_CONNECTOR "true")}}
|
|
||||||
|
|
||||||
location /key-connector/ {
|
|
||||||
proxy_pass {{{env.BW_KEY_CONNECTOR_INTERNAL_URL}}}/;
|
|
||||||
}
|
|
||||||
{{/if}}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
while true
|
|
||||||
do
|
|
||||||
[ "$1" = "loop" ] && sleep $((24 * 3600 - (`date +%_H` * 3600 + `date +%_M` * 60 + `date +%_S`)))
|
|
||||||
ts=$(date +%Y%m%d_%H%M%S)
|
|
||||||
mv /var/log/nginx/access.log /var/log/nginx/access.$ts.log
|
|
||||||
mv /var/log/nginx/error.log /var/log/nginx/error.$ts.log
|
|
||||||
kill -USR1 `cat /var/run/nginx/nginx.pid`
|
|
||||||
sleep 1
|
|
||||||
gzip /var/log/nginx/access.$ts.log
|
|
||||||
gzip /var/log/nginx/error.$ts.log
|
|
||||||
find /var/log/nginx/ -name "*.gz" -mtime +32 -delete
|
|
||||||
[ "$1" != "loop" ] && break
|
|
||||||
done
|
|
@ -1,138 +0,0 @@
|
|||||||
types {
|
|
||||||
|
|
||||||
# Data interchange
|
|
||||||
|
|
||||||
application/atom+xml atom;
|
|
||||||
application/json json map topojson;
|
|
||||||
application/ld+json jsonld;
|
|
||||||
application/rss+xml rss;
|
|
||||||
application/vnd.geo+json geojson;
|
|
||||||
application/xml rdf xml;
|
|
||||||
|
|
||||||
|
|
||||||
# JavaScript
|
|
||||||
|
|
||||||
# Normalize to standard type.
|
|
||||||
# https://tools.ietf.org/html/rfc4329#section-7.2
|
|
||||||
application/javascript js;
|
|
||||||
|
|
||||||
|
|
||||||
# Manifest files
|
|
||||||
|
|
||||||
application/manifest+json webmanifest;
|
|
||||||
application/x-web-app-manifest+json webapp;
|
|
||||||
text/cache-manifest appcache;
|
|
||||||
|
|
||||||
|
|
||||||
# Media files
|
|
||||||
|
|
||||||
audio/midi mid midi kar;
|
|
||||||
audio/mp4 aac f4a f4b m4a;
|
|
||||||
audio/mpeg mp3;
|
|
||||||
audio/ogg oga ogg opus;
|
|
||||||
audio/x-realaudio ra;
|
|
||||||
audio/x-wav wav;
|
|
||||||
image/bmp bmp;
|
|
||||||
image/gif gif;
|
|
||||||
image/jpeg jpeg jpg;
|
|
||||||
image/jxr jxr hdp wdp;
|
|
||||||
image/png png;
|
|
||||||
image/svg+xml svg svgz;
|
|
||||||
image/tiff tif tiff;
|
|
||||||
image/vnd.wap.wbmp wbmp;
|
|
||||||
image/webp webp;
|
|
||||||
image/x-jng jng;
|
|
||||||
video/3gpp 3gp 3gpp;
|
|
||||||
video/mp4 f4p f4v m4v mp4;
|
|
||||||
video/mpeg mpeg mpg;
|
|
||||||
video/ogg ogv;
|
|
||||||
video/quicktime mov;
|
|
||||||
video/webm webm;
|
|
||||||
video/x-flv flv;
|
|
||||||
video/x-mng mng;
|
|
||||||
video/x-ms-asf asf asx;
|
|
||||||
video/x-ms-wmv wmv;
|
|
||||||
video/x-msvideo avi;
|
|
||||||
|
|
||||||
# Serving `.ico` image files with a different media type
|
|
||||||
# prevents Internet Explorer from displaying then as images:
|
|
||||||
# https://github.com/h5bp/html5-boilerplate/commit/37b5fec090d00f38de64b591bcddcb205aadf8ee
|
|
||||||
|
|
||||||
image/x-icon cur ico;
|
|
||||||
|
|
||||||
|
|
||||||
# Microsoft Office
|
|
||||||
|
|
||||||
application/msword doc;
|
|
||||||
application/vnd.ms-excel xls;
|
|
||||||
application/vnd.ms-powerpoint ppt;
|
|
||||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document docx;
|
|
||||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx;
|
|
||||||
application/vnd.openxmlformats-officedocument.presentationml.presentation pptx;
|
|
||||||
|
|
||||||
|
|
||||||
# Web fonts
|
|
||||||
|
|
||||||
application/font-woff woff;
|
|
||||||
application/font-woff2 woff2;
|
|
||||||
application/vnd.ms-fontobject eot;
|
|
||||||
|
|
||||||
# Browsers usually ignore the font media types and simply sniff
|
|
||||||
# the bytes to figure out the font type.
|
|
||||||
# https://mimesniff.spec.whatwg.org/#matching-a-font-type-pattern
|
|
||||||
#
|
|
||||||
# However, Blink and WebKit based browsers will show a warning
|
|
||||||
# in the console if the following font types are served with any
|
|
||||||
# other media types.
|
|
||||||
|
|
||||||
application/x-font-ttf ttc ttf;
|
|
||||||
font/opentype otf;
|
|
||||||
|
|
||||||
|
|
||||||
# Other
|
|
||||||
|
|
||||||
application/java-archive ear jar war;
|
|
||||||
application/mac-binhex40 hqx;
|
|
||||||
application/octet-stream bin deb dll dmg exe img iso msi msm msp safariextz;
|
|
||||||
application/pdf pdf;
|
|
||||||
application/postscript ai eps ps;
|
|
||||||
application/rtf rtf;
|
|
||||||
application/vnd.google-earth.kml+xml kml;
|
|
||||||
application/vnd.google-earth.kmz kmz;
|
|
||||||
application/vnd.wap.wmlc wmlc;
|
|
||||||
application/x-7z-compressed 7z;
|
|
||||||
application/x-bb-appworld bbaw;
|
|
||||||
application/x-bittorrent torrent;
|
|
||||||
application/x-chrome-extension crx;
|
|
||||||
application/x-cocoa cco;
|
|
||||||
application/x-java-archive-diff jardiff;
|
|
||||||
application/x-java-jnlp-file jnlp;
|
|
||||||
application/x-makeself run;
|
|
||||||
application/x-opera-extension oex;
|
|
||||||
application/x-perl pl pm;
|
|
||||||
application/x-pilot pdb prc;
|
|
||||||
application/x-rar-compressed rar;
|
|
||||||
application/x-redhat-package-manager rpm;
|
|
||||||
application/x-sea sea;
|
|
||||||
application/x-shockwave-flash swf;
|
|
||||||
application/x-stuffit sit;
|
|
||||||
application/x-tcl tcl tk;
|
|
||||||
application/x-x509-ca-cert crt der pem;
|
|
||||||
application/x-xpinstall xpi;
|
|
||||||
application/xhtml+xml xhtml;
|
|
||||||
application/xslt+xml xsl;
|
|
||||||
application/zip zip;
|
|
||||||
text/css css;
|
|
||||||
text/csv csv;
|
|
||||||
text/html htm html shtml;
|
|
||||||
text/markdown md;
|
|
||||||
text/mathml mml;
|
|
||||||
text/plain txt;
|
|
||||||
text/vcard vcard vcf;
|
|
||||||
text/vnd.rim.location.xloc xloc;
|
|
||||||
text/vnd.sun.j2me.app-descriptor jad;
|
|
||||||
text/vnd.wap.wml wml;
|
|
||||||
text/vtt vtt;
|
|
||||||
text/x-component htc;
|
|
||||||
|
|
||||||
}
|
|
@ -1,147 +0,0 @@
|
|||||||
# nginx Configuration File
|
|
||||||
# http://wiki.nginx.org/Configuration
|
|
||||||
|
|
||||||
daemon off;
|
|
||||||
|
|
||||||
# Run as a less privileged user for security reasons.
|
|
||||||
# user www www;
|
|
||||||
|
|
||||||
# How many worker threads to run;
|
|
||||||
# "auto" sets it to the number of CPU cores available in the system, and
|
|
||||||
# offers the best performance. Don't set it higher than the number of CPU
|
|
||||||
# cores if changing this parameter.
|
|
||||||
|
|
||||||
# The maximum number of connections for Nginx is calculated by:
|
|
||||||
# max_clients = worker_processes * worker_connections
|
|
||||||
worker_processes auto;
|
|
||||||
|
|
||||||
# Maximum open file descriptors per process;
|
|
||||||
# should be > worker_connections.
|
|
||||||
worker_rlimit_nofile 8192;
|
|
||||||
|
|
||||||
events {
|
|
||||||
# When you need > 8000 * cpu_cores connections, you start optimizing your OS,
|
|
||||||
# and this is probably the point at which you hire people who are smarter than
|
|
||||||
# you, as this is *a lot* of requests.
|
|
||||||
worker_connections 8000;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Default error log file
|
|
||||||
# (this is only used when you don't override error_log on a server{} level)
|
|
||||||
error_log /var/log/nginx/error.log warn;
|
|
||||||
pid /var/run/nginx/nginx.pid;
|
|
||||||
|
|
||||||
http {
|
|
||||||
# Include proxy and server configuration.
|
|
||||||
include /etc/nginx/proxy.conf;
|
|
||||||
include /etc/nginx/http.d/bitwarden.conf;
|
|
||||||
|
|
||||||
# Hide nginx version information.
|
|
||||||
server_tokens off;
|
|
||||||
|
|
||||||
# Define the MIME types for files.
|
|
||||||
include /etc/nginx/mime.types;
|
|
||||||
default_type application/octet-stream;
|
|
||||||
|
|
||||||
# Update charset_types to match updated mime.types.
|
|
||||||
# text/html is always included by charset module.
|
|
||||||
# Default: text/html text/xml text/plain text/vnd.wap.wml application/javascript application/rss+xml
|
|
||||||
charset_types
|
|
||||||
text/css
|
|
||||||
text/plain
|
|
||||||
text/vnd.wap.wml
|
|
||||||
application/javascript
|
|
||||||
application/json
|
|
||||||
application/rss+xml
|
|
||||||
application/xml;
|
|
||||||
|
|
||||||
# Format to use in log files
|
|
||||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
|
||||||
'$status $body_bytes_sent "$http_referer" '
|
|
||||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
|
||||||
|
|
||||||
# Default log file
|
|
||||||
# (this is only used when you don't override access_log on a server{} level)
|
|
||||||
access_log /var/log/nginx/access.log main;
|
|
||||||
|
|
||||||
# How long to allow each connection to stay idle; longer values are better
|
|
||||||
# for each individual client, particularly for SSL, but means that worker
|
|
||||||
# connections are tied up longer. (Default: 65)
|
|
||||||
keepalive_timeout 20;
|
|
||||||
|
|
||||||
# Speed up file transfers by using sendfile() to copy directly
|
|
||||||
# between descriptors rather than using read()/write().
|
|
||||||
# For performance reasons, on FreeBSD systems w/ ZFS
|
|
||||||
# this option should be disabled as ZFS's ARC caches
|
|
||||||
# frequently used files in RAM by default.
|
|
||||||
sendfile on;
|
|
||||||
|
|
||||||
# Tell Nginx not to send out partial frames; this increases throughput
|
|
||||||
# since TCP frames are filled up before being sent out. (adds TCP_CORK)
|
|
||||||
tcp_nopush on;
|
|
||||||
|
|
||||||
|
|
||||||
# Compression
|
|
||||||
|
|
||||||
# Enable Gzip compressed.
|
|
||||||
gzip on;
|
|
||||||
|
|
||||||
# Compression level (1-9).
|
|
||||||
# 5 is a perfect compromise between size and cpu usage, offering about
|
|
||||||
# 75% reduction for most ascii files (almost identical to level 9).
|
|
||||||
gzip_comp_level 5;
|
|
||||||
|
|
||||||
# Don't compress anything that's already small and unlikely to shrink much
|
|
||||||
# if at all (the default is 20 bytes, which is bad as that usually leads to
|
|
||||||
# larger files after gzipping).
|
|
||||||
gzip_min_length 256;
|
|
||||||
|
|
||||||
# Compress data even for clients that are connecting to us via proxies,
|
|
||||||
# identified by the "Via" header (required for CloudFront).
|
|
||||||
gzip_proxied any;
|
|
||||||
|
|
||||||
# Tell proxies to cache both the gzipped and regular version of a resource
|
|
||||||
# whenever the client's Accept-Encoding capabilities header varies;
|
|
||||||
# Avoids the issue where a non-gzip capable client (which is extremely rare
|
|
||||||
# today) would display gibberish if their proxy gave them the gzipped version.
|
|
||||||
gzip_vary on;
|
|
||||||
|
|
||||||
# Compress all output labeled with one of the following MIME-types.
|
|
||||||
gzip_types
|
|
||||||
application/atom+xml
|
|
||||||
application/javascript
|
|
||||||
application/json
|
|
||||||
application/ld+json
|
|
||||||
application/manifest+json
|
|
||||||
application/rss+xml
|
|
||||||
application/vnd.geo+json
|
|
||||||
application/vnd.ms-fontobject
|
|
||||||
application/x-font-ttf
|
|
||||||
application/x-web-app-manifest+json
|
|
||||||
application/xhtml+xml
|
|
||||||
application/xml
|
|
||||||
font/opentype
|
|
||||||
image/bmp
|
|
||||||
image/svg+xml
|
|
||||||
image/x-icon
|
|
||||||
text/cache-manifest
|
|
||||||
text/css
|
|
||||||
text/plain
|
|
||||||
text/vcard
|
|
||||||
text/vnd.rim.location.xloc
|
|
||||||
text/vtt
|
|
||||||
text/x-component
|
|
||||||
text/x-cross-domain-policy;
|
|
||||||
# text/html is always compressed by HttpGzipModule
|
|
||||||
|
|
||||||
# This should be turned on if you are going to have pre-compressed copies (.gz) of
|
|
||||||
# static files available. If not it should be left off as it will cause extra I/O
|
|
||||||
# for the check. It is best if you enable this in a location{} block for
|
|
||||||
# a specific directory, or on an individual server{} level.
|
|
||||||
# gzip_static on;
|
|
||||||
|
|
||||||
# Content type for FIDO U2F facets
|
|
||||||
map $uri $fido_content_type {
|
|
||||||
default "application/fido.trusted-apps+json";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
proxy_redirect off;
|
|
||||||
|
|
||||||
map $http_host $upstream_host {
|
|
||||||
default "$host";
|
|
||||||
~. "$http_host";
|
|
||||||
}
|
|
||||||
proxy_set_header Host $upstream_host;
|
|
||||||
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
|
|
||||||
map $http_x_forwarded_proto $upstream_scheme {
|
|
||||||
default "$scheme";
|
|
||||||
~. "$http_x_forwarded_proto";
|
|
||||||
}
|
|
||||||
proxy_set_header X-Url-Scheme $upstream_scheme;
|
|
||||||
proxy_set_header X-Forwarded-Proto $upstream_scheme;
|
|
||||||
|
|
||||||
client_max_body_size 505m;
|
|
||||||
client_body_buffer_size 128k;
|
|
||||||
proxy_connect_timeout 90;
|
|
||||||
proxy_send_timeout 90;
|
|
||||||
proxy_read_timeout 90;
|
|
||||||
proxy_buffer_size 128k;
|
|
||||||
proxy_buffers 4 256k;
|
|
||||||
proxy_busy_buffers_size 256k;
|
|
||||||
large_client_header_buffers 4 32k;
|
|
@ -1,2 +0,0 @@
|
|||||||
# This will enforce HTTP browsing into HTTPS and avoid ssl stripping attack. 6 months age
|
|
||||||
add_header Strict-Transport-Security max-age=15768000;
|
|
@ -1,3 +0,0 @@
|
|||||||
add_header Referrer-Policy same-origin;
|
|
||||||
add_header X-Content-Type-Options nosniff;
|
|
||||||
add_header X-XSS-Protection "1; mode=block";
|
|
@ -1,69 +0,0 @@
|
|||||||
#####################
|
|
||||||
# Required Settings #
|
|
||||||
#####################
|
|
||||||
|
|
||||||
# Server hostname
|
|
||||||
BW_DOMAIN=bitwarden.yourdomain.com
|
|
||||||
|
|
||||||
# Database
|
|
||||||
# Available providers are sqlserver, postgresql, mysql/mariadb, or sqlite
|
|
||||||
BW_DB_PROVIDER=mysql
|
|
||||||
BW_DB_SERVER=db
|
|
||||||
BW_DB_DATABASE=bitwarden_vault
|
|
||||||
BW_DB_USERNAME=bitwarden
|
|
||||||
BW_DB_PASSWORD=super_strong_password
|
|
||||||
|
|
||||||
# Installation information
|
|
||||||
# Get your ID and key from https://bitwarden.com/host/
|
|
||||||
BW_INSTALLATION_ID=00000000-0000-0000-0000-000000000000
|
|
||||||
BW_INSTALLATION_KEY=xxxxxxxxxxxx
|
|
||||||
|
|
||||||
#####################
|
|
||||||
# Optional Settings #
|
|
||||||
#####################
|
|
||||||
# Learn more here: https://bitwarden.com/help/environment-variables/
|
|
||||||
|
|
||||||
# Container user ID/group ID
|
|
||||||
#PUID=1000
|
|
||||||
#PGID=1000
|
|
||||||
|
|
||||||
# Webserver ports
|
|
||||||
#BW_PORT_HTTP=8080
|
|
||||||
#BW_PORT_HTTPS=8443
|
|
||||||
|
|
||||||
# SSL
|
|
||||||
#BW_ENABLE_SSL=true
|
|
||||||
#BW_ENABLE_SSL_CA=true
|
|
||||||
#BW_SSL_CERT=ssl.crt
|
|
||||||
#BW_SSL_KEY=ssl.key
|
|
||||||
#BW_SSL_CA_CERT=ca.crt
|
|
||||||
|
|
||||||
# Services
|
|
||||||
# Some services, namely for enterprise use cases, are disabled by default. Defaults shown below.
|
|
||||||
#BW_ENABLE_ADMIN=true
|
|
||||||
#BW_ENABLE_API=true
|
|
||||||
#BW_ENABLE_EVENTS=false
|
|
||||||
#BW_ENABLE_ICONS=true
|
|
||||||
#BW_ENABLE_IDENTITY=true
|
|
||||||
#BW_ENABLE_NOTIFICATIONS=true
|
|
||||||
#BW_ENABLE_SCIM=false
|
|
||||||
#BW_ENABLE_SSO=false
|
|
||||||
|
|
||||||
#BW_ICONS_PROXY_TO_CLOUD=false
|
|
||||||
|
|
||||||
# Mail
|
|
||||||
#globalSettings__mail__replyToEmail=noreply@$BW_DOMAIN
|
|
||||||
#globalSettings__mail__smtp__host=smtphost.example.com
|
|
||||||
#globalSettings__mail__smtp__port=587
|
|
||||||
#globalSettings__mail__smtp__ssl=false
|
|
||||||
#globalSettings__mail__smtp__username=smtpusername
|
|
||||||
#globalSettings__mail__smtp__password=smtppassword
|
|
||||||
|
|
||||||
# Yubikey
|
|
||||||
#globalSettings__yubico__clientId=REPLACE
|
|
||||||
#globalSettings__yubico__key=REPLACE
|
|
||||||
|
|
||||||
# Other
|
|
||||||
#globalSettings__disableUserRegistration=false
|
|
||||||
#globalSettings__hibpApiKey=REPLACE
|
|
||||||
#adminSettings__admins=admin1@email.com,admin2@email.com
|
|
@ -1,9 +0,0 @@
|
|||||||
[program:admin]
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
command=/usr/bin/dotnet "Admin.dll"
|
|
||||||
directory=/app/Admin
|
|
||||||
environment=ASPNETCORE_URLS="http://+:5000"
|
|
||||||
redirect_stderr=true
|
|
||||||
startsecs=15
|
|
||||||
stdout_logfile=/var/log/bitwarden/admin.log
|
|
@ -1,9 +0,0 @@
|
|||||||
[program:api]
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
command=/usr/bin/dotnet "Api.dll"
|
|
||||||
directory=/app/Api
|
|
||||||
environment=ASPNETCORE_URLS="http://+:5001"
|
|
||||||
redirect_stderr=true
|
|
||||||
startsecs=15
|
|
||||||
stdout_logfile=/var/log/bitwarden/api.log
|
|
@ -1,9 +0,0 @@
|
|||||||
[program:events]
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
command=/usr/bin/dotnet "Events.dll"
|
|
||||||
directory=/app/Events
|
|
||||||
environment=ASPNETCORE_URLS="http://+:5003"
|
|
||||||
redirect_stderr=true
|
|
||||||
startsecs=15
|
|
||||||
stdout_logfile=/var/log/bitwarden/events.log
|
|
@ -1,9 +0,0 @@
|
|||||||
[program:icons]
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
command=/usr/bin/dotnet "Icons.dll"
|
|
||||||
directory=/app/Icons
|
|
||||||
environment=ASPNETCORE_URLS="http://+:5004"
|
|
||||||
redirect_stderr=true
|
|
||||||
startsecs=15
|
|
||||||
stdout_logfile=/var/log/bitwarden/icons.log
|
|
@ -1,10 +0,0 @@
|
|||||||
[program:identity]
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
command=/usr/bin/dotnet "Identity.dll"
|
|
||||||
directory=/app/Identity
|
|
||||||
environment=ASPNETCORE_URLS="http://+:5005"
|
|
||||||
priority=1
|
|
||||||
redirect_stderr=true
|
|
||||||
startsecs=15
|
|
||||||
stdout_logfile=/var/log/bitwarden/identity.log
|
|
@ -1,7 +0,0 @@
|
|||||||
[program:nginx]
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
command=nginx
|
|
||||||
redirect_stderr=true
|
|
||||||
startsecs=15
|
|
||||||
stdout_logfile=/var/log/bitwarden/nginx.log
|
|
@ -1,9 +0,0 @@
|
|||||||
[program:notifications]
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
command=/usr/bin/dotnet "Notifications.dll"
|
|
||||||
directory=/app/Notifications
|
|
||||||
environment=ASPNETCORE_URLS="http://+:5006"
|
|
||||||
redirect_stderr=true
|
|
||||||
startsecs=15
|
|
||||||
stdout_logfile=/var/log/bitwarden/notifications.log
|
|
@ -1,9 +0,0 @@
|
|||||||
[program:scim]
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
command=/usr/bin/dotnet "Scim.dll"
|
|
||||||
directory=/app/Scim
|
|
||||||
environment=ASPNETCORE_URLS="http://+:5002"
|
|
||||||
redirect_stderr=true
|
|
||||||
startsecs=15
|
|
||||||
stdout_logfile=/var/log/bitwarden/scim.log
|
|
@ -1,9 +0,0 @@
|
|||||||
[program:sso]
|
|
||||||
autostart=true
|
|
||||||
autorestart=true
|
|
||||||
command=/usr/bin/dotnet "Sso.dll"
|
|
||||||
directory=/app/Sso
|
|
||||||
environment=ASPNETCORE_URLS="http://+:5007"
|
|
||||||
redirect_stderr=true
|
|
||||||
startsecs=15
|
|
||||||
stdout_logfile=/var/log/bitwarden/sso.log
|
|
@ -1,15 +0,0 @@
|
|||||||
[unix_http_server]
|
|
||||||
file=/run/supervisord.sock ; the path to the socket file
|
|
||||||
|
|
||||||
[supervisord]
|
|
||||||
logfile=/var/log/supervisord.log ; main log file; default $CWD/supervisord.log
|
|
||||||
nodaemon=true ; start in foreground if true; default false
|
|
||||||
|
|
||||||
[rpcinterface:supervisor]
|
|
||||||
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
|
||||||
|
|
||||||
[supervisorctl]
|
|
||||||
serverurl=unix:///run/supervisord.sock ; use a unix:// URL for a unix socket
|
|
||||||
|
|
||||||
[include]
|
|
||||||
files = /etc/supervisor.d/*.ini
|
|
@ -287,6 +287,12 @@ public class OrganizationsController : Controller
|
|||||||
organization.UseTotp = model.UseTotp;
|
organization.UseTotp = model.UseTotp;
|
||||||
organization.UsersGetPremium = model.UsersGetPremium;
|
organization.UsersGetPremium = model.UsersGetPremium;
|
||||||
organization.UseSecretsManager = model.UseSecretsManager;
|
organization.UseSecretsManager = model.UseSecretsManager;
|
||||||
|
|
||||||
|
//secrets
|
||||||
|
organization.SmSeats = model.SmSeats;
|
||||||
|
organization.MaxAutoscaleSmSeats = model.MaxAutoscaleSmSeats;
|
||||||
|
organization.SmServiceAccounts = model.SmServiceAccounts;
|
||||||
|
organization.MaxAutoscaleSmServiceAccounts = model.MaxAutoscaleSmServiceAccounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_accessControlService.UserHasPermission(Permission.Org_Licensing_Edit))
|
if (_accessControlService.UserHasPermission(Permission.Org_Licensing_Edit))
|
||||||
|
@ -63,6 +63,10 @@ public class OrganizationEditModel : OrganizationViewModel
|
|||||||
Enabled = org.Enabled;
|
Enabled = org.Enabled;
|
||||||
LicenseKey = org.LicenseKey;
|
LicenseKey = org.LicenseKey;
|
||||||
ExpirationDate = org.ExpirationDate;
|
ExpirationDate = org.ExpirationDate;
|
||||||
|
SmSeats = org.SmSeats;
|
||||||
|
MaxAutoscaleSmSeats = org.MaxAutoscaleSmSeats;
|
||||||
|
SmServiceAccounts = org.SmServiceAccounts;
|
||||||
|
MaxAutoscaleSmServiceAccounts = org.MaxAutoscaleSmServiceAccounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BillingInfo BillingInfo { get; set; }
|
public BillingInfo BillingInfo { get; set; }
|
||||||
@ -134,6 +138,14 @@ public class OrganizationEditModel : OrganizationViewModel
|
|||||||
[Display(Name = "Expiration Date")]
|
[Display(Name = "Expiration Date")]
|
||||||
public DateTime? ExpirationDate { get; set; }
|
public DateTime? ExpirationDate { get; set; }
|
||||||
public bool SalesAssistedTrialStarted { get; set; }
|
public bool SalesAssistedTrialStarted { get; set; }
|
||||||
|
[Display(Name = "Seats")]
|
||||||
|
public int? SmSeats { get; set; }
|
||||||
|
[Display(Name = "Max Autoscale Seats")]
|
||||||
|
public int? MaxAutoscaleSmSeats { get; set; }
|
||||||
|
[Display(Name = "Max Service Accounts")]
|
||||||
|
public int? SmServiceAccounts { get; set; }
|
||||||
|
[Display(Name = "Max Autoscale Service Accounts")]
|
||||||
|
public int? MaxAutoscaleSmServiceAccounts { get; set; }
|
||||||
|
|
||||||
public Organization CreateOrganization(Provider provider)
|
public Organization CreateOrganization(Provider provider)
|
||||||
{
|
{
|
||||||
@ -174,6 +186,10 @@ public class OrganizationEditModel : OrganizationViewModel
|
|||||||
existingOrganization.LicenseKey = LicenseKey;
|
existingOrganization.LicenseKey = LicenseKey;
|
||||||
existingOrganization.ExpirationDate = ExpirationDate;
|
existingOrganization.ExpirationDate = ExpirationDate;
|
||||||
existingOrganization.MaxAutoscaleSeats = MaxAutoscaleSeats;
|
existingOrganization.MaxAutoscaleSeats = MaxAutoscaleSeats;
|
||||||
|
existingOrganization.SmSeats = SmSeats;
|
||||||
|
existingOrganization.MaxAutoscaleSmSeats = MaxAutoscaleSmSeats;
|
||||||
|
existingOrganization.SmServiceAccounts = SmServiceAccounts;
|
||||||
|
existingOrganization.MaxAutoscaleSmServiceAccounts = MaxAutoscaleSmServiceAccounts;
|
||||||
return existingOrganization;
|
return existingOrganization;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,34 +99,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="Seats"></label>
|
|
||||||
<input type="number" class="form-control" asp-for="Seats" min="1" readonly='@(!canEditPlan)'>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="MaxCollections"></label>
|
|
||||||
<input type="number" class="form-control" asp-for="MaxCollections" min="1" readonly='@(!canEditPlan)'>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="MaxStorageGb"></label>
|
|
||||||
<input type="number" class="form-control" asp-for="MaxStorageGb" min="1" readonly='@(!canEditPlan)'>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-4">
|
|
||||||
<div class="form-group">
|
|
||||||
<label asp-for="MaxAutoscaleSeats"></label>
|
|
||||||
<input type="number" class="form-control" asp-for="MaxAutoscaleSeats" min="1" readonly='@(!canEditPlan)'>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<h2>Features</h2>
|
<h2>Features</h2>
|
||||||
<div class="row mb-3">
|
<div class="row mb-3">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
@ -200,6 +172,75 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (canViewPlan)
|
||||||
|
{
|
||||||
|
<h2>Password Manager Configuration</h2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="Seats"></label>
|
||||||
|
<input type="number" class="form-control" asp-for="Seats" min="1" readonly='@(!canEditPlan)'>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="MaxCollections"></label>
|
||||||
|
<input type="number" class="form-control" asp-for="MaxCollections" min="1" readonly='@(!canEditPlan)'>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="MaxStorageGb"></label>
|
||||||
|
<input type="number" class="form-control" asp-for="MaxStorageGb" min="1" readonly='@(!canEditPlan)'>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="MaxAutoscaleSeats"></label>
|
||||||
|
<input type="number" class="form-control" asp-for="MaxAutoscaleSeats" min="1" readonly='@(!canEditPlan)'>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (canViewPlan)
|
||||||
|
{
|
||||||
|
<div id="organization-secrets-configuration" hidden="@(!Model.UseSecretsManager)">
|
||||||
|
<h2>Secrets Manager Configuration</h2>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="SmSeats"></label>
|
||||||
|
<input type="number" class="form-control" asp-for="SmSeats" min="1" readonly='@(!canEditPlan)'>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="MaxAutoscaleSmSeats"></label>
|
||||||
|
<input type="number" class="form-control" asp-for="MaxAutoscaleSmSeats" min="1" readonly='@(!canEditPlan)'>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="SmServiceAccounts"></label>
|
||||||
|
<input type="number" class="form-control" asp-for="SmServiceAccounts" min="1" readonly='@(!canEditPlan)'>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-4">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="MaxAutoscaleSmServiceAccounts"></label>
|
||||||
|
<input type="number" class="form-control" asp-for="MaxAutoscaleSmServiceAccounts" min="1" readonly='@(!canEditPlan)'>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
@if(canViewLicensing)
|
@if(canViewLicensing)
|
||||||
{
|
{
|
||||||
<h2>Licensing</h2>
|
<h2>Licensing</h2>
|
||||||
|
@ -32,6 +32,18 @@
|
|||||||
'subscriptions/' + subId.value, '_blank');
|
'subscriptions/' + subId.value, '_blank');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
document.getElementById('@(nameof(Model.UseSecretsManager))').addEventListener('change', (event) => {
|
||||||
|
document.getElementById('organization-secrets-configuration').hidden = !event.target.checked;
|
||||||
|
|
||||||
|
if (event.target.checked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('@(nameof(Model.SmSeats))').value = '';
|
||||||
|
document.getElementById('@(nameof(Model.MaxAutoscaleSmSeats))').value = '';
|
||||||
|
document.getElementById('@(nameof(Model.SmServiceAccounts))').value = '';
|
||||||
|
document.getElementById('@(nameof(Model.MaxAutoscaleSmServiceAccounts))').value = '';
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
function togglePlanSettings(planType) {
|
function togglePlanSettings(planType) {
|
||||||
@ -43,6 +55,10 @@
|
|||||||
document.getElementById('@(nameof(Model.Seats))').value = '10';
|
document.getElementById('@(nameof(Model.Seats))').value = '10';
|
||||||
document.getElementById('@(nameof(Model.MaxCollections))').value = '';
|
document.getElementById('@(nameof(Model.MaxCollections))').value = '';
|
||||||
document.getElementById('@(nameof(Model.MaxStorageGb))').value = '1';
|
document.getElementById('@(nameof(Model.MaxStorageGb))').value = '1';
|
||||||
|
// Secrets
|
||||||
|
if (document.getElementById('@(nameof(Model.UseSecretsManager))').checked) {
|
||||||
|
document.getElementById('@(nameof(Model.SmServiceAccounts))').value = '50';
|
||||||
|
}
|
||||||
// Features
|
// Features
|
||||||
document.getElementById('@(nameof(Model.UsePolicies))').checked = false;
|
document.getElementById('@(nameof(Model.UsePolicies))').checked = false;
|
||||||
document.getElementById('@(nameof(Model.UseSso))').checked = false;
|
document.getElementById('@(nameof(Model.UseSso))').checked = false;
|
||||||
@ -69,6 +85,10 @@
|
|||||||
document.getElementById('@(nameof(Model.Seats))').value = '10';
|
document.getElementById('@(nameof(Model.Seats))').value = '10';
|
||||||
document.getElementById('@(nameof(Model.MaxCollections))').value = '';
|
document.getElementById('@(nameof(Model.MaxCollections))').value = '';
|
||||||
document.getElementById('@(nameof(Model.MaxStorageGb))').value = '1';
|
document.getElementById('@(nameof(Model.MaxStorageGb))').value = '1';
|
||||||
|
// Secrets
|
||||||
|
if (document.getElementById('@(nameof(Model.UseSecretsManager))').checked) {
|
||||||
|
document.getElementById('@(nameof(Model.SmServiceAccounts))').value = '200';
|
||||||
|
}
|
||||||
// Features
|
// Features
|
||||||
document.getElementById('@(nameof(Model.UsePolicies))').checked = true;
|
document.getElementById('@(nameof(Model.UsePolicies))').checked = true;
|
||||||
document.getElementById('@(nameof(Model.UseSso))').checked = true;
|
document.getElementById('@(nameof(Model.UseSso))').checked = true;
|
||||||
|
@ -2834,7 +2834,7 @@
|
|||||||
"commercial.core": {
|
"commercial.core": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0"
|
"Core": "2023.4.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"core": {
|
"core": {
|
||||||
@ -2882,7 +2882,7 @@
|
|||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2890,7 +2890,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -2902,7 +2902,7 @@
|
|||||||
"migrator": {
|
"migrator": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.Extensions.Logging": "6.0.0",
|
"Microsoft.Extensions.Logging": "6.0.0",
|
||||||
"dbup-sqlserver": "5.0.8"
|
"dbup-sqlserver": "5.0.8"
|
||||||
}
|
}
|
||||||
@ -2910,30 +2910,30 @@
|
|||||||
"mysqlmigrations": {
|
"mysqlmigrations": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"postgresmigrations": {
|
"postgresmigrations": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sqlitemigrations": {
|
"sqlitemigrations": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,15 @@
|
|||||||
</Choose>
|
</Choose>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.AzureServiceBus" Version="6.1.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.AzureStorage" Version="6.1.2" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.Network" Version="6.0.4" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.Redis" Version="6.0.4" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.SendGrid" Version="6.0.2" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="6.0.2" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="6.0.3" />
|
||||||
<PackageReference Include="Azure.Messaging.EventGrid" Version="4.10.0" />
|
<PackageReference Include="Azure.Messaging.EventGrid" Version="4.10.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using Bit.Api.Models.Request;
|
using Bit.Api.Models.Request;
|
||||||
using Bit.Api.Models.Response;
|
using Bit.Api.Models.Response;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
@ -65,6 +66,15 @@ public class DevicesController : Controller
|
|||||||
return new ListResponseModel<DeviceResponseModel>(responses);
|
return new ListResponseModel<DeviceResponseModel>(responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost("exist-by-types")]
|
||||||
|
public async Task<ActionResult<bool>> GetExistenceByTypes([FromBody] DeviceType[] deviceTypes)
|
||||||
|
{
|
||||||
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var devices = await _deviceRepository.GetManyByUserIdAsync(userId);
|
||||||
|
var userHasDeviceOfTypes = devices.Any(d => deviceTypes.Contains(d.Type));
|
||||||
|
return Ok(userHasDeviceOfTypes);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost("")]
|
[HttpPost("")]
|
||||||
public async Task<DeviceResponseModel> Post([FromBody] DeviceRequestModel model)
|
public async Task<DeviceResponseModel> Post([FromBody] DeviceRequestModel model)
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,7 @@ using Bit.Core.Identity;
|
|||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
||||||
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Tools.Enums;
|
using Bit.Core.Tools.Enums;
|
||||||
@ -170,9 +171,40 @@ public class SecretsController : Controller
|
|||||||
[HttpPost("secrets/delete")]
|
[HttpPost("secrets/delete")]
|
||||||
public async Task<ListResponseModel<BulkDeleteResponseModel>> BulkDeleteAsync([FromBody] List<Guid> ids)
|
public async Task<ListResponseModel<BulkDeleteResponseModel>> BulkDeleteAsync([FromBody] List<Guid> ids)
|
||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var secrets = (await _secretRepository.GetManyByIds(ids)).ToList();
|
||||||
var results = await _deleteSecretCommand.DeleteSecrets(ids, userId);
|
if (!secrets.Any() || secrets.Count != ids.Count)
|
||||||
var responses = results.Select(r => new BulkDeleteResponseModel(r.Item1.Id, r.Item2));
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure all secrets belong to the same organization.
|
||||||
|
var organizationId = secrets.First().OrganizationId;
|
||||||
|
if (secrets.Any(secret => secret.OrganizationId != organizationId) ||
|
||||||
|
!_currentContext.AccessSecretsManager(organizationId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var secretsToDelete = new List<Secret>();
|
||||||
|
var results = new List<(Secret Secret, string Error)>();
|
||||||
|
|
||||||
|
foreach (var secret in secrets)
|
||||||
|
{
|
||||||
|
var authorizationResult =
|
||||||
|
await _authorizationService.AuthorizeAsync(User, secret, SecretOperations.Delete);
|
||||||
|
if (authorizationResult.Succeeded)
|
||||||
|
{
|
||||||
|
secretsToDelete.Add(secret);
|
||||||
|
results.Add((secret, ""));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
results.Add((secret, "access denied"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await _deleteSecretCommand.DeleteSecrets(secretsToDelete);
|
||||||
|
var responses = results.Select(r => new BulkDeleteResponseModel(r.Secret.Id, r.Error));
|
||||||
return new ListResponseModel<BulkDeleteResponseModel>(responses);
|
return new ListResponseModel<BulkDeleteResponseModel>(responses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
using Bit.Core.Models.Api;
|
using Bit.Core.Models.Api;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Models.Data;
|
||||||
|
|
||||||
namespace Bit.Api.SecretsManager.Models.Response;
|
namespace Bit.Api.SecretsManager.Models.Response;
|
||||||
|
|
||||||
@ -8,14 +8,14 @@ public class AccessTokenCreationResponseModel : ResponseModel
|
|||||||
{
|
{
|
||||||
private const string _objectName = "accessTokenCreation";
|
private const string _objectName = "accessTokenCreation";
|
||||||
|
|
||||||
public AccessTokenCreationResponseModel(ApiKey apiKey) : base(_objectName)
|
public AccessTokenCreationResponseModel(ApiKeyClientSecretDetails details) : base(_objectName)
|
||||||
{
|
{
|
||||||
Id = apiKey.Id;
|
Id = details.ApiKey.Id;
|
||||||
Name = apiKey.Name;
|
Name = details.ApiKey.Name;
|
||||||
ClientSecret = apiKey.ClientSecret;
|
ExpireAt = details.ApiKey.ExpireAt;
|
||||||
ExpireAt = apiKey.ExpireAt;
|
CreationDate = details.ApiKey.CreationDate;
|
||||||
CreationDate = apiKey.CreationDate;
|
RevisionDate = details.ApiKey.RevisionDate;
|
||||||
RevisionDate = apiKey.RevisionDate;
|
ClientSecret = details.ClientSecret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccessTokenCreationResponseModel() : base(_objectName)
|
public AccessTokenCreationResponseModel() : base(_objectName)
|
||||||
|
@ -8,9 +8,11 @@ using Bit.Core.Utilities;
|
|||||||
using IdentityModel;
|
using IdentityModel;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Bit.Core.IdentityServer;
|
using Bit.Core.IdentityServer;
|
||||||
|
using Bit.SharedWeb.Health;
|
||||||
using Microsoft.IdentityModel.Logging;
|
using Microsoft.IdentityModel.Logging;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Bit.SharedWeb.Utilities;
|
using Bit.SharedWeb.Utilities;
|
||||||
|
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using Bit.Core.Auth.Identity;
|
using Bit.Core.Auth.Identity;
|
||||||
|
|
||||||
@ -133,6 +135,9 @@ public class Startup
|
|||||||
services.AddDefaultServices(globalSettings);
|
services.AddDefaultServices(globalSettings);
|
||||||
services.AddCoreLocalizationServices();
|
services.AddCoreLocalizationServices();
|
||||||
|
|
||||||
|
//health check
|
||||||
|
services.AddHealthChecks(globalSettings);
|
||||||
|
|
||||||
#if OSS
|
#if OSS
|
||||||
services.AddOosServices();
|
services.AddOosServices();
|
||||||
#else
|
#else
|
||||||
@ -206,7 +211,17 @@ public class Startup
|
|||||||
app.UseMiddleware<CurrentContextMiddleware>();
|
app.UseMiddleware<CurrentContextMiddleware>();
|
||||||
|
|
||||||
// Add endpoints to the request pipeline.
|
// Add endpoints to the request pipeline.
|
||||||
app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());
|
app.UseEndpoints(endpoints =>
|
||||||
|
{
|
||||||
|
endpoints.MapDefaultControllerRoute();
|
||||||
|
|
||||||
|
endpoints.MapHealthChecks("/healthz");
|
||||||
|
|
||||||
|
endpoints.MapHealthChecks("/healthz/extended", new HealthCheckOptions
|
||||||
|
{
|
||||||
|
ResponseWriter = HealthCheckServiceExtensions.WriteResponse
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Add Swagger
|
// Add Swagger
|
||||||
if (Environment.IsDevelopment() || globalSettings.SelfHosted)
|
if (Environment.IsDevelopment() || globalSettings.SelfHosted)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Bit.Core.IdentityServer;
|
using Bit.Core.IdentityServer;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
|
using Bit.SharedWeb.Health;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
|
|
||||||
namespace Bit.Api.Utilities;
|
namespace Bit.Api.Utilities;
|
||||||
@ -69,4 +70,48 @@ public static class ServiceCollectionExtensions
|
|||||||
config.IncludeXmlComments(coreFilePath);
|
config.IncludeXmlComments(coreFilePath);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void AddHealthChecks(this IServiceCollection services, GlobalSettings globalSettings)
|
||||||
|
{
|
||||||
|
services.AddHealthCheckServices(globalSettings, builder =>
|
||||||
|
{
|
||||||
|
var identityUri = new Uri(globalSettings.BaseServiceUri.Identity
|
||||||
|
+ "/.well-known/openid-configuration");
|
||||||
|
|
||||||
|
builder.AddUrlGroup(identityUri, "identity");
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(globalSettings.SqlServer.ConnectionString))
|
||||||
|
{
|
||||||
|
builder.AddSqlServer(globalSettings.SqlServer.ConnectionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(globalSettings.Redis.ConnectionString))
|
||||||
|
{
|
||||||
|
builder.AddRedis(globalSettings.Redis.ConnectionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(globalSettings.Storage.ConnectionString))
|
||||||
|
{
|
||||||
|
builder.AddAzureQueueStorage(globalSettings.Storage.ConnectionString, name: "storage_queue")
|
||||||
|
.AddAzureQueueStorage(globalSettings.Events.ConnectionString, name: "events_queue");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(globalSettings.Notifications.ConnectionString))
|
||||||
|
{
|
||||||
|
builder.AddAzureQueueStorage(globalSettings.Notifications.ConnectionString,
|
||||||
|
name: "notifications_queue");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(globalSettings.ServiceBus.ConnectionString))
|
||||||
|
{
|
||||||
|
builder.AddAzureServiceBusTopic(_ => globalSettings.ServiceBus.ConnectionString,
|
||||||
|
_ => globalSettings.ServiceBus.ApplicationCacheTopicName, name: "service_bus");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(globalSettings.Mail.SendGridApiKey))
|
||||||
|
{
|
||||||
|
builder.AddSendGrid(globalSettings.Mail.SendGridApiKey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,81 @@
|
|||||||
"version": 1,
|
"version": 1,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"net6.0": {
|
"net6.0": {
|
||||||
|
"AspNetCore.HealthChecks.AzureServiceBus": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[6.1.0, )",
|
||||||
|
"resolved": "6.1.0",
|
||||||
|
"contentHash": "LepLE6NO4bLBVDzlx/730pD6jnfkV6zaaRUrbN1LqnNk4m1hROsv7wOpgbKgVDgYIfeLzdiVnBviEevSxWFKMQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Azure.Messaging.EventHubs": "5.7.4",
|
||||||
|
"Azure.Messaging.ServiceBus": "7.11.1",
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.AzureStorage": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[6.1.2, )",
|
||||||
|
"resolved": "6.1.2",
|
||||||
|
"contentHash": "R/uHJ40Cc0fBLi48SqDtT6fHyR5G8L3+PeKlbe8t498GLebeBIR3ve4l4n7UzCD0qgmQDDvyIYvVywx3i5Y6Ng==",
|
||||||
|
"dependencies": {
|
||||||
|
"Azure.Storage.Blobs": "12.14.1",
|
||||||
|
"Azure.Storage.Files.Shares": "12.11.0",
|
||||||
|
"Azure.Storage.Queues": "12.11.1",
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.Network": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[6.0.4, )",
|
||||||
|
"resolved": "6.0.4",
|
||||||
|
"contentHash": "rvoPkqlvhX1HW6dpqjE1rbvmmMo9v7+Uf9dJffEcd3mA/DyyEitlZFc6cwYtmZVFdgy2gbIU4ubs3654nVfvjA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.7",
|
||||||
|
"SSH.NET": "2020.0.2",
|
||||||
|
"System.Buffers": "4.5.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.Redis": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[6.0.4, )",
|
||||||
|
"resolved": "6.0.4",
|
||||||
|
"contentHash": "adNNWF6kV8v1HLTmF3b9F5K6ubvgx+S7VqhzA8T/5YuIpRWsCDk8+q3RIDDV8Twvl9pRahLfzCbFrPYxvzmk7g==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.4",
|
||||||
|
"StackExchange.Redis": "2.5.61"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.SendGrid": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[6.0.2, )",
|
||||||
|
"resolved": "6.0.2",
|
||||||
|
"contentHash": "VgskjkCUmSpAxil20rZlrj14bMi9aFNdiGLDtDTKjkUU0GYkoyi4HRVEy9Gp0FIgu9ce7quN+dNCpydKvMxjqA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.2",
|
||||||
|
"Microsoft.Extensions.Http": "6.0.0",
|
||||||
|
"SendGrid": "9.24.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.SqlServer": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[6.0.2, )",
|
||||||
|
"resolved": "6.0.2",
|
||||||
|
"contentHash": "Af7ws27DnZZ4bKCiEREm7emSAKEtIiYirEAkI0ixFgK1fwJ99jmMnPC+kU01zfqn3FyCO/gZOUO7WbyVvTPpFg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Data.SqlClient": "3.0.1",
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.Uris": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[6.0.3, )",
|
||||||
|
"resolved": "6.0.3",
|
||||||
|
"contentHash": "EY0Vh8s2UrbnyvM/QhbyYuCnbrBw36BKkdh5LqdINxqAGnlPFQXf+/UoNlH/76MTEyg+nvdp2wjr5MqWDkVFaQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.0",
|
||||||
|
"Microsoft.Extensions.Http": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Azure.Messaging.EventGrid": {
|
"Azure.Messaging.EventGrid": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[4.10.0, )",
|
"requested": "[4.10.0, )",
|
||||||
@ -97,6 +172,14 @@
|
|||||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Azure.Core.Amqp": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.2.0",
|
||||||
|
"contentHash": "vrF4isvhwdZspzorLwYhukXz3DA8/ONSnZUIBAqBtOCzsDNUgAsuILbCzvtDrn2oDxyq7DZx5Nh81pe0BeWmDQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Memory": "4.5.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": {
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.3.2",
|
"resolved": "1.3.2",
|
||||||
@ -121,6 +204,34 @@
|
|||||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Azure.Messaging.EventHubs": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "5.7.4",
|
||||||
|
"contentHash": "8vC4efO5HzDgZjx6LaViScywbyKu3xIkL+y+QoyN7Yo6u1pEmMAPW4ptaWIj1JW4gypeWC1tFy+U3zdQ/E7bGA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Azure.Core": "1.25.0",
|
||||||
|
"Azure.Core.Amqp": "1.2.0",
|
||||||
|
"Microsoft.Azure.Amqp": "2.5.12",
|
||||||
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
|
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
||||||
|
"System.Memory.Data": "1.0.2",
|
||||||
|
"System.Reflection.TypeExtensions": "4.7.0",
|
||||||
|
"System.Threading.Channels": "4.7.1",
|
||||||
|
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Azure.Messaging.ServiceBus": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "7.11.1",
|
||||||
|
"contentHash": "ioGedXeH8KK4HdTDEyOzdgNbEXGstGItVljI1EKYsz08sgwej6LpODCZmwPR2ui1fjXBWt8Zea0RJT4d9LwlMg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Azure.Core": "1.25.0",
|
||||||
|
"Azure.Core.Amqp": "1.2.0",
|
||||||
|
"Microsoft.Azure.Amqp": "2.5.12",
|
||||||
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
|
"System.Memory.Data": "1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Azure.Storage.Blobs": {
|
"Azure.Storage.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.14.1",
|
"resolved": "12.14.1",
|
||||||
@ -139,6 +250,15 @@
|
|||||||
"System.IO.Hashing": "6.0.0"
|
"System.IO.Hashing": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Azure.Storage.Files.Shares": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "12.11.0",
|
||||||
|
"contentHash": "C747FRSZNe/L4hu1wrvzQImVaIfNDcZXfttaV3FwX96+TsbgXotHe6Y0lmSu65H/gVYKt07sIW9E1mDi3bdADw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Azure.Storage.Common": "12.12.0",
|
||||||
|
"System.Text.Json": "4.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Azure.Storage.Queues": {
|
"Azure.Storage.Queues": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.12.0",
|
"resolved": "12.12.0",
|
||||||
@ -389,12 +509,8 @@
|
|||||||
},
|
},
|
||||||
"Microsoft.Azure.Amqp": {
|
"Microsoft.Azure.Amqp": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "2.4.11",
|
"resolved": "2.5.12",
|
||||||
"contentHash": "7x5fu2f6TLQDDJS0sY5qW8/daFwJaY9O75YvU8RcUfRzbug+9YGjXUBxoRrprgyi0jxdBAMQL05p1s783SOSFQ==",
|
"contentHash": "0SlEl+TSQdpjXWf9/37dXWAa0zk6R1EJKmGtGZeKUAH7WEQpJOWMxJ9I43igcBCnTkFwa28CdPnpSCjFZVQlkw=="
|
||||||
"dependencies": {
|
|
||||||
"System.Net.WebSockets.Client": "4.0.2",
|
|
||||||
"System.Runtime.Serialization.Primitives": "4.1.1"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Microsoft.Azure.Cosmos": {
|
"Microsoft.Azure.Cosmos": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
@ -722,6 +838,22 @@
|
|||||||
"System.Text.Json": "6.0.0"
|
"System.Text.Json": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.10",
|
||||||
|
"contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10",
|
||||||
|
"Microsoft.Extensions.Hosting.Abstractions": "6.0.0",
|
||||||
|
"Microsoft.Extensions.Logging.Abstractions": "6.0.2",
|
||||||
|
"Microsoft.Extensions.Options": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.10",
|
||||||
|
"contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA=="
|
||||||
|
},
|
||||||
"Microsoft.Extensions.FileProviders.Abstractions": {
|
"Microsoft.Extensions.FileProviders.Abstractions": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "6.0.0",
|
"resolved": "6.0.0",
|
||||||
@ -747,13 +879,12 @@
|
|||||||
},
|
},
|
||||||
"Microsoft.Extensions.Hosting.Abstractions": {
|
"Microsoft.Extensions.Hosting.Abstractions": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "3.1.32",
|
"resolved": "6.0.0",
|
||||||
"contentHash": "00J6eE920t5vfPnEHBSGyj1Ya9lG6WYsMwqvLZ0nMPPTD2UxkaL+FNJM5DNSnMFJtV84KkUudPRngmNiCkqhuA==",
|
"contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Extensions.Configuration.Abstractions": "3.1.32",
|
"Microsoft.Extensions.Configuration.Abstractions": "6.0.0",
|
||||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.32",
|
"Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0",
|
||||||
"Microsoft.Extensions.FileProviders.Abstractions": "3.1.32",
|
"Microsoft.Extensions.FileProviders.Abstractions": "6.0.0"
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "3.1.32"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Microsoft.Extensions.Http": {
|
"Microsoft.Extensions.Http": {
|
||||||
@ -801,8 +932,8 @@
|
|||||||
},
|
},
|
||||||
"Microsoft.Extensions.Logging.Abstractions": {
|
"Microsoft.Extensions.Logging.Abstractions": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "6.0.1",
|
"resolved": "6.0.2",
|
||||||
"contentHash": "dzB2Cgg+JmrouhjkcQGzSFjjvpwlq353i8oBQO2GWNjCXSzhbtBRUf28HSauWe7eib3wYOdb3tItdjRwAdwCSg=="
|
"contentHash": "pwXCZKaA7m5wgmCj49dW+H1RPSY7U62SKLTQYCcavf/k3Nyt/WnBgAjG4jMGnwy9rElfAZ2KvxvM5CJzJWG0hg=="
|
||||||
},
|
},
|
||||||
"Microsoft.Extensions.Options": {
|
"Microsoft.Extensions.Options": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
@ -1423,10 +1554,23 @@
|
|||||||
"SQLitePCLRaw.core": "2.1.2"
|
"SQLitePCLRaw.core": "2.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"SSH.NET": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "2020.0.2",
|
||||||
|
"contentHash": "G0dNlTBAM00KZXv1wWVwgg26d9/METcM6qWBpNQwllzQmmbu+Zu+FS1L1X4fFgGdPu3e8k9mmTBu6SwtQ0614g==",
|
||||||
|
"dependencies": {
|
||||||
|
"SshNet.Security.Cryptography": "[1.3.0]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SshNet.Security.Cryptography": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.3.0",
|
||||||
|
"contentHash": "5pBIXRjcSO/amY8WztpmNOhaaCNHY/B6CcYDI7FSTgqSyo/ZUojlLiKcsl+YGbxQuLX439qIkMfP0PHqxqJi/Q=="
|
||||||
|
},
|
||||||
"StackExchange.Redis": {
|
"StackExchange.Redis": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "2.5.43",
|
"resolved": "2.5.61",
|
||||||
"contentHash": "YQ38jVbX1b5mBi6lizESou+NpV6QZpeo6ofRR6qeuqJ8ePOmhcwhje3nDTNIGEkfPSK0sLuF6pR5rtFyq2F46g==",
|
"contentHash": "h1Gz4itrHL/PQ0GBLTEiPK8bBkOp5SFO6iaRFSSn/x1qltBWENsz/NUxPid6WHX9yf2Tiyzn9D3R7mtnksODxg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Pipelines.Sockets.Unofficial": "2.2.2",
|
"Pipelines.Sockets.Unofficial": "2.2.2",
|
||||||
"System.Diagnostics.PerformanceCounter": "5.0.0"
|
"System.Diagnostics.PerformanceCounter": "5.0.0"
|
||||||
@ -2061,42 +2205,6 @@
|
|||||||
"System.Runtime.Extensions": "4.1.0"
|
"System.Runtime.Extensions": "4.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"System.Net.WebSockets": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "4.0.0",
|
|
||||||
"contentHash": "2KJo8hir6Edi9jnMDAMhiJoI691xRBmKcbNpwjrvpIMOCTYOtBpSsSEGBxBDV7PKbasJNaFp1+PZz1D7xS41Hg==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Win32.Primitives": "4.0.1",
|
|
||||||
"System.Resources.ResourceManager": "4.0.1",
|
|
||||||
"System.Runtime": "4.1.0",
|
|
||||||
"System.Threading.Tasks": "4.0.11"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"System.Net.WebSockets.Client": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "4.0.2",
|
|
||||||
"contentHash": "NUCcDroX4lCQXgOrzlwIZ1u9YJ0krfyF0wk0ONnyLUmcQoEiYV2/OfUPRqUwQBbpH1BlGApkLgoQUwMqb5+c1g==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.NETCore.Platforms": "1.0.2",
|
|
||||||
"Microsoft.Win32.Primitives": "4.0.1",
|
|
||||||
"System.Collections": "4.0.11",
|
|
||||||
"System.Diagnostics.Debug": "4.0.11",
|
|
||||||
"System.Diagnostics.Tracing": "4.1.0",
|
|
||||||
"System.Globalization": "4.0.11",
|
|
||||||
"System.Net.Primitives": "4.0.11",
|
|
||||||
"System.Net.WebHeaderCollection": "4.0.1",
|
|
||||||
"System.Net.WebSockets": "4.0.0",
|
|
||||||
"System.Resources.ResourceManager": "4.0.1",
|
|
||||||
"System.Runtime": "4.1.0",
|
|
||||||
"System.Runtime.Extensions": "4.1.0",
|
|
||||||
"System.Runtime.Handles": "4.0.1",
|
|
||||||
"System.Runtime.InteropServices": "4.1.0",
|
|
||||||
"System.Security.Cryptography.X509Certificates": "4.1.0",
|
|
||||||
"System.Text.Encoding": "4.0.11",
|
|
||||||
"System.Threading": "4.0.11",
|
|
||||||
"System.Threading.Tasks": "4.0.11"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"System.Numerics.Vectors": {
|
"System.Numerics.Vectors": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.5.0",
|
"resolved": "4.5.0",
|
||||||
@ -2213,12 +2321,8 @@
|
|||||||
},
|
},
|
||||||
"System.Reflection.TypeExtensions": {
|
"System.Reflection.TypeExtensions": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.3.0",
|
"resolved": "4.7.0",
|
||||||
"contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==",
|
"contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA=="
|
||||||
"dependencies": {
|
|
||||||
"System.Reflection": "4.3.0",
|
|
||||||
"System.Runtime": "4.3.0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"System.Resources.ResourceManager": {
|
"System.Resources.ResourceManager": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
@ -2606,6 +2710,11 @@
|
|||||||
"System.Threading.Tasks": "4.3.0"
|
"System.Threading.Tasks": "4.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"System.Threading.Channels": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.7.1",
|
||||||
|
"contentHash": "6akRtHK/wab3246t4p5v3HQrtQk8LboOt5T4dtpNgsp3zvDeM4/Gx8V12t0h+c/W9/enUrilk8n6EQqdQorZAA=="
|
||||||
|
},
|
||||||
"System.Threading.Overlapped": {
|
"System.Threading.Overlapped": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.0.1",
|
"resolved": "4.0.1",
|
||||||
@ -2798,85 +2907,85 @@
|
|||||||
"commercial.core": {
|
"commercial.core": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0"
|
"Core": "[2023.5.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"commercial.infrastructure.entityframework": {
|
"commercial.infrastructure.entityframework": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "[2023.5.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"core": {
|
"core": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AWSSDK.SQS": "3.7.2.47",
|
"AWSSDK.SQS": "[3.7.2.47, )",
|
||||||
"AWSSDK.SimpleEmail": "3.7.0.150",
|
"AWSSDK.SimpleEmail": "[3.7.0.150, )",
|
||||||
"AspNetCoreRateLimit": "4.0.2",
|
"AspNetCoreRateLimit": "[4.0.2, )",
|
||||||
"AspNetCoreRateLimit.Redis": "1.0.1",
|
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "1.3.2",
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.3.2, )",
|
||||||
"Azure.Storage.Blobs": "12.14.1",
|
"Azure.Storage.Blobs": "[12.14.1, )",
|
||||||
"Azure.Storage.Queues": "12.12.0",
|
"Azure.Storage.Queues": "[12.12.0, )",
|
||||||
"BitPay.Light": "1.0.1907",
|
"BitPay.Light": "[1.0.1907, )",
|
||||||
"Braintree": "5.12.0",
|
"Braintree": "[5.12.0, )",
|
||||||
"DnsClient": "1.7.0",
|
"DnsClient": "[1.7.0, )",
|
||||||
"Fido2.AspNet": "3.0.1",
|
"Fido2.AspNet": "[3.0.1, )",
|
||||||
"Handlebars.Net": "2.1.2",
|
"Handlebars.Net": "[2.1.2, )",
|
||||||
"IdentityServer4": "4.1.2",
|
"IdentityServer4": "[4.1.2, )",
|
||||||
"IdentityServer4.AccessTokenValidation": "3.0.1",
|
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
|
||||||
"LaunchDarkly.ServerSdk": "7.0.0",
|
"LaunchDarkly.ServerSdk": "[7.0.0, )",
|
||||||
"MailKit": "3.2.0",
|
"MailKit": "[3.2.0, )",
|
||||||
"Microsoft.AspNetCore.Authentication.JwtBearer": "6.0.4",
|
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",
|
||||||
"Microsoft.Azure.Cosmos.Table": "1.0.8",
|
"Microsoft.Azure.Cosmos.Table": "[1.0.8, )",
|
||||||
"Microsoft.Azure.NotificationHubs": "4.1.0",
|
"Microsoft.Azure.NotificationHubs": "[4.1.0, )",
|
||||||
"Microsoft.Azure.ServiceBus": "5.2.0",
|
"Microsoft.Azure.ServiceBus": "[5.2.0, )",
|
||||||
"Microsoft.Data.SqlClient": "5.0.1",
|
"Microsoft.Data.SqlClient": "[5.0.1, )",
|
||||||
"Microsoft.Extensions.Caching.StackExchangeRedis": "6.0.6",
|
"Microsoft.Extensions.Caching.StackExchangeRedis": "[6.0.6, )",
|
||||||
"Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.1",
|
"Microsoft.Extensions.Configuration.EnvironmentVariables": "[6.0.1, )",
|
||||||
"Microsoft.Extensions.Configuration.UserSecrets": "6.0.1",
|
"Microsoft.Extensions.Configuration.UserSecrets": "[6.0.1, )",
|
||||||
"Microsoft.Extensions.Identity.Stores": "6.0.4",
|
"Microsoft.Extensions.Identity.Stores": "[6.0.4, )",
|
||||||
"Newtonsoft.Json": "13.0.1",
|
"Newtonsoft.Json": "[13.0.1, )",
|
||||||
"Otp.NET": "1.2.2",
|
"Otp.NET": "[1.2.2, )",
|
||||||
"Quartz": "3.4.0",
|
"Quartz": "[3.4.0, )",
|
||||||
"SendGrid": "9.27.0",
|
"SendGrid": "[9.27.0, )",
|
||||||
"Sentry.Serilog": "3.16.0",
|
"Sentry.Serilog": "[3.16.0, )",
|
||||||
"Serilog.AspNetCore": "5.0.0",
|
"Serilog.AspNetCore": "[5.0.0, )",
|
||||||
"Serilog.Extensions.Logging": "3.1.0",
|
"Serilog.Extensions.Logging": "[3.1.0, )",
|
||||||
"Serilog.Extensions.Logging.File": "2.0.0",
|
"Serilog.Extensions.Logging.File": "[2.0.0, )",
|
||||||
"Serilog.Sinks.AzureCosmosDB": "2.0.0",
|
"Serilog.Sinks.AzureCosmosDB": "[2.0.0, )",
|
||||||
"Serilog.Sinks.SyslogMessages": "2.0.6",
|
"Serilog.Sinks.SyslogMessages": "[2.0.6, )",
|
||||||
"Stripe.net": "40.0.0",
|
"Stripe.net": "[40.0.0, )",
|
||||||
"YubicoDotNetClient": "1.2.0"
|
"YubicoDotNetClient": "[1.2.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "[2.0.123, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"infrastructure.entityframework": {
|
"infrastructure.entityframework": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "[6.0.12, )",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "[6.0.12, )",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "[6.0.12, )",
|
||||||
"Npgsql.EntityFrameworkCore.PostgreSQL": "6.0.8",
|
"Npgsql.EntityFrameworkCore.PostgreSQL": "[6.0.8, )",
|
||||||
"Pomelo.EntityFrameworkCore.MySql": "6.0.2",
|
"Pomelo.EntityFrameworkCore.MySql": "[6.0.2, )",
|
||||||
"linq2db.EntityFrameworkCore": "6.11.0"
|
"linq2db.EntityFrameworkCore": "[6.11.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "[2023.5.0, )",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "[2023.5.0, )"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2786,7 +2786,7 @@
|
|||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2794,7 +2794,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -2806,9 +2806,9 @@
|
|||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,18 +12,18 @@ public class UserDecryptionOptions : ResponseModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Gets or sets whether the current user has a master password that can be used to decrypt their vault.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasMasterPassword { get; set; }
|
public bool HasMasterPassword { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Gets or sets information regarding this users trusted device decryption setup.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
public TrustedDeviceUserDecryptionOption? TrustedDeviceOption { get; set; }
|
public TrustedDeviceUserDecryptionOption? TrustedDeviceOption { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Gets or set information about the current users KeyConnector setup.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
public KeyConnectorUserDecryptionOption? KeyConnectorOption { get; set; }
|
public KeyConnectorUserDecryptionOption? KeyConnectorOption { get; set; }
|
||||||
|
@ -10,4 +10,5 @@ public static class SecretOperations
|
|||||||
{
|
{
|
||||||
public static readonly SecretOperationRequirement Create = new() { Name = nameof(Create) };
|
public static readonly SecretOperationRequirement Create = new() { Name = nameof(Create) };
|
||||||
public static readonly SecretOperationRequirement Update = new() { Name = nameof(Update) };
|
public static readonly SecretOperationRequirement Update = new() { Name = nameof(Update) };
|
||||||
|
public static readonly SecretOperationRequirement Delete = new() { Name = nameof(Delete) };
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
using Bit.Core.SecretsManager.Models.Data;
|
||||||
|
|
||||||
namespace Bit.Core.SecretsManager.Commands.AccessTokens.Interfaces;
|
namespace Bit.Core.SecretsManager.Commands.AccessTokens.Interfaces;
|
||||||
|
|
||||||
public interface ICreateAccessTokenCommand
|
public interface ICreateAccessTokenCommand
|
||||||
{
|
{
|
||||||
Task<ApiKey> CreateAsync(ApiKey apiKey);
|
Task<ApiKeyClientSecretDetails> CreateAsync(ApiKey apiKey);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,6 @@ namespace Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
|||||||
|
|
||||||
public interface IDeleteSecretCommand
|
public interface IDeleteSecretCommand
|
||||||
{
|
{
|
||||||
Task<List<Tuple<Secret, string>>> DeleteSecrets(List<Guid> ids, Guid userId);
|
Task DeleteSecrets(IEnumerable<Secret> secrets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,8 +10,8 @@ public class ApiKey : ITableObject<Guid>
|
|||||||
public Guid? ServiceAccountId { get; set; }
|
public Guid? ServiceAccountId { get; set; }
|
||||||
[MaxLength(200)]
|
[MaxLength(200)]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
[MaxLength(30)]
|
[MaxLength(128)]
|
||||||
public string ClientSecret { get; set; }
|
public string ClientSecretHash { get; set; }
|
||||||
[MaxLength(4000)]
|
[MaxLength(4000)]
|
||||||
public string Scope { get; set; }
|
public string Scope { get; set; }
|
||||||
[MaxLength(4000)]
|
[MaxLength(4000)]
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
|
||||||
|
namespace Bit.Core.SecretsManager.Models.Data;
|
||||||
|
|
||||||
|
public class ApiKeyClientSecretDetails
|
||||||
|
{
|
||||||
|
public ApiKey ApiKey { get; set; }
|
||||||
|
public string ClientSecret { get; set; }
|
||||||
|
}
|
@ -4,6 +4,8 @@ namespace Bit.Core.SecretsManager.Models.Data;
|
|||||||
|
|
||||||
public class ApiKeyDetails : ApiKey
|
public class ApiKeyDetails : ApiKey
|
||||||
{
|
{
|
||||||
|
public string ClientSecret { get; set; } // Deprecated as of 2023-05-17
|
||||||
|
|
||||||
protected ApiKeyDetails() { }
|
protected ApiKeyDetails() { }
|
||||||
|
|
||||||
protected ApiKeyDetails(ApiKey apiKey)
|
protected ApiKeyDetails(ApiKey apiKey)
|
||||||
@ -11,7 +13,7 @@ public class ApiKeyDetails : ApiKey
|
|||||||
Id = apiKey.Id;
|
Id = apiKey.Id;
|
||||||
ServiceAccountId = apiKey.ServiceAccountId;
|
ServiceAccountId = apiKey.ServiceAccountId;
|
||||||
Name = apiKey.Name;
|
Name = apiKey.Name;
|
||||||
ClientSecret = apiKey.ClientSecret;
|
ClientSecretHash = apiKey.ClientSecretHash;
|
||||||
Scope = apiKey.Scope;
|
Scope = apiKey.Scope;
|
||||||
EncryptedPayload = apiKey.EncryptedPayload;
|
EncryptedPayload = apiKey.EncryptedPayload;
|
||||||
Key = apiKey.Key;
|
Key = apiKey.Key;
|
||||||
|
@ -2786,7 +2786,7 @@
|
|||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2794,7 +2794,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -2806,9 +2806,9 @@
|
|||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2786,7 +2786,7 @@
|
|||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2794,7 +2794,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -2806,9 +2806,9 @@
|
|||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2796,7 +2796,7 @@
|
|||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2804,7 +2804,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -2816,9 +2816,9 @@
|
|||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,10 @@ using Bit.Core;
|
|||||||
using Bit.Core.Auth.Enums;
|
using Bit.Core.Auth.Enums;
|
||||||
using Bit.Core.Auth.Identity;
|
using Bit.Core.Auth.Identity;
|
||||||
using Bit.Core.Auth.Models;
|
using Bit.Core.Auth.Models;
|
||||||
|
using Bit.Core.Auth.Models.Api.Response;
|
||||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||||
|
using Bit.Core.Auth.Models.Data;
|
||||||
|
using Bit.Core.Auth.Repositories;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -43,6 +46,8 @@ public abstract class BaseRequestValidator<T> where T : class
|
|||||||
|
|
||||||
protected ICurrentContext CurrentContext { get; }
|
protected ICurrentContext CurrentContext { get; }
|
||||||
protected IPolicyService PolicyService { get; }
|
protected IPolicyService PolicyService { get; }
|
||||||
|
protected IFeatureService FeatureService { get; }
|
||||||
|
protected ISsoConfigRepository SsoConfigRepository { get; }
|
||||||
|
|
||||||
public BaseRequestValidator(
|
public BaseRequestValidator(
|
||||||
UserManager<User> userManager,
|
UserManager<User> userManager,
|
||||||
@ -58,10 +63,11 @@ public abstract class BaseRequestValidator<T> where T : class
|
|||||||
ILogger logger,
|
ILogger logger,
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings,
|
||||||
IPolicyRepository policyRepository,
|
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
IPolicyService policyService,
|
IPolicyService policyService,
|
||||||
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory)
|
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory,
|
||||||
|
IFeatureService featureService,
|
||||||
|
ISsoConfigRepository ssoConfigRepository)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_deviceRepository = deviceRepository;
|
_deviceRepository = deviceRepository;
|
||||||
@ -79,6 +85,8 @@ public abstract class BaseRequestValidator<T> where T : class
|
|||||||
PolicyService = policyService;
|
PolicyService = policyService;
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
_tokenDataFactory = tokenDataFactory;
|
_tokenDataFactory = tokenDataFactory;
|
||||||
|
FeatureService = featureService;
|
||||||
|
SsoConfigRepository = ssoConfigRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task ValidateAsync(T context, ValidatedTokenRequest request,
|
protected async Task ValidateAsync(T context, ValidatedTokenRequest request,
|
||||||
@ -199,6 +207,7 @@ public abstract class BaseRequestValidator<T> where T : class
|
|||||||
customResponse.Add("KdfIterations", user.KdfIterations);
|
customResponse.Add("KdfIterations", user.KdfIterations);
|
||||||
customResponse.Add("KdfMemory", user.KdfMemory);
|
customResponse.Add("KdfMemory", user.KdfMemory);
|
||||||
customResponse.Add("KdfParallelism", user.KdfParallelism);
|
customResponse.Add("KdfParallelism", user.KdfParallelism);
|
||||||
|
customResponse.Add("UserDecryptionOptions", await CreateUserDecryptionOptionsAsync(user, GetSubject(context)));
|
||||||
|
|
||||||
if (sendRememberToken)
|
if (sendRememberToken)
|
||||||
{
|
{
|
||||||
@ -300,6 +309,7 @@ public abstract class BaseRequestValidator<T> where T : class
|
|||||||
Dictionary<string, object> customResponse);
|
Dictionary<string, object> customResponse);
|
||||||
|
|
||||||
protected abstract void SetErrorResult(T context, Dictionary<string, object> customResponse);
|
protected abstract void SetErrorResult(T context, Dictionary<string, object> customResponse);
|
||||||
|
protected abstract ClaimsPrincipal GetSubject(T context);
|
||||||
|
|
||||||
private async Task<Tuple<bool, Organization>> RequiresTwoFactorAsync(User user, ValidatedTokenRequest request)
|
private async Task<Tuple<bool, Organization>> RequiresTwoFactorAsync(User user, ValidatedTokenRequest request)
|
||||||
{
|
{
|
||||||
@ -572,4 +582,53 @@ public abstract class BaseRequestValidator<T> where T : class
|
|||||||
|
|
||||||
return new MasterPasswordPolicyResponseModel(await PolicyService.GetMasterPasswordPolicyForUserAsync(user));
|
return new MasterPasswordPolicyResponseModel(await PolicyService.GetMasterPasswordPolicyForUserAsync(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
/// <summary>
|
||||||
|
/// Used to create a list of all possible ways the newly authenticated user can decrypt their vault contents
|
||||||
|
/// </summary>
|
||||||
|
private async Task<UserDecryptionOptions> CreateUserDecryptionOptionsAsync(User user, ClaimsPrincipal subject)
|
||||||
|
{
|
||||||
|
var ssoConfigurationData = await GetSsoConfigurationDataAsync(subject);
|
||||||
|
|
||||||
|
var userDecryptionOption = new UserDecryptionOptions
|
||||||
|
{
|
||||||
|
HasMasterPassword = !string.IsNullOrEmpty(user.MasterPassword)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ssoConfigurationData is { MemberDecryptionType: MemberDecryptionType.KeyConnector } && !string.IsNullOrEmpty(ssoConfigurationData.KeyConnectorUrl))
|
||||||
|
{
|
||||||
|
// KeyConnector makes it mutually exclusive
|
||||||
|
userDecryptionOption.KeyConnectorOption = new KeyConnectorUserDecryptionOption(ssoConfigurationData.KeyConnectorUrl);
|
||||||
|
return userDecryptionOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only add the trusted device specific option when the flag is turned on
|
||||||
|
if (FeatureService.IsEnabled(FeatureFlagKeys.TrustedDeviceEncryption, CurrentContext) && ssoConfigurationData is { MemberDecryptionType: MemberDecryptionType.TrustedDeviceEncryption })
|
||||||
|
{
|
||||||
|
var hasAdminApproval = await PolicyService.AnyPoliciesApplicableToUserAsync(user.Id, PolicyType.ResetPassword);
|
||||||
|
// TrustedDeviceEncryption only exists for SSO, but if that ever changes this value won't always be true
|
||||||
|
userDecryptionOption.TrustedDeviceOption = new TrustedDeviceUserDecryptionOption(hasAdminApproval);
|
||||||
|
}
|
||||||
|
|
||||||
|
return userDecryptionOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<SsoConfigurationData?> GetSsoConfigurationDataAsync(ClaimsPrincipal subject)
|
||||||
|
{
|
||||||
|
var organizationClaim = subject?.FindFirstValue("organizationId");
|
||||||
|
|
||||||
|
if (organizationClaim == null || !Guid.TryParse(organizationClaim, out var organizationId))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ssoConfig = await SsoConfigRepository.GetByOrganizationIdAsync(organizationId);
|
||||||
|
if (ssoConfig == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ssoConfig.GetData();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,11 +107,16 @@ public class ClientStore : IClientStore
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(apiKey.ClientSecretHash))
|
||||||
|
{
|
||||||
|
apiKey.ClientSecretHash = apiKey.ClientSecret.Sha256();
|
||||||
|
}
|
||||||
|
|
||||||
var client = new Client
|
var client = new Client
|
||||||
{
|
{
|
||||||
ClientId = clientId,
|
ClientId = clientId,
|
||||||
RequireClientSecret = true,
|
RequireClientSecret = true,
|
||||||
ClientSecrets = { new Secret(apiKey.ClientSecret.Sha256()) },
|
ClientSecrets = { new Secret(apiKey.ClientSecretHash) },
|
||||||
AllowedScopes = apiKey.GetScopes(),
|
AllowedScopes = apiKey.GetScopes(),
|
||||||
AllowedGrantTypes = GrantTypes.ClientCredentials,
|
AllowedGrantTypes = GrantTypes.ClientCredentials,
|
||||||
AccessTokenLifetime = 3600 * 1,
|
AccessTokenLifetime = 3600 * 1,
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Auth.Enums;
|
|
||||||
using Bit.Core.Auth.Identity;
|
using Bit.Core.Auth.Identity;
|
||||||
using Bit.Core.Auth.Models.Api.Response;
|
using Bit.Core.Auth.Models.Api.Response;
|
||||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||||
using Bit.Core.Auth.Models.Data;
|
|
||||||
using Bit.Core.Auth.Repositories;
|
using Bit.Core.Auth.Repositories;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
|
||||||
using Bit.Core.IdentityServer;
|
using Bit.Core.IdentityServer;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
@ -27,8 +23,6 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
|
|||||||
ICustomTokenRequestValidator
|
ICustomTokenRequestValidator
|
||||||
{
|
{
|
||||||
private readonly UserManager<User> _userManager;
|
private readonly UserManager<User> _userManager;
|
||||||
private readonly ISsoConfigRepository _ssoConfigRepository;
|
|
||||||
private readonly IFeatureService _featureService;
|
|
||||||
|
|
||||||
public CustomTokenRequestValidator(
|
public CustomTokenRequestValidator(
|
||||||
UserManager<User> userManager,
|
UserManager<User> userManager,
|
||||||
@ -44,7 +38,6 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
|
|||||||
ILogger<CustomTokenRequestValidator> logger,
|
ILogger<CustomTokenRequestValidator> logger,
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings,
|
||||||
IPolicyRepository policyRepository,
|
|
||||||
ISsoConfigRepository ssoConfigRepository,
|
ISsoConfigRepository ssoConfigRepository,
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
IPolicyService policyService,
|
IPolicyService policyService,
|
||||||
@ -52,12 +45,10 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
|
|||||||
IFeatureService featureService)
|
IFeatureService featureService)
|
||||||
: base(userManager, deviceRepository, deviceService, userService, eventService,
|
: base(userManager, deviceRepository, deviceService, userService, eventService,
|
||||||
organizationDuoWebTokenProvider, organizationRepository, organizationUserRepository,
|
organizationDuoWebTokenProvider, organizationRepository, organizationUserRepository,
|
||||||
applicationCacheService, mailService, logger, currentContext, globalSettings, policyRepository,
|
applicationCacheService, mailService, logger, currentContext, globalSettings,
|
||||||
userRepository, policyService, tokenDataFactory)
|
userRepository, policyService, tokenDataFactory, featureService, ssoConfigRepository)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_ssoConfigRepository = ssoConfigRepository;
|
|
||||||
_featureService = featureService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ValidateAsync(CustomTokenRequestValidationContext context)
|
public async Task ValidateAsync(CustomTokenRequestValidationContext context)
|
||||||
@ -96,7 +87,7 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
|
|||||||
return validatorContext.User != null;
|
return validatorContext.User != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task SetSuccessResult(CustomTokenRequestValidationContext context, User user,
|
protected override Task SetSuccessResult(CustomTokenRequestValidationContext context, User user,
|
||||||
List<Claim> claims, Dictionary<string, object> customResponse)
|
List<Claim> claims, Dictionary<string, object> customResponse)
|
||||||
{
|
{
|
||||||
context.Result.CustomResponse = customResponse;
|
context.Result.CustomResponse = customResponse;
|
||||||
@ -110,23 +101,9 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempts to find ssoConfigData for a given validate request subject
|
|
||||||
// this is actually guarenteed to pretty often be null, because more than just sso login requests will come
|
|
||||||
// through here
|
|
||||||
var ssoConfigData = await GetSsoConfigurationDataAsync(context.Result.ValidatedRequest.Subject);
|
|
||||||
|
|
||||||
// You can't put this below the user.MasterPassword != null check because TDE users can still have a MasterPassword
|
|
||||||
// It's worth noting that CurrentContext here will build a user in LaunchDarkly that is anonymous but DOES belong
|
|
||||||
// to an organization. So we will not be able to turn this feature on for only a single user, only for an entire
|
|
||||||
// organization at a time.
|
|
||||||
if (ssoConfigData != null && _featureService.IsEnabled(FeatureFlagKeys.TrustedDeviceEncryption, CurrentContext))
|
|
||||||
{
|
|
||||||
context.Result.CustomResponse["UserDecryptionOptions"] = await CreateUserDecryptionOptionsAsync(ssoConfigData, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.Result.CustomResponse == null || user.MasterPassword != null)
|
if (context.Result.CustomResponse == null || user.MasterPassword != null)
|
||||||
{
|
{
|
||||||
return;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
// KeyConnector responses below
|
// KeyConnector responses below
|
||||||
@ -141,36 +118,30 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
|
|||||||
context.Result.CustomResponse["ResetMasterPassword"] = false;
|
context.Result.CustomResponse["ResetMasterPassword"] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSO login
|
// Key connector data should have already been set in the decryption options
|
||||||
// This does a double check, that ssoConfigData is not null and that it has the KeyConnector member decryption type
|
// for backwards compatibility we set them this way too. We can eventually get rid of this
|
||||||
if (ssoConfigData is { MemberDecryptionType: MemberDecryptionType.KeyConnector } && !string.IsNullOrEmpty(ssoConfigData.KeyConnectorUrl))
|
// when all clients don't read them from the existing locations.
|
||||||
|
if (!context.Result.CustomResponse.TryGetValue("UserDecryptionOptions", out var userDecryptionOptionsObj) ||
|
||||||
|
userDecryptionOptionsObj is not UserDecryptionOptions userDecryptionOptions)
|
||||||
{
|
{
|
||||||
// TODO: Can be removed in the future
|
return Task.CompletedTask;
|
||||||
context.Result.CustomResponse["KeyConnectorUrl"] = ssoConfigData.KeyConnectorUrl;
|
}
|
||||||
// Prevent clients redirecting to set-password
|
|
||||||
|
if (userDecryptionOptions is { KeyConnectorOption: { } })
|
||||||
|
{
|
||||||
|
context.Result.CustomResponse["KeyConnectorUrl"] = userDecryptionOptions.KeyConnectorOption.KeyConnectorUrl;
|
||||||
context.Result.CustomResponse["ResetMasterPassword"] = false;
|
context.Result.CustomResponse["ResetMasterPassword"] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SsoConfigurationData?> GetSsoConfigurationDataAsync(ClaimsPrincipal? subject)
|
protected override ClaimsPrincipal GetSubject(CustomTokenRequestValidationContext context)
|
||||||
{
|
{
|
||||||
var organizationClaim = subject?.FindFirstValue("organizationId");
|
return context.Result.ValidatedRequest.Subject;
|
||||||
|
|
||||||
if (organizationClaim == null || !Guid.TryParse(organizationClaim, out var organizationId))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(organizationId);
|
|
||||||
if (ssoConfig == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ssoConfig.GetData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SetTwoFactorResult(CustomTokenRequestValidationContext context,
|
protected override void SetTwoFactorResult(CustomTokenRequestValidationContext context,
|
||||||
@ -198,29 +169,4 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
|
|||||||
context.Result.IsError = true;
|
context.Result.IsError = true;
|
||||||
context.Result.CustomResponse = customResponse;
|
context.Result.CustomResponse = customResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used to create a list of all possible ways the newly authenticated user can decrypt their vault contents
|
|
||||||
/// </summary>
|
|
||||||
private async Task<UserDecryptionOptions> CreateUserDecryptionOptionsAsync(SsoConfigurationData ssoConfigurationData, User user)
|
|
||||||
{
|
|
||||||
var userDecryptionOption = new UserDecryptionOptions();
|
|
||||||
if (ssoConfigurationData is { MemberDecryptionType: MemberDecryptionType.KeyConnector } && !string.IsNullOrEmpty(ssoConfigurationData.KeyConnectorUrl))
|
|
||||||
{
|
|
||||||
// KeyConnector makes it mutually exclusive
|
|
||||||
userDecryptionOption.KeyConnectorOption = new KeyConnectorUserDecryptionOption(ssoConfigurationData.KeyConnectorUrl);
|
|
||||||
return userDecryptionOption;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ssoConfigurationData is { MemberDecryptionType: MemberDecryptionType.TrustedDeviceEncryption })
|
|
||||||
{
|
|
||||||
var hasAdminApproval = await PolicyService.AnyPoliciesApplicableToUserAsync(user.Id, PolicyType.ResetPassword);
|
|
||||||
// TrustedDeviceEncryption only exists for SSO, but if that ever changes this value won't always be true
|
|
||||||
userDecryptionOption.TrustedDeviceOption = new TrustedDeviceUserDecryptionOption(hasAdminApproval);
|
|
||||||
}
|
|
||||||
|
|
||||||
userDecryptionOption.HasMasterPassword = !string.IsNullOrEmpty(user.MasterPassword);
|
|
||||||
|
|
||||||
return userDecryptionOption;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Bit.Core.Auth.Identity;
|
using Bit.Core.Auth.Identity;
|
||||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||||
|
using Bit.Core.Auth.Repositories;
|
||||||
using Bit.Core.Auth.Services;
|
using Bit.Core.Auth.Services;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
@ -37,16 +38,17 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner
|
|||||||
ILogger<ResourceOwnerPasswordValidator> logger,
|
ILogger<ResourceOwnerPasswordValidator> logger,
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings,
|
||||||
IPolicyRepository policyRepository,
|
|
||||||
ICaptchaValidationService captchaValidationService,
|
ICaptchaValidationService captchaValidationService,
|
||||||
IAuthRequestRepository authRequestRepository,
|
IAuthRequestRepository authRequestRepository,
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
IPolicyService policyService,
|
IPolicyService policyService,
|
||||||
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory)
|
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory,
|
||||||
|
IFeatureService featureService,
|
||||||
|
ISsoConfigRepository ssoConfigRepository)
|
||||||
: base(userManager, deviceRepository, deviceService, userService, eventService,
|
: base(userManager, deviceRepository, deviceService, userService, eventService,
|
||||||
organizationDuoWebTokenProvider, organizationRepository, organizationUserRepository,
|
organizationDuoWebTokenProvider, organizationRepository, organizationUserRepository,
|
||||||
applicationCacheService, mailService, logger, currentContext, globalSettings, policyRepository,
|
applicationCacheService, mailService, logger, currentContext, globalSettings, userRepository, policyService,
|
||||||
userRepository, policyService, tokenDataFactory)
|
tokenDataFactory, featureService, ssoConfigRepository)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
@ -166,6 +168,11 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner
|
|||||||
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, customResponse: customResponse);
|
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, customResponse: customResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override ClaimsPrincipal GetSubject(ResourceOwnerPasswordValidationContext context)
|
||||||
|
{
|
||||||
|
return context.Result.Subject;
|
||||||
|
}
|
||||||
|
|
||||||
private bool AuthEmailHeaderIsValid(ResourceOwnerPasswordValidationContext context)
|
private bool AuthEmailHeaderIsValid(ResourceOwnerPasswordValidationContext context)
|
||||||
{
|
{
|
||||||
if (!_currentContext.HttpContext.Request.Headers.ContainsKey("Auth-Email"))
|
if (!_currentContext.HttpContext.Request.Headers.ContainsKey("Auth-Email"))
|
||||||
|
@ -2808,7 +2808,7 @@
|
|||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2816,7 +2816,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -2828,9 +2828,9 @@
|
|||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2836,7 +2836,7 @@
|
|||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2844,7 +2844,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -2856,9 +2856,9 @@
|
|||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
60
src/SharedWeb/Health/HealthCheckServiceExtensions.cs
Normal file
60
src/SharedWeb/Health/HealthCheckServiceExtensions.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Bit.Core.Settings;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||||
|
|
||||||
|
namespace Bit.SharedWeb.Health;
|
||||||
|
|
||||||
|
public static class HealthCheckServiceExtensions
|
||||||
|
{
|
||||||
|
public static void AddHealthCheckServices(this IServiceCollection services, GlobalSettings globalSettings,
|
||||||
|
Action<IHealthChecksBuilder> addBuilder = null)
|
||||||
|
{
|
||||||
|
var builder = services.AddHealthChecks();
|
||||||
|
addBuilder?.Invoke(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task WriteResponse(HttpContext context, HealthReport healthReport)
|
||||||
|
{
|
||||||
|
context.Response.ContentType = "application/json; charset=utf-8";
|
||||||
|
|
||||||
|
var options = new JsonWriterOptions { Indented = true };
|
||||||
|
|
||||||
|
using var memoryStream = new MemoryStream();
|
||||||
|
using (var jsonWriter = new Utf8JsonWriter(memoryStream, options))
|
||||||
|
{
|
||||||
|
jsonWriter.WriteStartObject();
|
||||||
|
jsonWriter.WriteString("status", healthReport.Status.ToString());
|
||||||
|
jsonWriter.WriteStartObject("results");
|
||||||
|
|
||||||
|
foreach (var healthReportEntry in healthReport.Entries)
|
||||||
|
{
|
||||||
|
jsonWriter.WriteStartObject(healthReportEntry.Key);
|
||||||
|
jsonWriter.WriteString("status",
|
||||||
|
healthReportEntry.Value.Status.ToString());
|
||||||
|
jsonWriter.WriteString("description",
|
||||||
|
healthReportEntry.Value.Description ?? healthReportEntry.Value.Exception?.Message);
|
||||||
|
jsonWriter.WriteStartObject("data");
|
||||||
|
|
||||||
|
foreach (var item in healthReportEntry.Value.Data)
|
||||||
|
{
|
||||||
|
jsonWriter.WritePropertyName(item.Key);
|
||||||
|
|
||||||
|
JsonSerializer.Serialize(jsonWriter, item.Value,
|
||||||
|
item.Value?.GetType() ?? typeof(object));
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonWriter.WriteEndObject();
|
||||||
|
jsonWriter.WriteEndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonWriter.WriteEndObject();
|
||||||
|
jsonWriter.WriteEndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.Response.WriteAsync(
|
||||||
|
Encoding.UTF8.GetString(memoryStream.ToArray()));
|
||||||
|
}
|
||||||
|
}
|
@ -2786,7 +2786,7 @@
|
|||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2794,7 +2794,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
|
@ -2,7 +2,8 @@ CREATE PROCEDURE [dbo].[ApiKey_Create]
|
|||||||
@Id UNIQUEIDENTIFIER OUTPUT,
|
@Id UNIQUEIDENTIFIER OUTPUT,
|
||||||
@ServiceAccountId UNIQUEIDENTIFIER,
|
@ServiceAccountId UNIQUEIDENTIFIER,
|
||||||
@Name VARCHAR(200),
|
@Name VARCHAR(200),
|
||||||
@ClientSecret VARCHAR(30),
|
@ClientSecret VARCHAR(30) = 'migrated', -- Deprecated as of 2023-05-17
|
||||||
|
@ClientSecretHash VARCHAR(128) = NULL,
|
||||||
@Scope NVARCHAR(4000),
|
@Scope NVARCHAR(4000),
|
||||||
@EncryptedPayload NVARCHAR(4000),
|
@EncryptedPayload NVARCHAR(4000),
|
||||||
@Key VARCHAR(MAX),
|
@Key VARCHAR(MAX),
|
||||||
@ -13,12 +14,19 @@ AS
|
|||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
IF (@ClientSecretHash IS NULL)
|
||||||
|
BEGIN
|
||||||
|
DECLARE @hb VARBINARY(128) = HASHBYTES('SHA2_256', @ClientSecret);
|
||||||
|
SET @ClientSecretHash = CAST(N'' as xml).value('xs:base64Binary(sql:variable("@hb"))', 'VARCHAR(128)');
|
||||||
|
END
|
||||||
|
|
||||||
INSERT INTO [dbo].[ApiKey]
|
INSERT INTO [dbo].[ApiKey]
|
||||||
(
|
(
|
||||||
[Id],
|
[Id],
|
||||||
[ServiceAccountId],
|
[ServiceAccountId],
|
||||||
[Name],
|
[Name],
|
||||||
[ClientSecret],
|
[ClientSecret],
|
||||||
|
[ClientSecretHash],
|
||||||
[Scope],
|
[Scope],
|
||||||
[EncryptedPayload],
|
[EncryptedPayload],
|
||||||
[Key],
|
[Key],
|
||||||
@ -32,6 +40,7 @@ BEGIN
|
|||||||
@ServiceAccountId,
|
@ServiceAccountId,
|
||||||
@Name,
|
@Name,
|
||||||
@ClientSecret,
|
@ClientSecret,
|
||||||
|
@ClientSecretHash,
|
||||||
@Scope,
|
@Scope,
|
||||||
@EncryptedPayload,
|
@EncryptedPayload,
|
||||||
@Key,
|
@Key,
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
CREATE TABLE [dbo].[ApiKey] (
|
CREATE TABLE [dbo].[ApiKey] (
|
||||||
[Id] UNIQUEIDENTIFIER,
|
[Id] UNIQUEIDENTIFIER,
|
||||||
[ServiceAccountId] UNIQUEIDENTIFIER NULL,
|
[ServiceAccountId] UNIQUEIDENTIFIER NULL,
|
||||||
[Name] VARCHAR(200) NOT NULL,
|
[Name] VARCHAR(200) NOT NULL,
|
||||||
[ClientSecret] VARCHAR(30) NOT NULL,
|
[ClientSecret] VARCHAR(30) NOT NULL,
|
||||||
[Scope] NVARCHAR (4000) NOT NULL,
|
[ClientSecretHash] VARCHAR(128) NULL,
|
||||||
[EncryptedPayload] NVARCHAR (4000) NOT NULL,
|
[Scope] NVARCHAR (4000) NOT NULL,
|
||||||
[Key] VARCHAR (MAX) NOT NULL,
|
[EncryptedPayload] NVARCHAR (4000) NOT NULL,
|
||||||
[ExpireAt] DATETIME2(7) NULL,
|
[Key] VARCHAR (MAX) NOT NULL,
|
||||||
[CreationDate] DATETIME2(7) NOT NULL,
|
[ExpireAt] DATETIME2(7) NULL,
|
||||||
[RevisionDate] DATETIME2(7) NOT NULL,
|
[CreationDate] DATETIME2(7) NOT NULL,
|
||||||
|
[RevisionDate] DATETIME2(7) NOT NULL,
|
||||||
CONSTRAINT [PK_ApiKey] PRIMARY KEY CLUSTERED ([Id] ASC),
|
CONSTRAINT [PK_ApiKey] PRIMARY KEY CLUSTERED ([Id] ASC),
|
||||||
CONSTRAINT [FK_ApiKey_ServiceAccountId] FOREIGN KEY ([ServiceAccountId]) REFERENCES [dbo].[ServiceAccount] ([Id])
|
CONSTRAINT [FK_ApiKey_ServiceAccountId] FOREIGN KEY ([ServiceAccountId]) REFERENCES [dbo].[ServiceAccount] ([Id])
|
||||||
);
|
);
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
CREATE FUNCTION [dbo].[PolicyApplicableToUser]
|
|
||||||
(
|
|
||||||
@UserId UNIQUEIDENTIFIER,
|
|
||||||
@PolicyType TINYINT,
|
|
||||||
@MinimumStatus SMALLINT
|
|
||||||
)
|
|
||||||
RETURNS TABLE
|
|
||||||
AS RETURN
|
|
||||||
SELECT
|
|
||||||
P.*
|
|
||||||
FROM
|
|
||||||
[dbo].[PolicyView] P
|
|
||||||
INNER JOIN
|
|
||||||
[dbo].[OrganizationUserView] OU ON P.[OrganizationId] = OU.[OrganizationId]
|
|
||||||
LEFT JOIN
|
|
||||||
(SELECT
|
|
||||||
PU.UserId,
|
|
||||||
PO.OrganizationId
|
|
||||||
FROM
|
|
||||||
[dbo].[ProviderUserView] PU
|
|
||||||
INNER JOIN
|
|
||||||
[ProviderOrganizationView] PO ON PO.[ProviderId] = PU.[ProviderId]) PUPO
|
|
||||||
ON PUPO.UserId = OU.UserId
|
|
||||||
AND PUPO.OrganizationId = P.OrganizationId
|
|
||||||
WHERE
|
|
||||||
(
|
|
||||||
(
|
|
||||||
OU.[Status] != 0 -- OrgUsers who have accepted their invite and are linked to a UserId
|
|
||||||
AND OU.[UserId] = @UserId
|
|
||||||
)
|
|
||||||
OR (
|
|
||||||
OU.[Status] = 0 -- 'Invited' OrgUsers are not linked to a UserId yet, so we have to look up their email
|
|
||||||
AND OU.[Email] IN (SELECT U.Email FROM [dbo].[UserView] U WHERE U.Id = @UserId)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
AND P.[Type] = @PolicyType
|
|
||||||
AND P.[Enabled] = 1
|
|
||||||
AND OU.[Status] >= @MinimumStatus
|
|
||||||
AND OU.[Type] >= 2 -- Not an owner (0) or admin (1)
|
|
||||||
AND ( -- Can't manage policies
|
|
||||||
OU.[Permissions] IS NULL
|
|
||||||
OR COALESCE(JSON_VALUE(OU.[Permissions], '$.managePolicies'), 'false') = 'false'
|
|
||||||
)
|
|
||||||
AND PUPO.[UserId] IS NULL -- Not a provider
|
|
@ -1,41 +0,0 @@
|
|||||||
CREATE PROCEDURE [dbo].[Collection_CreateWithGroups]
|
|
||||||
@Id UNIQUEIDENTIFIER,
|
|
||||||
@OrganizationId UNIQUEIDENTIFIER,
|
|
||||||
@Name VARCHAR(MAX),
|
|
||||||
@ExternalId NVARCHAR(300),
|
|
||||||
@CreationDate DATETIME2(7),
|
|
||||||
@RevisionDate DATETIME2(7),
|
|
||||||
@Groups AS [dbo].[SelectionReadOnlyArray] READONLY
|
|
||||||
AS
|
|
||||||
BEGIN
|
|
||||||
SET NOCOUNT ON
|
|
||||||
|
|
||||||
EXEC [dbo].[Collection_Create] @Id, @OrganizationId, @Name, @ExternalId, @CreationDate, @RevisionDate
|
|
||||||
|
|
||||||
;WITH [AvailableGroupsCTE] AS(
|
|
||||||
SELECT
|
|
||||||
[Id]
|
|
||||||
FROM
|
|
||||||
[dbo].[Group]
|
|
||||||
WHERE
|
|
||||||
[OrganizationId] = @OrganizationId
|
|
||||||
)
|
|
||||||
INSERT INTO [dbo].[CollectionGroup]
|
|
||||||
(
|
|
||||||
[CollectionId],
|
|
||||||
[GroupId],
|
|
||||||
[ReadOnly],
|
|
||||||
[HidePasswords]
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
@Id,
|
|
||||||
[Id],
|
|
||||||
[ReadOnly],
|
|
||||||
[HidePasswords]
|
|
||||||
FROM
|
|
||||||
@Groups
|
|
||||||
WHERE
|
|
||||||
[Id] IN (SELECT [Id] FROM [AvailableGroupsCTE])
|
|
||||||
|
|
||||||
EXEC [dbo].[User_BumpAccountRevisionDateByOrganizationId] @OrganizationId
|
|
||||||
END
|
|
@ -1,17 +0,0 @@
|
|||||||
CREATE PROCEDURE [dbo].[Collection_ReadWithGroupsById]
|
|
||||||
@Id UNIQUEIDENTIFIER
|
|
||||||
AS
|
|
||||||
BEGIN
|
|
||||||
SET NOCOUNT ON
|
|
||||||
|
|
||||||
EXEC [dbo].[Collection_ReadById] @Id
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
[GroupId] [Id],
|
|
||||||
[ReadOnly],
|
|
||||||
[HidePasswords]
|
|
||||||
FROM
|
|
||||||
[dbo].[CollectionGroup]
|
|
||||||
WHERE
|
|
||||||
[CollectionId] = @Id
|
|
||||||
END
|
|
@ -1,18 +0,0 @@
|
|||||||
CREATE PROCEDURE [dbo].[Collection_ReadWithGroupsByIdUserId]
|
|
||||||
@Id UNIQUEIDENTIFIER,
|
|
||||||
@UserId UNIQUEIDENTIFIER
|
|
||||||
AS
|
|
||||||
BEGIN
|
|
||||||
SET NOCOUNT ON
|
|
||||||
|
|
||||||
EXEC [dbo].[Collection_ReadByIdUserId] @Id, @UserId
|
|
||||||
|
|
||||||
SELECT
|
|
||||||
[GroupId] [Id],
|
|
||||||
[ReadOnly],
|
|
||||||
[HidePasswords]
|
|
||||||
FROM
|
|
||||||
[dbo].[CollectionGroup]
|
|
||||||
WHERE
|
|
||||||
[CollectionId] = @Id
|
|
||||||
END
|
|
@ -1,51 +0,0 @@
|
|||||||
CREATE PROCEDURE [dbo].[Collection_UpdateWithGroups]
|
|
||||||
@Id UNIQUEIDENTIFIER,
|
|
||||||
@OrganizationId UNIQUEIDENTIFIER,
|
|
||||||
@Name VARCHAR(MAX),
|
|
||||||
@ExternalId NVARCHAR(300),
|
|
||||||
@CreationDate DATETIME2(7),
|
|
||||||
@RevisionDate DATETIME2(7),
|
|
||||||
@Groups AS [dbo].[SelectionReadOnlyArray] READONLY
|
|
||||||
AS
|
|
||||||
BEGIN
|
|
||||||
SET NOCOUNT ON
|
|
||||||
|
|
||||||
EXEC [dbo].[Collection_Update] @Id, @OrganizationId, @Name, @ExternalId, @CreationDate, @RevisionDate
|
|
||||||
|
|
||||||
;WITH [AvailableGroupsCTE] AS(
|
|
||||||
SELECT
|
|
||||||
Id
|
|
||||||
FROM
|
|
||||||
[dbo].[Group]
|
|
||||||
WHERE
|
|
||||||
OrganizationId = @OrganizationId
|
|
||||||
)
|
|
||||||
MERGE
|
|
||||||
[dbo].[CollectionGroup] AS [Target]
|
|
||||||
USING
|
|
||||||
@Groups AS [Source]
|
|
||||||
ON
|
|
||||||
[Target].[CollectionId] = @Id
|
|
||||||
AND [Target].[GroupId] = [Source].[Id]
|
|
||||||
WHEN NOT MATCHED BY TARGET
|
|
||||||
AND [Source].[Id] IN (SELECT [Id] FROM [AvailableGroupsCTE]) THEN
|
|
||||||
INSERT VALUES
|
|
||||||
(
|
|
||||||
@Id,
|
|
||||||
[Source].[Id],
|
|
||||||
[Source].[ReadOnly],
|
|
||||||
[Source].[HidePasswords]
|
|
||||||
)
|
|
||||||
WHEN MATCHED AND (
|
|
||||||
[Target].[ReadOnly] != [Source].[ReadOnly]
|
|
||||||
OR [Target].[HidePasswords] != [Source].[HidePasswords]
|
|
||||||
) THEN
|
|
||||||
UPDATE SET [Target].[ReadOnly] = [Source].[ReadOnly],
|
|
||||||
[Target].[HidePasswords] = [Source].[HidePasswords]
|
|
||||||
WHEN NOT MATCHED BY SOURCE
|
|
||||||
AND [Target].[CollectionId] = @Id THEN
|
|
||||||
DELETE
|
|
||||||
;
|
|
||||||
|
|
||||||
EXEC [dbo].[User_BumpAccountRevisionDateByCollectionId] @Id, @OrganizationId
|
|
||||||
END
|
|
@ -1,11 +0,0 @@
|
|||||||
CREATE PROCEDURE [dbo].[Policy_CountByTypeApplicableToUser]
|
|
||||||
@UserId UNIQUEIDENTIFIER,
|
|
||||||
@PolicyType TINYINT,
|
|
||||||
@MinimumStatus SMALLINT
|
|
||||||
AS
|
|
||||||
BEGIN
|
|
||||||
SET NOCOUNT ON
|
|
||||||
|
|
||||||
SELECT COUNT(1)
|
|
||||||
FROM [dbo].[PolicyApplicableToUser](@UserId, @PolicyType, @MinimumStatus)
|
|
||||||
END
|
|
@ -1,11 +0,0 @@
|
|||||||
CREATE PROCEDURE [dbo].[Policy_ReadByTypeApplicableToUser]
|
|
||||||
@UserId UNIQUEIDENTIFIER,
|
|
||||||
@PolicyType TINYINT,
|
|
||||||
@MinimumStatus SMALLINT
|
|
||||||
AS
|
|
||||||
BEGIN
|
|
||||||
SET NOCOUNT ON
|
|
||||||
|
|
||||||
SELECT *
|
|
||||||
FROM [dbo].[PolicyApplicableToUser](@UserId, @PolicyType, @MinimumStatus)
|
|
||||||
END
|
|
42
src/Sql/dbo_future/Stored Procedures/ApiKey_Create.sql
Normal file
42
src/Sql/dbo_future/Stored Procedures/ApiKey_Create.sql
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[ApiKey_Create]
|
||||||
|
@Id UNIQUEIDENTIFIER OUTPUT,
|
||||||
|
@ServiceAccountId UNIQUEIDENTIFIER,
|
||||||
|
@Name VARCHAR(200),
|
||||||
|
@ClientSecretHash VARCHAR(128),
|
||||||
|
@Scope NVARCHAR(4000),
|
||||||
|
@EncryptedPayload NVARCHAR(4000),
|
||||||
|
@Key VARCHAR(MAX),
|
||||||
|
@ExpireAt DATETIME2(7),
|
||||||
|
@CreationDate DATETIME2(7),
|
||||||
|
@RevisionDate DATETIME2(7)
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
INSERT INTO [dbo].[ApiKey]
|
||||||
|
(
|
||||||
|
[Id],
|
||||||
|
[ServiceAccountId],
|
||||||
|
[Name],
|
||||||
|
[ClientSecretHash],
|
||||||
|
[Scope],
|
||||||
|
[EncryptedPayload],
|
||||||
|
[Key],
|
||||||
|
[ExpireAt],
|
||||||
|
[CreationDate],
|
||||||
|
[RevisionDate]
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
@Id,
|
||||||
|
@ServiceAccountId,
|
||||||
|
@Name,
|
||||||
|
@ClientSecretHash,
|
||||||
|
@Scope,
|
||||||
|
@EncryptedPayload,
|
||||||
|
@Key,
|
||||||
|
@ExpireAt,
|
||||||
|
@CreationDate,
|
||||||
|
@RevisionDate
|
||||||
|
)
|
||||||
|
END
|
18
src/Sql/dbo_future/Tables/ApiKey.sql
Normal file
18
src/Sql/dbo_future/Tables/ApiKey.sql
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
CREATE TABLE [dbo].[ApiKey] (
|
||||||
|
[Id] UNIQUEIDENTIFIER,
|
||||||
|
[ServiceAccountId] UNIQUEIDENTIFIER NULL,
|
||||||
|
[Name] VARCHAR(200) NOT NULL,
|
||||||
|
[ClientSecretHash] VARCHAR(128) NULL,
|
||||||
|
[Scope] NVARCHAR (4000) NOT NULL,
|
||||||
|
[EncryptedPayload] NVARCHAR (4000) NOT NULL,
|
||||||
|
[Key] VARCHAR (MAX) NOT NULL,
|
||||||
|
[ExpireAt] DATETIME2(7) NULL,
|
||||||
|
[CreationDate] DATETIME2(7) NOT NULL,
|
||||||
|
[RevisionDate] DATETIME2(7) NOT NULL,
|
||||||
|
CONSTRAINT [PK_ApiKey] PRIMARY KEY CLUSTERED ([Id] ASC),
|
||||||
|
CONSTRAINT [FK_ApiKey_ServiceAccountId] FOREIGN KEY ([ServiceAccountId]) REFERENCES [dbo].[ServiceAccount] ([Id])
|
||||||
|
);
|
||||||
|
|
||||||
|
GO
|
||||||
|
CREATE NONCLUSTERED INDEX [IX_ApiKey_ServiceAccountId]
|
||||||
|
ON [dbo].[ApiKey]([ServiceAccountId] ASC);
|
@ -644,10 +644,28 @@ public class SecretsControllerTests : IClassFixture<ApiApplicationFactory>, IAsy
|
|||||||
});
|
});
|
||||||
var secretIds = new[] { secret.Id };
|
var secretIds = new[] { secret.Id };
|
||||||
|
|
||||||
var response = await _client.PostAsJsonAsync($"/secrets/{org.Id}/delete", secretIds);
|
var response = await _client.PostAsJsonAsync($"/secrets/delete", secretIds);
|
||||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Delete_MissingAccessPolicy_AccessDenied()
|
||||||
|
{
|
||||||
|
var (org, _) = await _organizationHelper.Initialize(true, true);
|
||||||
|
var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
|
||||||
|
await LoginAsync(email);
|
||||||
|
|
||||||
|
var (_, secretIds) = await CreateSecretsAsync(org.Id, 3);
|
||||||
|
|
||||||
|
var response = await _client.PostAsync("/secrets/delete", JsonContent.Create(secretIds));
|
||||||
|
|
||||||
|
var results = await response.Content.ReadFromJsonAsync<ListResponseModel<BulkDeleteResponseModel>>();
|
||||||
|
Assert.NotNull(results);
|
||||||
|
Assert.Equal(secretIds.OrderBy(x => x),
|
||||||
|
results!.Data.Select(x => x.Id).OrderBy(x => x));
|
||||||
|
Assert.All(results.Data, item => Assert.Equal("access denied", item.Error));
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(PermissionType.RunAsAdmin)]
|
[InlineData(PermissionType.RunAsAdmin)]
|
||||||
[InlineData(PermissionType.RunAsUserWithPermission)]
|
[InlineData(PermissionType.RunAsUserWithPermission)]
|
||||||
@ -656,12 +674,7 @@ public class SecretsControllerTests : IClassFixture<ApiApplicationFactory>, IAsy
|
|||||||
var (org, _) = await _organizationHelper.Initialize(true, true);
|
var (org, _) = await _organizationHelper.Initialize(true, true);
|
||||||
await LoginAsync(_email);
|
await LoginAsync(_email);
|
||||||
|
|
||||||
var project = await _projectRepository.CreateAsync(new Project()
|
var (project, secretIds) = await CreateSecretsAsync(org.Id, 3);
|
||||||
{
|
|
||||||
Id = new Guid(),
|
|
||||||
OrganizationId = org.Id,
|
|
||||||
Name = _mockEncryptedString
|
|
||||||
});
|
|
||||||
|
|
||||||
if (permissionType == PermissionType.RunAsUserWithPermission)
|
if (permissionType == PermissionType.RunAsUserWithPermission)
|
||||||
{
|
{
|
||||||
@ -678,21 +691,6 @@ public class SecretsControllerTests : IClassFixture<ApiApplicationFactory>, IAsy
|
|||||||
await _accessPolicyRepository.CreateManyAsync(accessPolicies);
|
await _accessPolicyRepository.CreateManyAsync(accessPolicies);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var secretIds = new List<Guid>();
|
|
||||||
for (var i = 0; i < 3; i++)
|
|
||||||
{
|
|
||||||
var secret = await _secretRepository.CreateAsync(new Secret
|
|
||||||
{
|
|
||||||
OrganizationId = org.Id,
|
|
||||||
Key = _mockEncryptedString,
|
|
||||||
Value = _mockEncryptedString,
|
|
||||||
Note = _mockEncryptedString,
|
|
||||||
Projects = new List<Project>() { project }
|
|
||||||
});
|
|
||||||
secretIds.Add(secret.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = await _client.PostAsJsonAsync($"/secrets/delete", secretIds);
|
var response = await _client.PostAsJsonAsync($"/secrets/delete", secretIds);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
@ -710,4 +708,30 @@ public class SecretsControllerTests : IClassFixture<ApiApplicationFactory>, IAsy
|
|||||||
var secrets = await _secretRepository.GetManyByIds(secretIds);
|
var secrets = await _secretRepository.GetManyByIds(secretIds);
|
||||||
Assert.Empty(secrets);
|
Assert.Empty(secrets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<(Project Project, List<Guid> secretIds)> CreateSecretsAsync(Guid orgId, int numberToCreate = 3)
|
||||||
|
{
|
||||||
|
var project = await _projectRepository.CreateAsync(new Project
|
||||||
|
{
|
||||||
|
Id = new Guid(),
|
||||||
|
OrganizationId = orgId,
|
||||||
|
Name = _mockEncryptedString
|
||||||
|
});
|
||||||
|
|
||||||
|
var secretIds = new List<Guid>();
|
||||||
|
for (var i = 0; i < numberToCreate; i++)
|
||||||
|
{
|
||||||
|
var secret = await _secretRepository.CreateAsync(new Secret
|
||||||
|
{
|
||||||
|
OrganizationId = orgId,
|
||||||
|
Key = _mockEncryptedString,
|
||||||
|
Value = _mockEncryptedString,
|
||||||
|
Note = _mockEncryptedString,
|
||||||
|
Projects = new List<Project>() { project }
|
||||||
|
});
|
||||||
|
secretIds.Add(secret.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (project, secretIds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,74 @@
|
|||||||
"resolved": "2.4.3",
|
"resolved": "2.4.3",
|
||||||
"contentHash": "kZZSmOmKA8OBlAJaquPXnJJLM9RwQ27H7BMVqfMLUcTi9xHinWGJiWksa3D4NEtz0wZ/nxd2mogObvBgJKCRhQ=="
|
"contentHash": "kZZSmOmKA8OBlAJaquPXnJJLM9RwQ27H7BMVqfMLUcTi9xHinWGJiWksa3D4NEtz0wZ/nxd2mogObvBgJKCRhQ=="
|
||||||
},
|
},
|
||||||
|
"AspNetCore.HealthChecks.AzureServiceBus": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.1.0",
|
||||||
|
"contentHash": "LepLE6NO4bLBVDzlx/730pD6jnfkV6zaaRUrbN1LqnNk4m1hROsv7wOpgbKgVDgYIfeLzdiVnBviEevSxWFKMQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Azure.Messaging.EventHubs": "5.7.4",
|
||||||
|
"Azure.Messaging.ServiceBus": "7.11.1",
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.AzureStorage": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.1.2",
|
||||||
|
"contentHash": "R/uHJ40Cc0fBLi48SqDtT6fHyR5G8L3+PeKlbe8t498GLebeBIR3ve4l4n7UzCD0qgmQDDvyIYvVywx3i5Y6Ng==",
|
||||||
|
"dependencies": {
|
||||||
|
"Azure.Storage.Blobs": "12.14.1",
|
||||||
|
"Azure.Storage.Files.Shares": "12.11.0",
|
||||||
|
"Azure.Storage.Queues": "12.11.1",
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.Network": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.4",
|
||||||
|
"contentHash": "rvoPkqlvhX1HW6dpqjE1rbvmmMo9v7+Uf9dJffEcd3mA/DyyEitlZFc6cwYtmZVFdgy2gbIU4ubs3654nVfvjA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.7",
|
||||||
|
"SSH.NET": "2020.0.2",
|
||||||
|
"System.Buffers": "4.5.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.Redis": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.4",
|
||||||
|
"contentHash": "adNNWF6kV8v1HLTmF3b9F5K6ubvgx+S7VqhzA8T/5YuIpRWsCDk8+q3RIDDV8Twvl9pRahLfzCbFrPYxvzmk7g==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.4",
|
||||||
|
"StackExchange.Redis": "2.5.61"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.SendGrid": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.2",
|
||||||
|
"contentHash": "VgskjkCUmSpAxil20rZlrj14bMi9aFNdiGLDtDTKjkUU0GYkoyi4HRVEy9Gp0FIgu9ce7quN+dNCpydKvMxjqA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.2",
|
||||||
|
"Microsoft.Extensions.Http": "6.0.0",
|
||||||
|
"SendGrid": "9.24.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.SqlServer": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.2",
|
||||||
|
"contentHash": "Af7ws27DnZZ4bKCiEREm7emSAKEtIiYirEAkI0ixFgK1fwJ99jmMnPC+kU01zfqn3FyCO/gZOUO7WbyVvTPpFg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Data.SqlClient": "3.0.1",
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.Uris": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.3",
|
||||||
|
"contentHash": "EY0Vh8s2UrbnyvM/QhbyYuCnbrBw36BKkdh5LqdINxqAGnlPFQXf+/UoNlH/76MTEyg+nvdp2wjr5MqWDkVFaQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.0",
|
||||||
|
"Microsoft.Extensions.Http": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"AspNetCoreRateLimit": {
|
"AspNetCoreRateLimit": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.0.2",
|
"resolved": "4.0.2",
|
||||||
@ -134,6 +202,14 @@
|
|||||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Azure.Core.Amqp": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.2.0",
|
||||||
|
"contentHash": "vrF4isvhwdZspzorLwYhukXz3DA8/ONSnZUIBAqBtOCzsDNUgAsuILbCzvtDrn2oDxyq7DZx5Nh81pe0BeWmDQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Memory": "4.5.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": {
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.3.2",
|
"resolved": "1.3.2",
|
||||||
@ -168,6 +244,34 @@
|
|||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Azure.Messaging.EventHubs": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "5.7.4",
|
||||||
|
"contentHash": "8vC4efO5HzDgZjx6LaViScywbyKu3xIkL+y+QoyN7Yo6u1pEmMAPW4ptaWIj1JW4gypeWC1tFy+U3zdQ/E7bGA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Azure.Core": "1.25.0",
|
||||||
|
"Azure.Core.Amqp": "1.2.0",
|
||||||
|
"Microsoft.Azure.Amqp": "2.5.12",
|
||||||
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
|
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
||||||
|
"System.Memory.Data": "1.0.2",
|
||||||
|
"System.Reflection.TypeExtensions": "4.7.0",
|
||||||
|
"System.Threading.Channels": "4.7.1",
|
||||||
|
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Azure.Messaging.ServiceBus": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "7.11.1",
|
||||||
|
"contentHash": "ioGedXeH8KK4HdTDEyOzdgNbEXGstGItVljI1EKYsz08sgwej6LpODCZmwPR2ui1fjXBWt8Zea0RJT4d9LwlMg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Azure.Core": "1.25.0",
|
||||||
|
"Azure.Core.Amqp": "1.2.0",
|
||||||
|
"Microsoft.Azure.Amqp": "2.5.12",
|
||||||
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
|
"System.Memory.Data": "1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Azure.Storage.Blobs": {
|
"Azure.Storage.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.14.1",
|
"resolved": "12.14.1",
|
||||||
@ -186,6 +290,15 @@
|
|||||||
"System.IO.Hashing": "6.0.0"
|
"System.IO.Hashing": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Azure.Storage.Files.Shares": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "12.11.0",
|
||||||
|
"contentHash": "C747FRSZNe/L4hu1wrvzQImVaIfNDcZXfttaV3FwX96+TsbgXotHe6Y0lmSu65H/gVYKt07sIW9E1mDi3bdADw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Azure.Storage.Common": "12.12.0",
|
||||||
|
"System.Text.Json": "4.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Azure.Storage.Queues": {
|
"Azure.Storage.Queues": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.12.0",
|
"resolved": "12.12.0",
|
||||||
@ -488,12 +601,8 @@
|
|||||||
},
|
},
|
||||||
"Microsoft.Azure.Amqp": {
|
"Microsoft.Azure.Amqp": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "2.4.11",
|
"resolved": "2.5.12",
|
||||||
"contentHash": "7x5fu2f6TLQDDJS0sY5qW8/daFwJaY9O75YvU8RcUfRzbug+9YGjXUBxoRrprgyi0jxdBAMQL05p1s783SOSFQ==",
|
"contentHash": "0SlEl+TSQdpjXWf9/37dXWAa0zk6R1EJKmGtGZeKUAH7WEQpJOWMxJ9I43igcBCnTkFwa28CdPnpSCjFZVQlkw=="
|
||||||
"dependencies": {
|
|
||||||
"System.Net.WebSockets.Client": "4.0.2",
|
|
||||||
"System.Runtime.Serialization.Primitives": "4.1.1"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Microsoft.Azure.Cosmos": {
|
"Microsoft.Azure.Cosmos": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
@ -843,6 +952,22 @@
|
|||||||
"System.Text.Json": "6.0.0"
|
"System.Text.Json": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.10",
|
||||||
|
"contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10",
|
||||||
|
"Microsoft.Extensions.Hosting.Abstractions": "6.0.0",
|
||||||
|
"Microsoft.Extensions.Logging.Abstractions": "6.0.2",
|
||||||
|
"Microsoft.Extensions.Options": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.10",
|
||||||
|
"contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA=="
|
||||||
|
},
|
||||||
"Microsoft.Extensions.FileProviders.Abstractions": {
|
"Microsoft.Extensions.FileProviders.Abstractions": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "6.0.0",
|
"resolved": "6.0.0",
|
||||||
@ -949,8 +1074,8 @@
|
|||||||
},
|
},
|
||||||
"Microsoft.Extensions.Logging.Abstractions": {
|
"Microsoft.Extensions.Logging.Abstractions": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "6.0.1",
|
"resolved": "6.0.2",
|
||||||
"contentHash": "dzB2Cgg+JmrouhjkcQGzSFjjvpwlq353i8oBQO2GWNjCXSzhbtBRUf28HSauWe7eib3wYOdb3tItdjRwAdwCSg=="
|
"contentHash": "pwXCZKaA7m5wgmCj49dW+H1RPSY7U62SKLTQYCcavf/k3Nyt/WnBgAjG4jMGnwy9rElfAZ2KvxvM5CJzJWG0hg=="
|
||||||
},
|
},
|
||||||
"Microsoft.Extensions.Logging.Configuration": {
|
"Microsoft.Extensions.Logging.Configuration": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
@ -1672,10 +1797,23 @@
|
|||||||
"SQLitePCLRaw.core": "2.1.2"
|
"SQLitePCLRaw.core": "2.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"SSH.NET": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "2020.0.2",
|
||||||
|
"contentHash": "G0dNlTBAM00KZXv1wWVwgg26d9/METcM6qWBpNQwllzQmmbu+Zu+FS1L1X4fFgGdPu3e8k9mmTBu6SwtQ0614g==",
|
||||||
|
"dependencies": {
|
||||||
|
"SshNet.Security.Cryptography": "[1.3.0]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SshNet.Security.Cryptography": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.3.0",
|
||||||
|
"contentHash": "5pBIXRjcSO/amY8WztpmNOhaaCNHY/B6CcYDI7FSTgqSyo/ZUojlLiKcsl+YGbxQuLX439qIkMfP0PHqxqJi/Q=="
|
||||||
|
},
|
||||||
"StackExchange.Redis": {
|
"StackExchange.Redis": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "2.5.43",
|
"resolved": "2.5.61",
|
||||||
"contentHash": "YQ38jVbX1b5mBi6lizESou+NpV6QZpeo6ofRR6qeuqJ8ePOmhcwhje3nDTNIGEkfPSK0sLuF6pR5rtFyq2F46g==",
|
"contentHash": "h1Gz4itrHL/PQ0GBLTEiPK8bBkOp5SFO6iaRFSSn/x1qltBWENsz/NUxPid6WHX9yf2Tiyzn9D3R7mtnksODxg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Pipelines.Sockets.Unofficial": "2.2.2",
|
"Pipelines.Sockets.Unofficial": "2.2.2",
|
||||||
"System.Diagnostics.PerformanceCounter": "5.0.0"
|
"System.Diagnostics.PerformanceCounter": "5.0.0"
|
||||||
@ -2383,42 +2521,6 @@
|
|||||||
"System.Runtime.Extensions": "4.1.0"
|
"System.Runtime.Extensions": "4.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"System.Net.WebSockets": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "4.0.0",
|
|
||||||
"contentHash": "2KJo8hir6Edi9jnMDAMhiJoI691xRBmKcbNpwjrvpIMOCTYOtBpSsSEGBxBDV7PKbasJNaFp1+PZz1D7xS41Hg==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Win32.Primitives": "4.0.1",
|
|
||||||
"System.Resources.ResourceManager": "4.0.1",
|
|
||||||
"System.Runtime": "4.1.0",
|
|
||||||
"System.Threading.Tasks": "4.0.11"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"System.Net.WebSockets.Client": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "4.0.2",
|
|
||||||
"contentHash": "NUCcDroX4lCQXgOrzlwIZ1u9YJ0krfyF0wk0ONnyLUmcQoEiYV2/OfUPRqUwQBbpH1BlGApkLgoQUwMqb5+c1g==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.NETCore.Platforms": "1.0.2",
|
|
||||||
"Microsoft.Win32.Primitives": "4.0.1",
|
|
||||||
"System.Collections": "4.0.11",
|
|
||||||
"System.Diagnostics.Debug": "4.0.11",
|
|
||||||
"System.Diagnostics.Tracing": "4.1.0",
|
|
||||||
"System.Globalization": "4.0.11",
|
|
||||||
"System.Net.Primitives": "4.0.11",
|
|
||||||
"System.Net.WebHeaderCollection": "4.0.1",
|
|
||||||
"System.Net.WebSockets": "4.0.0",
|
|
||||||
"System.Resources.ResourceManager": "4.0.1",
|
|
||||||
"System.Runtime": "4.1.0",
|
|
||||||
"System.Runtime.Extensions": "4.1.0",
|
|
||||||
"System.Runtime.Handles": "4.0.1",
|
|
||||||
"System.Runtime.InteropServices": "4.1.0",
|
|
||||||
"System.Security.Cryptography.X509Certificates": "4.1.0",
|
|
||||||
"System.Text.Encoding": "4.0.11",
|
|
||||||
"System.Threading": "4.0.11",
|
|
||||||
"System.Threading.Tasks": "4.0.11"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"System.Numerics.Vectors": {
|
"System.Numerics.Vectors": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.5.0",
|
"resolved": "4.5.0",
|
||||||
@ -2540,12 +2642,8 @@
|
|||||||
},
|
},
|
||||||
"System.Reflection.TypeExtensions": {
|
"System.Reflection.TypeExtensions": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.3.0",
|
"resolved": "4.7.0",
|
||||||
"contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==",
|
"contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA=="
|
||||||
"dependencies": {
|
|
||||||
"System.Reflection": "4.3.0",
|
|
||||||
"System.Runtime": "4.3.0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"System.Resources.ResourceManager": {
|
"System.Resources.ResourceManager": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
@ -2933,6 +3031,11 @@
|
|||||||
"System.Threading.Tasks": "4.3.0"
|
"System.Threading.Tasks": "4.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"System.Threading.Channels": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.7.1",
|
||||||
|
"contentHash": "6akRtHK/wab3246t4p5v3HQrtQk8LboOt5T4dtpNgsp3zvDeM4/Gx8V12t0h+c/W9/enUrilk8n6EQqdQorZAA=="
|
||||||
|
},
|
||||||
"System.Threading.Overlapped": {
|
"System.Threading.Overlapped": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.0.1",
|
"resolved": "4.0.1",
|
||||||
@ -3170,126 +3273,133 @@
|
|||||||
"api": {
|
"api": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Messaging.EventGrid": "4.10.0",
|
"AspNetCore.HealthChecks.AzureServiceBus": "[6.1.0, )",
|
||||||
"Commercial.Core": "2023.3.0",
|
"AspNetCore.HealthChecks.AzureStorage": "[6.1.2, )",
|
||||||
"Commercial.Infrastructure.EntityFramework": "2023.3.0",
|
"AspNetCore.HealthChecks.Network": "[6.0.4, )",
|
||||||
"Core": "2023.3.0",
|
"AspNetCore.HealthChecks.Redis": "[6.0.4, )",
|
||||||
"SharedWeb": "2023.3.0",
|
"AspNetCore.HealthChecks.SendGrid": "[6.0.2, )",
|
||||||
"Swashbuckle.AspNetCore": "6.5.0"
|
"AspNetCore.HealthChecks.SqlServer": "[6.0.2, )",
|
||||||
|
"AspNetCore.HealthChecks.Uris": "[6.0.3, )",
|
||||||
|
"Azure.Messaging.EventGrid": "[4.10.0, )",
|
||||||
|
"Commercial.Core": "[2023.5.0, )",
|
||||||
|
"Commercial.Infrastructure.EntityFramework": "[2023.5.0, )",
|
||||||
|
"Core": "[2023.5.0, )",
|
||||||
|
"SharedWeb": "[2023.5.0, )",
|
||||||
|
"Swashbuckle.AspNetCore": "[6.5.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"commercial.core": {
|
"commercial.core": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0"
|
"Core": "[2023.5.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"commercial.infrastructure.entityframework": {
|
"commercial.infrastructure.entityframework": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "[2023.5.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoFixture.AutoNSubstitute": "4.17.0",
|
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
|
||||||
"AutoFixture.Xunit2": "4.17.0",
|
"AutoFixture.Xunit2": "[4.17.0, )",
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
|
||||||
"Microsoft.NET.Test.Sdk": "17.1.0",
|
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
|
||||||
"NSubstitute": "4.3.0",
|
"NSubstitute": "[4.3.0, )",
|
||||||
"xunit": "2.4.1"
|
"xunit": "[2.4.1, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"core": {
|
"core": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AWSSDK.SQS": "3.7.2.47",
|
"AWSSDK.SQS": "[3.7.2.47, )",
|
||||||
"AWSSDK.SimpleEmail": "3.7.0.150",
|
"AWSSDK.SimpleEmail": "[3.7.0.150, )",
|
||||||
"AspNetCoreRateLimit": "4.0.2",
|
"AspNetCoreRateLimit": "[4.0.2, )",
|
||||||
"AspNetCoreRateLimit.Redis": "1.0.1",
|
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "1.3.2",
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.3.2, )",
|
||||||
"Azure.Storage.Blobs": "12.14.1",
|
"Azure.Storage.Blobs": "[12.14.1, )",
|
||||||
"Azure.Storage.Queues": "12.12.0",
|
"Azure.Storage.Queues": "[12.12.0, )",
|
||||||
"BitPay.Light": "1.0.1907",
|
"BitPay.Light": "[1.0.1907, )",
|
||||||
"Braintree": "5.12.0",
|
"Braintree": "[5.12.0, )",
|
||||||
"DnsClient": "1.7.0",
|
"DnsClient": "[1.7.0, )",
|
||||||
"Fido2.AspNet": "3.0.1",
|
"Fido2.AspNet": "[3.0.1, )",
|
||||||
"Handlebars.Net": "2.1.2",
|
"Handlebars.Net": "[2.1.2, )",
|
||||||
"IdentityServer4": "4.1.2",
|
"IdentityServer4": "[4.1.2, )",
|
||||||
"IdentityServer4.AccessTokenValidation": "3.0.1",
|
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
|
||||||
"LaunchDarkly.ServerSdk": "7.0.0",
|
"LaunchDarkly.ServerSdk": "[7.0.0, )",
|
||||||
"MailKit": "3.2.0",
|
"MailKit": "[3.2.0, )",
|
||||||
"Microsoft.AspNetCore.Authentication.JwtBearer": "6.0.4",
|
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",
|
||||||
"Microsoft.Azure.Cosmos.Table": "1.0.8",
|
"Microsoft.Azure.Cosmos.Table": "[1.0.8, )",
|
||||||
"Microsoft.Azure.NotificationHubs": "4.1.0",
|
"Microsoft.Azure.NotificationHubs": "[4.1.0, )",
|
||||||
"Microsoft.Azure.ServiceBus": "5.2.0",
|
"Microsoft.Azure.ServiceBus": "[5.2.0, )",
|
||||||
"Microsoft.Data.SqlClient": "5.0.1",
|
"Microsoft.Data.SqlClient": "[5.0.1, )",
|
||||||
"Microsoft.Extensions.Caching.StackExchangeRedis": "6.0.6",
|
"Microsoft.Extensions.Caching.StackExchangeRedis": "[6.0.6, )",
|
||||||
"Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.1",
|
"Microsoft.Extensions.Configuration.EnvironmentVariables": "[6.0.1, )",
|
||||||
"Microsoft.Extensions.Configuration.UserSecrets": "6.0.1",
|
"Microsoft.Extensions.Configuration.UserSecrets": "[6.0.1, )",
|
||||||
"Microsoft.Extensions.Identity.Stores": "6.0.4",
|
"Microsoft.Extensions.Identity.Stores": "[6.0.4, )",
|
||||||
"Newtonsoft.Json": "13.0.1",
|
"Newtonsoft.Json": "[13.0.1, )",
|
||||||
"Otp.NET": "1.2.2",
|
"Otp.NET": "[1.2.2, )",
|
||||||
"Quartz": "3.4.0",
|
"Quartz": "[3.4.0, )",
|
||||||
"SendGrid": "9.27.0",
|
"SendGrid": "[9.27.0, )",
|
||||||
"Sentry.Serilog": "3.16.0",
|
"Sentry.Serilog": "[3.16.0, )",
|
||||||
"Serilog.AspNetCore": "5.0.0",
|
"Serilog.AspNetCore": "[5.0.0, )",
|
||||||
"Serilog.Extensions.Logging": "3.1.0",
|
"Serilog.Extensions.Logging": "[3.1.0, )",
|
||||||
"Serilog.Extensions.Logging.File": "2.0.0",
|
"Serilog.Extensions.Logging.File": "[2.0.0, )",
|
||||||
"Serilog.Sinks.AzureCosmosDB": "2.0.0",
|
"Serilog.Sinks.AzureCosmosDB": "[2.0.0, )",
|
||||||
"Serilog.Sinks.SyslogMessages": "2.0.6",
|
"Serilog.Sinks.SyslogMessages": "[2.0.6, )",
|
||||||
"Stripe.net": "40.0.0",
|
"Stripe.net": "[40.0.0, )",
|
||||||
"YubicoDotNetClient": "1.2.0"
|
"YubicoDotNetClient": "[1.2.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"identity": {
|
"identity": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"SharedWeb": "2023.3.0",
|
"SharedWeb": "[2023.5.0, )",
|
||||||
"Swashbuckle.AspNetCore.SwaggerGen": "6.5.0"
|
"Swashbuckle.AspNetCore.SwaggerGen": "[6.5.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "[2.0.123, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"infrastructure.entityframework": {
|
"infrastructure.entityframework": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "[6.0.12, )",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "[6.0.12, )",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "[6.0.12, )",
|
||||||
"Npgsql.EntityFrameworkCore.PostgreSQL": "6.0.8",
|
"Npgsql.EntityFrameworkCore.PostgreSQL": "[6.0.8, )",
|
||||||
"Pomelo.EntityFrameworkCore.MySql": "6.0.2",
|
"Pomelo.EntityFrameworkCore.MySql": "[6.0.2, )",
|
||||||
"linq2db.EntityFrameworkCore": "6.11.0"
|
"linq2db.EntityFrameworkCore": "[6.11.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"integrationtestcommon": {
|
"integrationtestcommon": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Common": "2023.3.0",
|
"Common": "[2023.5.0, )",
|
||||||
"Identity": "2023.3.0",
|
"Identity": "[2023.5.0, )",
|
||||||
"Microsoft.AspNetCore.Mvc.Testing": "6.0.5",
|
"Microsoft.AspNetCore.Mvc.Testing": "[6.0.5, )",
|
||||||
"Microsoft.EntityFrameworkCore.InMemory": "6.0.5",
|
"Microsoft.EntityFrameworkCore.InMemory": "[6.0.5, )",
|
||||||
"Microsoft.Extensions.Configuration": "6.0.1"
|
"Microsoft.Extensions.Configuration": "[6.0.1, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "[2023.5.0, )",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "[2023.5.0, )"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,45 +244,106 @@ public class SecretsControllerTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(PermissionType.RunAsAdmin)]
|
[BitAutoData]
|
||||||
[BitAutoData(PermissionType.RunAsUserWithPermission)]
|
public async void BulkDelete_NoSecretsFound_ThrowsNotFound(SutProvider<SecretsController> sutProvider, List<Secret> data)
|
||||||
public async void BulkDeleteSecret_Success(PermissionType permissionType, SutProvider<SecretsController> sutProvider, List<Secret> data, Guid organizationId, Guid userId, Project mockProject)
|
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
|
var ids = data.Select(s => s.Id).ToList();
|
||||||
|
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(new List<Secret>());
|
||||||
if (permissionType == PermissionType.RunAsAdmin)
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.BulkDeleteAsync(ids));
|
||||||
{
|
await sutProvider.GetDependency<IDeleteSecretCommand>().DidNotReceiveWithAnyArgs().DeleteSecrets(Arg.Any<List<Secret>>());
|
||||||
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(organizationId).Returns(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data.FirstOrDefault().Projects = new List<Project>() { mockProject };
|
|
||||||
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(organizationId).Returns(false);
|
|
||||||
sutProvider.GetDependency<IProjectRepository>().AccessToProjectAsync(default, default, default)
|
|
||||||
.Returns((true, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var ids = data.Select(secret => secret.Id).ToList();
|
|
||||||
var mockResult = new List<Tuple<Secret, string>>();
|
|
||||||
|
|
||||||
foreach (var secret in data)
|
|
||||||
{
|
|
||||||
mockResult.Add(new Tuple<Secret, string>(secret, ""));
|
|
||||||
}
|
|
||||||
sutProvider.GetDependency<IDeleteSecretCommand>().DeleteSecrets(ids, userId).ReturnsForAnyArgs(mockResult);
|
|
||||||
|
|
||||||
var results = await sutProvider.Sut.BulkDeleteAsync(ids);
|
|
||||||
await sutProvider.GetDependency<IDeleteSecretCommand>().Received(1)
|
|
||||||
.DeleteSecrets(Arg.Is(ids), userId);
|
|
||||||
Assert.Equal(data.Count, results.Data.Count());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async void BulkDeleteSecret_NoGuids_ThrowsArgumentNullException(SutProvider<SecretsController> sutProvider)
|
public async void BulkDelete_SecretsFoundMisMatch_ThrowsNotFound(SutProvider<SecretsController> sutProvider, List<Secret> data, Secret mockSecret)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(new Guid());
|
data.Add(mockSecret);
|
||||||
await Assert.ThrowsAsync<ArgumentNullException>(() => sutProvider.Sut.BulkDeleteAsync(new List<Guid>()));
|
var ids = data.Select(s => s.Id).ToList();
|
||||||
|
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(new List<Secret> { mockSecret });
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.BulkDeleteAsync(ids));
|
||||||
|
await sutProvider.GetDependency<IDeleteSecretCommand>().DidNotReceiveWithAnyArgs().DeleteSecrets(Arg.Any<List<Secret>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async void BulkDelete_OrganizationMistMatch_ThrowsNotFound(SutProvider<SecretsController> sutProvider, List<Secret> data)
|
||||||
|
{
|
||||||
|
var ids = data.Select(s => s.Id).ToList();
|
||||||
|
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data);
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.BulkDeleteAsync(ids));
|
||||||
|
await sutProvider.GetDependency<IDeleteSecretCommand>().DidNotReceiveWithAnyArgs().DeleteSecrets(Arg.Any<List<Secret>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async void BulkDelete_NoAccessToSecretsManager_ThrowsNotFound(SutProvider<SecretsController> sutProvider, List<Secret> data)
|
||||||
|
{
|
||||||
|
var ids = data.Select(s => s.Id).ToList();
|
||||||
|
var organizationId = data.First().OrganizationId;
|
||||||
|
foreach (var s in data)
|
||||||
|
{
|
||||||
|
s.OrganizationId = organizationId;
|
||||||
|
}
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Is(organizationId)).ReturnsForAnyArgs(false);
|
||||||
|
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data);
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.BulkDeleteAsync(ids));
|
||||||
|
await sutProvider.GetDependency<IDeleteSecretCommand>().DidNotReceiveWithAnyArgs().DeleteSecrets(Arg.Any<List<Secret>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async void BulkDelete_ReturnsAccessDeniedForSecretsWithoutAccess_Success(SutProvider<SecretsController> sutProvider, List<Secret> data)
|
||||||
|
{
|
||||||
|
var ids = data.Select(s => s.Id).ToList();
|
||||||
|
var organizationId = data.First().OrganizationId;
|
||||||
|
foreach (var secret in data)
|
||||||
|
{
|
||||||
|
secret.OrganizationId = organizationId;
|
||||||
|
sutProvider.GetDependency<IAuthorizationService>()
|
||||||
|
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), secret,
|
||||||
|
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Success());
|
||||||
|
}
|
||||||
|
sutProvider.GetDependency<IAuthorizationService>()
|
||||||
|
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), data.First(),
|
||||||
|
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).Returns(AuthorizationResult.Failed());
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Is(organizationId)).ReturnsForAnyArgs(true);
|
||||||
|
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data);
|
||||||
|
|
||||||
|
var results = await sutProvider.Sut.BulkDeleteAsync(ids);
|
||||||
|
|
||||||
|
Assert.Equal(data.Count, results.Data.Count());
|
||||||
|
Assert.Equal("access denied", results.Data.First().Error);
|
||||||
|
|
||||||
|
data.Remove(data.First());
|
||||||
|
await sutProvider.GetDependency<IDeleteSecretCommand>().Received(1)
|
||||||
|
.DeleteSecrets(Arg.Is(AssertHelper.AssertPropertyEqual(data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async void BulkDelete_Success(SutProvider<SecretsController> sutProvider, List<Secret> data)
|
||||||
|
{
|
||||||
|
var ids = data.Select(sa => sa.Id).ToList();
|
||||||
|
var organizationId = data.First().OrganizationId;
|
||||||
|
foreach (var secret in data)
|
||||||
|
{
|
||||||
|
secret.OrganizationId = organizationId;
|
||||||
|
sutProvider.GetDependency<IAuthorizationService>()
|
||||||
|
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), secret,
|
||||||
|
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Success());
|
||||||
|
}
|
||||||
|
|
||||||
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Is(organizationId)).ReturnsForAnyArgs(true);
|
||||||
|
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data);
|
||||||
|
|
||||||
|
var results = await sutProvider.Sut.BulkDeleteAsync(ids);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IDeleteSecretCommand>().Received(1)
|
||||||
|
.DeleteSecrets(Arg.Is(AssertHelper.AssertPropertyEqual(data)));
|
||||||
|
Assert.Equal(data.Count, results.Data.Count());
|
||||||
|
foreach (var result in results.Data)
|
||||||
|
{
|
||||||
|
Assert.Null(result.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ using Bit.Core.Exceptions;
|
|||||||
using Bit.Core.SecretsManager.Commands.AccessTokens.Interfaces;
|
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.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
using Bit.Core.SecretsManager.Models.Data;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
@ -144,7 +145,7 @@ public class ServiceAccountsControllerTests
|
|||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async void CreateAccessToken_NoAccess_Throws(SutProvider<ServiceAccountsController> sutProvider,
|
public async void CreateAccessToken_NoAccess_Throws(SutProvider<ServiceAccountsController> sutProvider,
|
||||||
AccessTokenCreateRequestModel data, ServiceAccount serviceAccount)
|
AccessTokenCreateRequestModel data, ServiceAccount serviceAccount, string mockClientSecret)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(serviceAccount.Id).Returns(serviceAccount);
|
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(serviceAccount.Id).Returns(serviceAccount);
|
||||||
sutProvider.GetDependency<IAuthorizationService>()
|
sutProvider.GetDependency<IAuthorizationService>()
|
||||||
@ -152,8 +153,9 @@ public class ServiceAccountsControllerTests
|
|||||||
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Failed());
|
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Failed());
|
||||||
var resultAccessToken = data.ToApiKey(serviceAccount.Id);
|
var resultAccessToken = data.ToApiKey(serviceAccount.Id);
|
||||||
|
|
||||||
sutProvider.GetDependency<ICreateAccessTokenCommand>().CreateAsync(default)
|
sutProvider.GetDependency<ICreateAccessTokenCommand>()
|
||||||
.ReturnsForAnyArgs(resultAccessToken);
|
.CreateAsync(default)
|
||||||
|
.ReturnsForAnyArgs(new ApiKeyClientSecretDetails { ApiKey = resultAccessToken, ClientSecret = mockClientSecret });
|
||||||
|
|
||||||
await Assert.ThrowsAsync<NotFoundException>(() =>
|
await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||||
sutProvider.Sut.CreateAccessTokenAsync(serviceAccount.Id, data));
|
sutProvider.Sut.CreateAccessTokenAsync(serviceAccount.Id, data));
|
||||||
@ -164,7 +166,7 @@ public class ServiceAccountsControllerTests
|
|||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async void CreateAccessToken_Success(SutProvider<ServiceAccountsController> sutProvider,
|
public async void CreateAccessToken_Success(SutProvider<ServiceAccountsController> sutProvider,
|
||||||
AccessTokenCreateRequestModel data, ServiceAccount serviceAccount)
|
AccessTokenCreateRequestModel data, ServiceAccount serviceAccount, string mockClientSecret)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(serviceAccount.Id).Returns(serviceAccount);
|
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(serviceAccount.Id).Returns(serviceAccount);
|
||||||
sutProvider.GetDependency<IAuthorizationService>()
|
sutProvider.GetDependency<IAuthorizationService>()
|
||||||
@ -173,7 +175,7 @@ public class ServiceAccountsControllerTests
|
|||||||
var resultAccessToken = data.ToApiKey(serviceAccount.Id);
|
var resultAccessToken = data.ToApiKey(serviceAccount.Id);
|
||||||
|
|
||||||
sutProvider.GetDependency<ICreateAccessTokenCommand>().CreateAsync(default)
|
sutProvider.GetDependency<ICreateAccessTokenCommand>().CreateAsync(default)
|
||||||
.ReturnsForAnyArgs(resultAccessToken);
|
.ReturnsForAnyArgs(new ApiKeyClientSecretDetails { ApiKey = resultAccessToken, ClientSecret = mockClientSecret });
|
||||||
|
|
||||||
await sutProvider.Sut.CreateAccessTokenAsync(serviceAccount.Id, data);
|
await sutProvider.Sut.CreateAccessTokenAsync(serviceAccount.Id, data);
|
||||||
await sutProvider.GetDependency<ICreateAccessTokenCommand>().Received(1)
|
await sutProvider.GetDependency<ICreateAccessTokenCommand>().Received(1)
|
||||||
|
@ -54,6 +54,74 @@
|
|||||||
"resolved": "2.4.3",
|
"resolved": "2.4.3",
|
||||||
"contentHash": "kZZSmOmKA8OBlAJaquPXnJJLM9RwQ27H7BMVqfMLUcTi9xHinWGJiWksa3D4NEtz0wZ/nxd2mogObvBgJKCRhQ=="
|
"contentHash": "kZZSmOmKA8OBlAJaquPXnJJLM9RwQ27H7BMVqfMLUcTi9xHinWGJiWksa3D4NEtz0wZ/nxd2mogObvBgJKCRhQ=="
|
||||||
},
|
},
|
||||||
|
"AspNetCore.HealthChecks.AzureServiceBus": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.1.0",
|
||||||
|
"contentHash": "LepLE6NO4bLBVDzlx/730pD6jnfkV6zaaRUrbN1LqnNk4m1hROsv7wOpgbKgVDgYIfeLzdiVnBviEevSxWFKMQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Azure.Messaging.EventHubs": "5.7.4",
|
||||||
|
"Azure.Messaging.ServiceBus": "7.11.1",
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.AzureStorage": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.1.2",
|
||||||
|
"contentHash": "R/uHJ40Cc0fBLi48SqDtT6fHyR5G8L3+PeKlbe8t498GLebeBIR3ve4l4n7UzCD0qgmQDDvyIYvVywx3i5Y6Ng==",
|
||||||
|
"dependencies": {
|
||||||
|
"Azure.Storage.Blobs": "12.14.1",
|
||||||
|
"Azure.Storage.Files.Shares": "12.11.0",
|
||||||
|
"Azure.Storage.Queues": "12.11.1",
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.Network": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.4",
|
||||||
|
"contentHash": "rvoPkqlvhX1HW6dpqjE1rbvmmMo9v7+Uf9dJffEcd3mA/DyyEitlZFc6cwYtmZVFdgy2gbIU4ubs3654nVfvjA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.7",
|
||||||
|
"SSH.NET": "2020.0.2",
|
||||||
|
"System.Buffers": "4.5.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.Redis": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.4",
|
||||||
|
"contentHash": "adNNWF6kV8v1HLTmF3b9F5K6ubvgx+S7VqhzA8T/5YuIpRWsCDk8+q3RIDDV8Twvl9pRahLfzCbFrPYxvzmk7g==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.4",
|
||||||
|
"StackExchange.Redis": "2.5.61"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.SendGrid": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.2",
|
||||||
|
"contentHash": "VgskjkCUmSpAxil20rZlrj14bMi9aFNdiGLDtDTKjkUU0GYkoyi4HRVEy9Gp0FIgu9ce7quN+dNCpydKvMxjqA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.2",
|
||||||
|
"Microsoft.Extensions.Http": "6.0.0",
|
||||||
|
"SendGrid": "9.24.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.SqlServer": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.2",
|
||||||
|
"contentHash": "Af7ws27DnZZ4bKCiEREm7emSAKEtIiYirEAkI0ixFgK1fwJ99jmMnPC+kU01zfqn3FyCO/gZOUO7WbyVvTPpFg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Data.SqlClient": "3.0.1",
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AspNetCore.HealthChecks.Uris": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.3",
|
||||||
|
"contentHash": "EY0Vh8s2UrbnyvM/QhbyYuCnbrBw36BKkdh5LqdINxqAGnlPFQXf+/UoNlH/76MTEyg+nvdp2wjr5MqWDkVFaQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": "6.0.0",
|
||||||
|
"Microsoft.Extensions.Http": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"AspNetCoreRateLimit": {
|
"AspNetCoreRateLimit": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.0.2",
|
"resolved": "4.0.2",
|
||||||
@ -144,6 +212,14 @@
|
|||||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Azure.Core.Amqp": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.2.0",
|
||||||
|
"contentHash": "vrF4isvhwdZspzorLwYhukXz3DA8/ONSnZUIBAqBtOCzsDNUgAsuILbCzvtDrn2oDxyq7DZx5Nh81pe0BeWmDQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Memory": "4.5.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": {
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.3.2",
|
"resolved": "1.3.2",
|
||||||
@ -178,6 +254,34 @@
|
|||||||
"System.Text.Json": "4.7.2"
|
"System.Text.Json": "4.7.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Azure.Messaging.EventHubs": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "5.7.4",
|
||||||
|
"contentHash": "8vC4efO5HzDgZjx6LaViScywbyKu3xIkL+y+QoyN7Yo6u1pEmMAPW4ptaWIj1JW4gypeWC1tFy+U3zdQ/E7bGA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Azure.Core": "1.25.0",
|
||||||
|
"Azure.Core.Amqp": "1.2.0",
|
||||||
|
"Microsoft.Azure.Amqp": "2.5.12",
|
||||||
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
|
"System.Diagnostics.DiagnosticSource": "4.6.0",
|
||||||
|
"System.Memory.Data": "1.0.2",
|
||||||
|
"System.Reflection.TypeExtensions": "4.7.0",
|
||||||
|
"System.Threading.Channels": "4.7.1",
|
||||||
|
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Azure.Messaging.ServiceBus": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "7.11.1",
|
||||||
|
"contentHash": "ioGedXeH8KK4HdTDEyOzdgNbEXGstGItVljI1EKYsz08sgwej6LpODCZmwPR2ui1fjXBWt8Zea0RJT4d9LwlMg==",
|
||||||
|
"dependencies": {
|
||||||
|
"Azure.Core": "1.25.0",
|
||||||
|
"Azure.Core.Amqp": "1.2.0",
|
||||||
|
"Microsoft.Azure.Amqp": "2.5.12",
|
||||||
|
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||||
|
"System.Memory.Data": "1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Azure.Storage.Blobs": {
|
"Azure.Storage.Blobs": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.14.1",
|
"resolved": "12.14.1",
|
||||||
@ -196,6 +300,15 @@
|
|||||||
"System.IO.Hashing": "6.0.0"
|
"System.IO.Hashing": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Azure.Storage.Files.Shares": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "12.11.0",
|
||||||
|
"contentHash": "C747FRSZNe/L4hu1wrvzQImVaIfNDcZXfttaV3FwX96+TsbgXotHe6Y0lmSu65H/gVYKt07sIW9E1mDi3bdADw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Azure.Storage.Common": "12.12.0",
|
||||||
|
"System.Text.Json": "4.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Azure.Storage.Queues": {
|
"Azure.Storage.Queues": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "12.12.0",
|
"resolved": "12.12.0",
|
||||||
@ -480,12 +593,8 @@
|
|||||||
},
|
},
|
||||||
"Microsoft.Azure.Amqp": {
|
"Microsoft.Azure.Amqp": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "2.4.11",
|
"resolved": "2.5.12",
|
||||||
"contentHash": "7x5fu2f6TLQDDJS0sY5qW8/daFwJaY9O75YvU8RcUfRzbug+9YGjXUBxoRrprgyi0jxdBAMQL05p1s783SOSFQ==",
|
"contentHash": "0SlEl+TSQdpjXWf9/37dXWAa0zk6R1EJKmGtGZeKUAH7WEQpJOWMxJ9I43igcBCnTkFwa28CdPnpSCjFZVQlkw=="
|
||||||
"dependencies": {
|
|
||||||
"System.Net.WebSockets.Client": "4.0.2",
|
|
||||||
"System.Runtime.Serialization.Primitives": "4.1.1"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Microsoft.Azure.Cosmos": {
|
"Microsoft.Azure.Cosmos": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
@ -818,6 +927,22 @@
|
|||||||
"System.Text.Json": "6.0.0"
|
"System.Text.Json": "6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.10",
|
||||||
|
"contentHash": "YmTyFOc7xx2/9FKuAlCmcWYKYLr0bYgNrRlcNPy/vc8qXnxnRV+kua6z96RUXSJVSQadCbJcEjmnTUMTEVfXOQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "6.0.10",
|
||||||
|
"Microsoft.Extensions.Hosting.Abstractions": "6.0.0",
|
||||||
|
"Microsoft.Extensions.Logging.Abstractions": "6.0.2",
|
||||||
|
"Microsoft.Extensions.Options": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.10",
|
||||||
|
"contentHash": "U1PO967am1BIWbxBiLcYzVx8KOTYa9NvhBNgTn8Oii3LcsjvIwHzM+GTYy6bTiHnAFAlK5HAjxusAnAHSHJRoA=="
|
||||||
|
},
|
||||||
"Microsoft.Extensions.FileProviders.Abstractions": {
|
"Microsoft.Extensions.FileProviders.Abstractions": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "6.0.0",
|
"resolved": "6.0.0",
|
||||||
@ -843,13 +968,12 @@
|
|||||||
},
|
},
|
||||||
"Microsoft.Extensions.Hosting.Abstractions": {
|
"Microsoft.Extensions.Hosting.Abstractions": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "3.1.32",
|
"resolved": "6.0.0",
|
||||||
"contentHash": "00J6eE920t5vfPnEHBSGyj1Ya9lG6WYsMwqvLZ0nMPPTD2UxkaL+FNJM5DNSnMFJtV84KkUudPRngmNiCkqhuA==",
|
"contentHash": "GcT5l2CYXL6Sa27KCSh0TixsRfADUgth+ojQSD5EkzisZxmGFh7CwzkcYuGwvmXLjr27uWRNrJ2vuuEjMhU05Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Extensions.Configuration.Abstractions": "3.1.32",
|
"Microsoft.Extensions.Configuration.Abstractions": "6.0.0",
|
||||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "3.1.32",
|
"Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0",
|
||||||
"Microsoft.Extensions.FileProviders.Abstractions": "3.1.32",
|
"Microsoft.Extensions.FileProviders.Abstractions": "6.0.0"
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "3.1.32"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Microsoft.Extensions.Http": {
|
"Microsoft.Extensions.Http": {
|
||||||
@ -897,8 +1021,8 @@
|
|||||||
},
|
},
|
||||||
"Microsoft.Extensions.Logging.Abstractions": {
|
"Microsoft.Extensions.Logging.Abstractions": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "6.0.1",
|
"resolved": "6.0.2",
|
||||||
"contentHash": "dzB2Cgg+JmrouhjkcQGzSFjjvpwlq353i8oBQO2GWNjCXSzhbtBRUf28HSauWe7eib3wYOdb3tItdjRwAdwCSg=="
|
"contentHash": "pwXCZKaA7m5wgmCj49dW+H1RPSY7U62SKLTQYCcavf/k3Nyt/WnBgAjG4jMGnwy9rElfAZ2KvxvM5CJzJWG0hg=="
|
||||||
},
|
},
|
||||||
"Microsoft.Extensions.Options": {
|
"Microsoft.Extensions.Options": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
@ -1556,10 +1680,23 @@
|
|||||||
"SQLitePCLRaw.core": "2.1.2"
|
"SQLitePCLRaw.core": "2.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"SSH.NET": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "2020.0.2",
|
||||||
|
"contentHash": "G0dNlTBAM00KZXv1wWVwgg26d9/METcM6qWBpNQwllzQmmbu+Zu+FS1L1X4fFgGdPu3e8k9mmTBu6SwtQ0614g==",
|
||||||
|
"dependencies": {
|
||||||
|
"SshNet.Security.Cryptography": "[1.3.0]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SshNet.Security.Cryptography": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.3.0",
|
||||||
|
"contentHash": "5pBIXRjcSO/amY8WztpmNOhaaCNHY/B6CcYDI7FSTgqSyo/ZUojlLiKcsl+YGbxQuLX439qIkMfP0PHqxqJi/Q=="
|
||||||
|
},
|
||||||
"StackExchange.Redis": {
|
"StackExchange.Redis": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "2.5.43",
|
"resolved": "2.5.61",
|
||||||
"contentHash": "YQ38jVbX1b5mBi6lizESou+NpV6QZpeo6ofRR6qeuqJ8ePOmhcwhje3nDTNIGEkfPSK0sLuF6pR5rtFyq2F46g==",
|
"contentHash": "h1Gz4itrHL/PQ0GBLTEiPK8bBkOp5SFO6iaRFSSn/x1qltBWENsz/NUxPid6WHX9yf2Tiyzn9D3R7mtnksODxg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Pipelines.Sockets.Unofficial": "2.2.2",
|
"Pipelines.Sockets.Unofficial": "2.2.2",
|
||||||
"System.Diagnostics.PerformanceCounter": "5.0.0"
|
"System.Diagnostics.PerformanceCounter": "5.0.0"
|
||||||
@ -2262,42 +2399,6 @@
|
|||||||
"System.Runtime.Extensions": "4.1.0"
|
"System.Runtime.Extensions": "4.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"System.Net.WebSockets": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "4.0.0",
|
|
||||||
"contentHash": "2KJo8hir6Edi9jnMDAMhiJoI691xRBmKcbNpwjrvpIMOCTYOtBpSsSEGBxBDV7PKbasJNaFp1+PZz1D7xS41Hg==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Win32.Primitives": "4.0.1",
|
|
||||||
"System.Resources.ResourceManager": "4.0.1",
|
|
||||||
"System.Runtime": "4.1.0",
|
|
||||||
"System.Threading.Tasks": "4.0.11"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"System.Net.WebSockets.Client": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "4.0.2",
|
|
||||||
"contentHash": "NUCcDroX4lCQXgOrzlwIZ1u9YJ0krfyF0wk0ONnyLUmcQoEiYV2/OfUPRqUwQBbpH1BlGApkLgoQUwMqb5+c1g==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.NETCore.Platforms": "1.0.2",
|
|
||||||
"Microsoft.Win32.Primitives": "4.0.1",
|
|
||||||
"System.Collections": "4.0.11",
|
|
||||||
"System.Diagnostics.Debug": "4.0.11",
|
|
||||||
"System.Diagnostics.Tracing": "4.1.0",
|
|
||||||
"System.Globalization": "4.0.11",
|
|
||||||
"System.Net.Primitives": "4.0.11",
|
|
||||||
"System.Net.WebHeaderCollection": "4.0.1",
|
|
||||||
"System.Net.WebSockets": "4.0.0",
|
|
||||||
"System.Resources.ResourceManager": "4.0.1",
|
|
||||||
"System.Runtime": "4.1.0",
|
|
||||||
"System.Runtime.Extensions": "4.1.0",
|
|
||||||
"System.Runtime.Handles": "4.0.1",
|
|
||||||
"System.Runtime.InteropServices": "4.1.0",
|
|
||||||
"System.Security.Cryptography.X509Certificates": "4.1.0",
|
|
||||||
"System.Text.Encoding": "4.0.11",
|
|
||||||
"System.Threading": "4.0.11",
|
|
||||||
"System.Threading.Tasks": "4.0.11"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"System.Numerics.Vectors": {
|
"System.Numerics.Vectors": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.5.0",
|
"resolved": "4.5.0",
|
||||||
@ -2419,12 +2520,8 @@
|
|||||||
},
|
},
|
||||||
"System.Reflection.TypeExtensions": {
|
"System.Reflection.TypeExtensions": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.3.0",
|
"resolved": "4.7.0",
|
||||||
"contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==",
|
"contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA=="
|
||||||
"dependencies": {
|
|
||||||
"System.Reflection": "4.3.0",
|
|
||||||
"System.Runtime": "4.3.0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"System.Resources.ResourceManager": {
|
"System.Resources.ResourceManager": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
@ -2812,6 +2909,11 @@
|
|||||||
"System.Threading.Tasks": "4.3.0"
|
"System.Threading.Tasks": "4.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"System.Threading.Channels": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.7.1",
|
||||||
|
"contentHash": "6akRtHK/wab3246t4p5v3HQrtQk8LboOt5T4dtpNgsp3zvDeM4/Gx8V12t0h+c/W9/enUrilk8n6EQqdQorZAA=="
|
||||||
|
},
|
||||||
"System.Threading.Overlapped": {
|
"System.Threading.Overlapped": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.0.1",
|
"resolved": "4.0.1",
|
||||||
@ -3049,122 +3151,129 @@
|
|||||||
"api": {
|
"api": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Azure.Messaging.EventGrid": "4.10.0",
|
"AspNetCore.HealthChecks.AzureServiceBus": "[6.1.0, )",
|
||||||
"Commercial.Core": "2023.3.0",
|
"AspNetCore.HealthChecks.AzureStorage": "[6.1.2, )",
|
||||||
"Commercial.Infrastructure.EntityFramework": "2023.3.0",
|
"AspNetCore.HealthChecks.Network": "[6.0.4, )",
|
||||||
"Core": "2023.3.0",
|
"AspNetCore.HealthChecks.Redis": "[6.0.4, )",
|
||||||
"SharedWeb": "2023.3.0",
|
"AspNetCore.HealthChecks.SendGrid": "[6.0.2, )",
|
||||||
"Swashbuckle.AspNetCore": "6.5.0"
|
"AspNetCore.HealthChecks.SqlServer": "[6.0.2, )",
|
||||||
|
"AspNetCore.HealthChecks.Uris": "[6.0.3, )",
|
||||||
|
"Azure.Messaging.EventGrid": "[4.10.0, )",
|
||||||
|
"Commercial.Core": "[2023.5.0, )",
|
||||||
|
"Commercial.Infrastructure.EntityFramework": "[2023.5.0, )",
|
||||||
|
"Core": "[2023.5.0, )",
|
||||||
|
"SharedWeb": "[2023.5.0, )",
|
||||||
|
"Swashbuckle.AspNetCore": "[6.5.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"commercial.core": {
|
"commercial.core": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0"
|
"Core": "[2023.5.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"commercial.infrastructure.entityframework": {
|
"commercial.infrastructure.entityframework": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "[2023.5.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoFixture.AutoNSubstitute": "4.17.0",
|
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
|
||||||
"AutoFixture.Xunit2": "4.17.0",
|
"AutoFixture.Xunit2": "[4.17.0, )",
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
|
||||||
"Microsoft.NET.Test.Sdk": "17.1.0",
|
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
|
||||||
"NSubstitute": "4.3.0",
|
"NSubstitute": "[4.3.0, )",
|
||||||
"xunit": "2.4.1"
|
"xunit": "[2.4.1, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"core": {
|
"core": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AWSSDK.SQS": "3.7.2.47",
|
"AWSSDK.SQS": "[3.7.2.47, )",
|
||||||
"AWSSDK.SimpleEmail": "3.7.0.150",
|
"AWSSDK.SimpleEmail": "[3.7.0.150, )",
|
||||||
"AspNetCoreRateLimit": "4.0.2",
|
"AspNetCoreRateLimit": "[4.0.2, )",
|
||||||
"AspNetCoreRateLimit.Redis": "1.0.1",
|
"AspNetCoreRateLimit.Redis": "[1.0.1, )",
|
||||||
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "1.3.2",
|
"Azure.Extensions.AspNetCore.DataProtection.Blobs": "[1.3.2, )",
|
||||||
"Azure.Storage.Blobs": "12.14.1",
|
"Azure.Storage.Blobs": "[12.14.1, )",
|
||||||
"Azure.Storage.Queues": "12.12.0",
|
"Azure.Storage.Queues": "[12.12.0, )",
|
||||||
"BitPay.Light": "1.0.1907",
|
"BitPay.Light": "[1.0.1907, )",
|
||||||
"Braintree": "5.12.0",
|
"Braintree": "[5.12.0, )",
|
||||||
"DnsClient": "1.7.0",
|
"DnsClient": "[1.7.0, )",
|
||||||
"Fido2.AspNet": "3.0.1",
|
"Fido2.AspNet": "[3.0.1, )",
|
||||||
"Handlebars.Net": "2.1.2",
|
"Handlebars.Net": "[2.1.2, )",
|
||||||
"IdentityServer4": "4.1.2",
|
"IdentityServer4": "[4.1.2, )",
|
||||||
"IdentityServer4.AccessTokenValidation": "3.0.1",
|
"IdentityServer4.AccessTokenValidation": "[3.0.1, )",
|
||||||
"LaunchDarkly.ServerSdk": "7.0.0",
|
"LaunchDarkly.ServerSdk": "[7.0.0, )",
|
||||||
"MailKit": "3.2.0",
|
"MailKit": "[3.2.0, )",
|
||||||
"Microsoft.AspNetCore.Authentication.JwtBearer": "6.0.4",
|
"Microsoft.AspNetCore.Authentication.JwtBearer": "[6.0.4, )",
|
||||||
"Microsoft.Azure.Cosmos.Table": "1.0.8",
|
"Microsoft.Azure.Cosmos.Table": "[1.0.8, )",
|
||||||
"Microsoft.Azure.NotificationHubs": "4.1.0",
|
"Microsoft.Azure.NotificationHubs": "[4.1.0, )",
|
||||||
"Microsoft.Azure.ServiceBus": "5.2.0",
|
"Microsoft.Azure.ServiceBus": "[5.2.0, )",
|
||||||
"Microsoft.Data.SqlClient": "5.0.1",
|
"Microsoft.Data.SqlClient": "[5.0.1, )",
|
||||||
"Microsoft.Extensions.Caching.StackExchangeRedis": "6.0.6",
|
"Microsoft.Extensions.Caching.StackExchangeRedis": "[6.0.6, )",
|
||||||
"Microsoft.Extensions.Configuration.EnvironmentVariables": "6.0.1",
|
"Microsoft.Extensions.Configuration.EnvironmentVariables": "[6.0.1, )",
|
||||||
"Microsoft.Extensions.Configuration.UserSecrets": "6.0.1",
|
"Microsoft.Extensions.Configuration.UserSecrets": "[6.0.1, )",
|
||||||
"Microsoft.Extensions.Identity.Stores": "6.0.4",
|
"Microsoft.Extensions.Identity.Stores": "[6.0.4, )",
|
||||||
"Newtonsoft.Json": "13.0.1",
|
"Newtonsoft.Json": "[13.0.1, )",
|
||||||
"Otp.NET": "1.2.2",
|
"Otp.NET": "[1.2.2, )",
|
||||||
"Quartz": "3.4.0",
|
"Quartz": "[3.4.0, )",
|
||||||
"SendGrid": "9.27.0",
|
"SendGrid": "[9.27.0, )",
|
||||||
"Sentry.Serilog": "3.16.0",
|
"Sentry.Serilog": "[3.16.0, )",
|
||||||
"Serilog.AspNetCore": "5.0.0",
|
"Serilog.AspNetCore": "[5.0.0, )",
|
||||||
"Serilog.Extensions.Logging": "3.1.0",
|
"Serilog.Extensions.Logging": "[3.1.0, )",
|
||||||
"Serilog.Extensions.Logging.File": "2.0.0",
|
"Serilog.Extensions.Logging.File": "[2.0.0, )",
|
||||||
"Serilog.Sinks.AzureCosmosDB": "2.0.0",
|
"Serilog.Sinks.AzureCosmosDB": "[2.0.0, )",
|
||||||
"Serilog.Sinks.SyslogMessages": "2.0.6",
|
"Serilog.Sinks.SyslogMessages": "[2.0.6, )",
|
||||||
"Stripe.net": "40.0.0",
|
"Stripe.net": "[40.0.0, )",
|
||||||
"YubicoDotNetClient": "1.2.0"
|
"YubicoDotNetClient": "[1.2.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"core.test": {
|
"core.test": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoFixture.AutoNSubstitute": "4.17.0",
|
"AutoFixture.AutoNSubstitute": "[4.17.0, )",
|
||||||
"AutoFixture.Xunit2": "4.17.0",
|
"AutoFixture.Xunit2": "[4.17.0, )",
|
||||||
"Common": "2023.3.0",
|
"Common": "[2023.5.0, )",
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
"Kralizek.AutoFixture.Extensions.MockHttp": "[1.2.0, )",
|
||||||
"Microsoft.NET.Test.Sdk": "17.1.0",
|
"Microsoft.NET.Test.Sdk": "[17.1.0, )",
|
||||||
"Moq": "4.17.2",
|
"Moq": "[4.17.2, )",
|
||||||
"NSubstitute": "4.3.0",
|
"NSubstitute": "[4.3.0, )",
|
||||||
"xunit": "2.4.1"
|
"xunit": "[2.4.1, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "[2.0.123, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"infrastructure.entityframework": {
|
"infrastructure.entityframework": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "[12.0.1, )",
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "[6.0.12, )",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "[6.0.12, )",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "[6.0.12, )",
|
||||||
"Npgsql.EntityFrameworkCore.PostgreSQL": "6.0.8",
|
"Npgsql.EntityFrameworkCore.PostgreSQL": "[6.0.8, )",
|
||||||
"Pomelo.EntityFrameworkCore.MySql": "6.0.2",
|
"Pomelo.EntityFrameworkCore.MySql": "[6.0.2, )",
|
||||||
"linq2db.EntityFrameworkCore": "6.11.0"
|
"linq2db.EntityFrameworkCore": "[6.11.0, )"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "[2023.5.0, )",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "[2023.5.0, )",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "[2023.5.0, )"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2988,8 +2988,8 @@
|
|||||||
"billing": {
|
"billing": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"SharedWeb": "2023.3.0"
|
"SharedWeb": "2023.4.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
@ -2997,7 +2997,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoFixture.AutoNSubstitute": "4.17.0",
|
"AutoFixture.AutoNSubstitute": "4.17.0",
|
||||||
"AutoFixture.Xunit2": "4.17.0",
|
"AutoFixture.Xunit2": "4.17.0",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
||||||
"Microsoft.NET.Test.Sdk": "17.1.0",
|
"Microsoft.NET.Test.Sdk": "17.1.0",
|
||||||
"NSubstitute": "4.3.0",
|
"NSubstitute": "4.3.0",
|
||||||
@ -3049,7 +3049,7 @@
|
|||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3057,7 +3057,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -3069,9 +3069,9 @@
|
|||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2825,7 +2825,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoFixture.AutoNSubstitute": "4.17.0",
|
"AutoFixture.AutoNSubstitute": "4.17.0",
|
||||||
"AutoFixture.Xunit2": "4.17.0",
|
"AutoFixture.Xunit2": "4.17.0",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
||||||
"Microsoft.NET.Test.Sdk": "17.1.0",
|
"Microsoft.NET.Test.Sdk": "17.1.0",
|
||||||
"NSubstitute": "4.3.0",
|
"NSubstitute": "4.3.0",
|
||||||
|
@ -2972,14 +2972,14 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AngleSharp": "0.16.1",
|
"AngleSharp": "0.16.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"SharedWeb": "2023.3.0"
|
"SharedWeb": "2023.4.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2987,7 +2987,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -2999,9 +2999,9 @@
|
|||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,6 +245,59 @@ public class IdentityServerSsoTests
|
|||||||
AssertHelper.AssertJsonProperty(trustedDeviceOption, "HasAdminApproval", JsonValueKind.False);
|
AssertHelper.AssertJsonProperty(trustedDeviceOption, "HasAdminApproval", JsonValueKind.False);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SsoLogin_TrustedDeviceEncryption_FlagTurnedOff_DoesNotReturnOption()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var challenge = new string('c', 50);
|
||||||
|
|
||||||
|
// This creates SsoConfig that HAS enabled trusted device encryption which should have only been
|
||||||
|
// done with the feature flag turned on but we are testing that even if they have done that, this will turn off
|
||||||
|
// if returning as an option if the flag has later been turned off. We should be very careful turning the flag
|
||||||
|
// back off.
|
||||||
|
var factory = await CreateFactoryAsync(new SsoConfigurationData
|
||||||
|
{
|
||||||
|
MemberDecryptionType = MemberDecryptionType.TrustedDeviceEncryption,
|
||||||
|
}, challenge, trustedDeviceEnabled: false);
|
||||||
|
|
||||||
|
await UpdateUserAsync(factory, user => user.MasterPassword = null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var context = await factory.Server.PostAsync("/connect/token", new FormUrlEncodedContent(new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "scope", "api offline_access" },
|
||||||
|
{ "client_id", "web" },
|
||||||
|
{ "deviceType", "10" },
|
||||||
|
{ "deviceIdentifier", "test_id" },
|
||||||
|
{ "deviceName", "firefox" },
|
||||||
|
{ "twoFactorToken", "TEST"},
|
||||||
|
{ "twoFactorProvider", "5" }, // RememberMe Provider
|
||||||
|
{ "twoFactorRemember", "0" },
|
||||||
|
{ "grant_type", "authorization_code" },
|
||||||
|
{ "code", "test_code" },
|
||||||
|
{ "code_verifier", challenge },
|
||||||
|
{ "redirect_uri", "https://localhost:8080/sso-connector.html" }
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
// If the organization has selected TrustedDeviceEncryption but the user still has their master password
|
||||||
|
// they can decrypt with either option
|
||||||
|
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
|
||||||
|
using var responseBody = await AssertHelper.AssertResponseTypeIs<JsonDocument>(context);
|
||||||
|
var root = responseBody.RootElement;
|
||||||
|
AssertHelper.AssertJsonProperty(root, "access_token", JsonValueKind.String);
|
||||||
|
var userDecryptionOptions = AssertHelper.AssertJsonProperty(root, "UserDecryptionOptions", JsonValueKind.Object);
|
||||||
|
|
||||||
|
// Expected to look like:
|
||||||
|
// "UserDecryptionOptions": {
|
||||||
|
// "Object": "userDecryptionOptions"
|
||||||
|
// "HasMasterPassword": false
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Should only have 2 properties
|
||||||
|
Assert.Equal(2, userDecryptionOptions.EnumerateObject().Count());
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task SsoLogin_KeyConnector_ReturnsOptions()
|
public async Task SsoLogin_KeyConnector_ReturnsOptions()
|
||||||
{
|
{
|
||||||
@ -301,7 +354,7 @@ public class IdentityServerSsoTests
|
|||||||
Assert.Equal("https://key_connector.com", keyConnectorUrl);
|
Assert.Equal("https://key_connector.com", keyConnectorUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<IdentityApplicationFactory> CreateFactoryAsync(SsoConfigurationData ssoConfigurationData, string challenge)
|
private static async Task<IdentityApplicationFactory> CreateFactoryAsync(SsoConfigurationData ssoConfigurationData, string challenge, bool trustedDeviceEnabled = true)
|
||||||
{
|
{
|
||||||
var factory = new IdentityApplicationFactory();
|
var factory = new IdentityApplicationFactory();
|
||||||
|
|
||||||
@ -327,7 +380,7 @@ public class IdentityServerSsoTests
|
|||||||
factory.SubstitueService<IFeatureService>(service =>
|
factory.SubstitueService<IFeatureService>(service =>
|
||||||
{
|
{
|
||||||
service.IsEnabled(FeatureFlagKeys.TrustedDeviceEncryption, Arg.Any<ICurrentContext>())
|
service.IsEnabled(FeatureFlagKeys.TrustedDeviceEncryption, Arg.Any<ICurrentContext>())
|
||||||
.Returns(true);
|
.Returns(trustedDeviceEnabled);
|
||||||
});
|
});
|
||||||
|
|
||||||
// This starts the server and finalizes services
|
// This starts the server and finalizes services
|
||||||
|
@ -65,6 +65,7 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
|||||||
Assert.Equal(0, kdf);
|
Assert.Equal(0, kdf);
|
||||||
var kdfIterations = AssertHelper.AssertJsonProperty(root, "KdfIterations", JsonValueKind.Number).GetInt32();
|
var kdfIterations = AssertHelper.AssertJsonProperty(root, "KdfIterations", JsonValueKind.Number).GetInt32();
|
||||||
Assert.Equal(5000, kdfIterations);
|
Assert.Equal(5000, kdfIterations);
|
||||||
|
AssertUserDecryptionOptions(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
@ -635,6 +636,16 @@ public class IdentityServerTests : IClassFixture<IdentityApplicationFactory>
|
|||||||
Assert.StartsWith("sso authentication", errorDescription.ToLowerInvariant());
|
Assert.StartsWith("sso authentication", errorDescription.ToLowerInvariant());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void AssertUserDecryptionOptions(JsonElement tokenResponse)
|
||||||
|
{
|
||||||
|
var userDecryptionOptions = AssertHelper.AssertJsonProperty(tokenResponse, "UserDecryptionOptions", JsonValueKind.Object)
|
||||||
|
.EnumerateObject();
|
||||||
|
|
||||||
|
Assert.Collection(userDecryptionOptions,
|
||||||
|
(prop) => { Assert.Equal("HasMasterPassword", prop.Name); Assert.Equal(JsonValueKind.True, prop.Value.ValueKind); },
|
||||||
|
(prop) => { Assert.Equal("Object", prop.Name); Assert.Equal("userDecryptionOptions", prop.Value.GetString()); });
|
||||||
|
}
|
||||||
|
|
||||||
private void ReinitializeDbForTests()
|
private void ReinitializeDbForTests()
|
||||||
{
|
{
|
||||||
var databaseContext = _factory.GetDatabaseContext();
|
var databaseContext = _factory.GetDatabaseContext();
|
||||||
|
@ -3144,7 +3144,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoFixture.AutoNSubstitute": "4.17.0",
|
"AutoFixture.AutoNSubstitute": "4.17.0",
|
||||||
"AutoFixture.Xunit2": "4.17.0",
|
"AutoFixture.Xunit2": "4.17.0",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
||||||
"Microsoft.NET.Test.Sdk": "17.1.0",
|
"Microsoft.NET.Test.Sdk": "17.1.0",
|
||||||
"NSubstitute": "4.3.0",
|
"NSubstitute": "4.3.0",
|
||||||
@ -3196,15 +3196,15 @@
|
|||||||
"identity": {
|
"identity": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"SharedWeb": "2023.3.0",
|
"SharedWeb": "2023.4.3",
|
||||||
"Swashbuckle.AspNetCore.SwaggerGen": "6.5.0"
|
"Swashbuckle.AspNetCore.SwaggerGen": "6.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3212,7 +3212,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -3224,8 +3224,8 @@
|
|||||||
"integrationtestcommon": {
|
"integrationtestcommon": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Common": "2023.3.0",
|
"Common": "2023.4.3",
|
||||||
"Identity": "2023.3.0",
|
"Identity": "2023.4.3",
|
||||||
"Microsoft.AspNetCore.Mvc.Testing": "6.0.5",
|
"Microsoft.AspNetCore.Mvc.Testing": "6.0.5",
|
||||||
"Microsoft.EntityFrameworkCore.InMemory": "6.0.5",
|
"Microsoft.EntityFrameworkCore.InMemory": "6.0.5",
|
||||||
"Microsoft.Extensions.Configuration": "6.0.1"
|
"Microsoft.Extensions.Configuration": "6.0.1"
|
||||||
@ -3234,9 +3234,9 @@
|
|||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3011,7 +3011,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoFixture.AutoNSubstitute": "4.17.0",
|
"AutoFixture.AutoNSubstitute": "4.17.0",
|
||||||
"AutoFixture.Xunit2": "4.17.0",
|
"AutoFixture.Xunit2": "4.17.0",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
||||||
"Microsoft.NET.Test.Sdk": "17.1.0",
|
"Microsoft.NET.Test.Sdk": "17.1.0",
|
||||||
"NSubstitute": "4.3.0",
|
"NSubstitute": "4.3.0",
|
||||||
@ -3063,15 +3063,15 @@
|
|||||||
"identity": {
|
"identity": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"SharedWeb": "2023.3.0",
|
"SharedWeb": "2023.4.3",
|
||||||
"Swashbuckle.AspNetCore.SwaggerGen": "6.5.0"
|
"Swashbuckle.AspNetCore.SwaggerGen": "6.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3079,7 +3079,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -3091,9 +3091,9 @@
|
|||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3001,7 +3001,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoFixture.AutoNSubstitute": "4.17.0",
|
"AutoFixture.AutoNSubstitute": "4.17.0",
|
||||||
"AutoFixture.Xunit2": "4.17.0",
|
"AutoFixture.Xunit2": "4.17.0",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
||||||
"Microsoft.NET.Test.Sdk": "17.1.0",
|
"Microsoft.NET.Test.Sdk": "17.1.0",
|
||||||
"NSubstitute": "4.3.0",
|
"NSubstitute": "4.3.0",
|
||||||
@ -3055,8 +3055,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoFixture.AutoNSubstitute": "4.17.0",
|
"AutoFixture.AutoNSubstitute": "4.17.0",
|
||||||
"AutoFixture.Xunit2": "4.17.0",
|
"AutoFixture.Xunit2": "4.17.0",
|
||||||
"Common": "2023.3.0",
|
"Common": "2023.4.3",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
||||||
"Microsoft.NET.Test.Sdk": "17.1.0",
|
"Microsoft.NET.Test.Sdk": "17.1.0",
|
||||||
"Moq": "4.17.2",
|
"Moq": "4.17.2",
|
||||||
@ -3067,7 +3067,7 @@
|
|||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3075,7 +3075,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
|
@ -2901,7 +2901,7 @@
|
|||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2909,7 +2909,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
|
@ -3130,7 +3130,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoFixture.AutoNSubstitute": "4.17.0",
|
"AutoFixture.AutoNSubstitute": "4.17.0",
|
||||||
"AutoFixture.Xunit2": "4.17.0",
|
"AutoFixture.Xunit2": "4.17.0",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
"Kralizek.AutoFixture.Extensions.MockHttp": "1.2.0",
|
||||||
"Microsoft.NET.Test.Sdk": "17.1.0",
|
"Microsoft.NET.Test.Sdk": "17.1.0",
|
||||||
"NSubstitute": "4.3.0",
|
"NSubstitute": "4.3.0",
|
||||||
@ -3182,15 +3182,15 @@
|
|||||||
"identity": {
|
"identity": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"SharedWeb": "2023.3.0",
|
"SharedWeb": "2023.4.3",
|
||||||
"Swashbuckle.AspNetCore.SwaggerGen": "6.5.0"
|
"Swashbuckle.AspNetCore.SwaggerGen": "6.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"infrastructure.dapper": {
|
"infrastructure.dapper": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Dapper": "2.0.123"
|
"Dapper": "2.0.123"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3198,7 +3198,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection": "12.0.1",
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Relational": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
"Microsoft.EntityFrameworkCore.SqlServer": "6.0.12",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
"Microsoft.EntityFrameworkCore.Sqlite": "6.0.12",
|
||||||
@ -3210,9 +3210,9 @@
|
|||||||
"sharedweb": {
|
"sharedweb": {
|
||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Core": "2023.3.0",
|
"Core": "2023.4.3",
|
||||||
"Infrastructure.Dapper": "2023.3.0",
|
"Infrastructure.Dapper": "2023.4.3",
|
||||||
"Infrastructure.EntityFramework": "2023.3.0"
|
"Infrastructure.EntityFramework": "2023.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
72
util/Migrator/DbScripts/2023-05-16_00_ClientSecretHash.sql
Normal file
72
util/Migrator/DbScripts/2023-05-16_00_ClientSecretHash.sql
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
IF COL_LENGTH('[dbo].[ApiKey]', 'ClientSecretHash') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[ApiKey]
|
||||||
|
ADD [ClientSecretHash] VARCHAR(128);
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- Refresh views
|
||||||
|
IF OBJECT_ID('[dbo].[ApiKeyDetailsView]') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
EXECUTE sp_refreshview N'[dbo].[ApiKeyDetailsView]';
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID('[dbo].[ApiKeyView]') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
EXECUTE sp_refreshview N'[dbo].[ApiKeyView]';
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE OR ALTER PROCEDURE [dbo].[ApiKey_Create]
|
||||||
|
@Id UNIQUEIDENTIFIER OUTPUT,
|
||||||
|
@ServiceAccountId UNIQUEIDENTIFIER,
|
||||||
|
@Name VARCHAR(200),
|
||||||
|
@ClientSecret VARCHAR(30) = 'migrated', -- Deprecated as of 2023-05-17
|
||||||
|
@ClientSecretHash VARCHAR(128) = NULL,
|
||||||
|
@Scope NVARCHAR(4000),
|
||||||
|
@EncryptedPayload NVARCHAR(4000),
|
||||||
|
@Key VARCHAR(MAX),
|
||||||
|
@ExpireAt DATETIME2(7),
|
||||||
|
@CreationDate DATETIME2(7),
|
||||||
|
@RevisionDate DATETIME2(7)
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
IF (@ClientSecretHash IS NULL)
|
||||||
|
BEGIN
|
||||||
|
DECLARE @hb VARBINARY(128) = HASHBYTES('SHA2_256', @ClientSecret);
|
||||||
|
SET @ClientSecretHash = CAST(N'' as xml).value('xs:base64Binary(sql:variable("@hb"))', 'VARCHAR(128)');
|
||||||
|
END
|
||||||
|
|
||||||
|
INSERT INTO [dbo].[ApiKey]
|
||||||
|
(
|
||||||
|
[Id],
|
||||||
|
[ServiceAccountId],
|
||||||
|
[Name],
|
||||||
|
[ClientSecret],
|
||||||
|
[ClientSecretHash],
|
||||||
|
[Scope],
|
||||||
|
[EncryptedPayload],
|
||||||
|
[Key],
|
||||||
|
[ExpireAt],
|
||||||
|
[CreationDate],
|
||||||
|
[RevisionDate]
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
@Id,
|
||||||
|
@ServiceAccountId,
|
||||||
|
@Name,
|
||||||
|
@ClientSecret,
|
||||||
|
@ClientSecretHash,
|
||||||
|
@Scope,
|
||||||
|
@EncryptedPayload,
|
||||||
|
@Key,
|
||||||
|
@ExpireAt,
|
||||||
|
@CreationDate,
|
||||||
|
@RevisionDate
|
||||||
|
)
|
||||||
|
END
|
||||||
|
GO
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
This is the data migration script for the client secret hash updates.
|
||||||
|
The initial migration util/Migrator/DbScripts/2023-05-16_00_ClientSecretHash.sql should be run prior.
|
||||||
|
The final migration is in util/Migrator/DbScripts_future/2023-06-FutureMigration.sql.
|
||||||
|
*/
|
||||||
|
IF COL_LENGTH('[dbo].[ApiKey]', 'ClientSecretHash') IS NOT NULL AND COL_LENGTH('[dbo].[ApiKey]', 'ClientSecret') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
-- Add index
|
||||||
|
IF NOT EXISTS(SELECT name FROM sys.indexes WHERE name = 'IX_ApiKey_ClientSecretHash')
|
||||||
|
BEGIN
|
||||||
|
CREATE NONCLUSTERED INDEX [IX_ApiKey_ClientSecretHash]
|
||||||
|
ON [dbo].[ApiKey]([ClientSecretHash] ASC)
|
||||||
|
WITH (ONLINE = ON)
|
||||||
|
END
|
||||||
|
|
||||||
|
-- Data Migration
|
||||||
|
DECLARE @BatchSize INT = 10000
|
||||||
|
WHILE @BatchSize > 0
|
||||||
|
BEGIN
|
||||||
|
BEGIN TRANSACTION Migrate_ClientSecretHash
|
||||||
|
|
||||||
|
UPDATE TOP(@BatchSize) [dbo].[ApiKey]
|
||||||
|
SET ClientSecretHash = (
|
||||||
|
SELECT CAST(N'' AS XML).value('xs:base64Binary(sql:column("HASH"))', 'VARCHAR(128)')
|
||||||
|
FROM (
|
||||||
|
SELECT HASHBYTES('SHA2_256', [ClientSecret]) AS HASH
|
||||||
|
) SRC
|
||||||
|
)
|
||||||
|
WHERE [ClientSecretHash] IS NULL
|
||||||
|
|
||||||
|
SET @BatchSize = @@ROWCOUNT
|
||||||
|
|
||||||
|
COMMIT TRANSACTION Migrate_ClientSecretHash
|
||||||
|
END
|
||||||
|
|
||||||
|
-- Drop index
|
||||||
|
DROP INDEX IF EXISTS [IX_ApiKey_ClientSecretHash]
|
||||||
|
ON [dbo].[ApiKey];
|
||||||
|
|
||||||
|
END
|
||||||
|
GO
|
65
util/Migrator/DbScripts_future/2023-06-FutureMigration.sql
Normal file
65
util/Migrator/DbScripts_future/2023-06-FutureMigration.sql
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
-- Remove Column
|
||||||
|
IF COL_LENGTH('[dbo].[ApiKey]', 'ClientSecret') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE
|
||||||
|
[dbo].[ApiKey]
|
||||||
|
DROP COLUMN
|
||||||
|
[ClientSecret]
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- Refresh views
|
||||||
|
IF OBJECT_ID('[dbo].[ApiKeyDetailsView]') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
EXECUTE sp_refreshview N'[dbo].[ApiKeyDetailsView]';
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID('[dbo].[ApiKeyView]') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
EXECUTE sp_refreshview N'[dbo].[ApiKeyView]';
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE OR ALTER PROCEDURE [dbo].[ApiKey_Create]
|
||||||
|
@Id UNIQUEIDENTIFIER OUTPUT,
|
||||||
|
@ServiceAccountId UNIQUEIDENTIFIER,
|
||||||
|
@Name VARCHAR(200),
|
||||||
|
@ClientSecretHash VARCHAR(128),
|
||||||
|
@Scope NVARCHAR(4000),
|
||||||
|
@EncryptedPayload NVARCHAR(4000),
|
||||||
|
@Key VARCHAR(MAX),
|
||||||
|
@ExpireAt DATETIME2(7),
|
||||||
|
@CreationDate DATETIME2(7),
|
||||||
|
@RevisionDate DATETIME2(7)
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
INSERT INTO [dbo].[ApiKey]
|
||||||
|
(
|
||||||
|
[Id],
|
||||||
|
[ServiceAccountId],
|
||||||
|
[Name],
|
||||||
|
[ClientSecretHash],
|
||||||
|
[Scope],
|
||||||
|
[EncryptedPayload],
|
||||||
|
[Key],
|
||||||
|
[ExpireAt],
|
||||||
|
[CreationDate],
|
||||||
|
[RevisionDate]
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
@Id,
|
||||||
|
@ServiceAccountId,
|
||||||
|
@Name,
|
||||||
|
@ClientSecretHash,
|
||||||
|
@Scope,
|
||||||
|
@EncryptedPayload,
|
||||||
|
@Key,
|
||||||
|
@ExpireAt,
|
||||||
|
@CreationDate,
|
||||||
|
@RevisionDate
|
||||||
|
)
|
||||||
|
END
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user