mirror of
https://github.com/bitwarden/server.git
synced 2025-06-13 22:40:48 -05:00
Merge branch 'main' into auth/pm-20348/extension-auth-approvals-add-auth-request-endpoint
This commit is contained in:
commit
03a5e22d41
@ -3,7 +3,7 @@
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"swashbuckle.aspnetcore.cli": {
|
||||
"version": "7.2.0",
|
||||
"version": "7.3.2",
|
||||
"commands": ["swagger"]
|
||||
},
|
||||
"dotnet-ef": {
|
||||
|
7
.github/renovate.json5
vendored
7
.github/renovate.json5
vendored
@ -15,8 +15,7 @@
|
||||
matchManagers: ["github-actions"],
|
||||
matchFileNames: [
|
||||
".github/workflows/publish.yml",
|
||||
".github/workflows/release.yml",
|
||||
".github/workflows/repository-management.yml"
|
||||
".github/workflows/release.yml"
|
||||
],
|
||||
commitMessagePrefix: "[deps] BRE:",
|
||||
reviewers: ["team:dept-bre"],
|
||||
@ -134,8 +133,8 @@
|
||||
reviewers: ["team:dept-dbops"],
|
||||
},
|
||||
{
|
||||
matchPackageNames: ["CommandDotNet", "YamlDotNet"],
|
||||
description: "DevOps owned dependencies",
|
||||
matchPackageNames: ["YamlDotNet"],
|
||||
description: "BRE owned dependencies",
|
||||
commitMessagePrefix: "[deps] BRE:",
|
||||
reviewers: ["team:dept-bre"],
|
||||
},
|
||||
|
250
.github/workflows/build.yml
vendored
250
.github/workflows/build.yml
vendored
@ -11,6 +11,9 @@ on:
|
||||
types: [opened, synchronize]
|
||||
workflow_call:
|
||||
inputs: {}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
_AZ_REGISTRY: "bitwardenprod.azurecr.io"
|
||||
@ -19,7 +22,7 @@ env:
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
@ -33,115 +36,15 @@ jobs:
|
||||
run: dotnet format --verify-no-changes
|
||||
|
||||
build-artifacts:
|
||||
name: Build artifacts
|
||||
runs-on: ubuntu-22.04
|
||||
name: Build Docker images
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- lint
|
||||
outputs:
|
||||
has_secrets: ${{ steps.check-secrets.outputs.has_secrets }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- project_name: Admin
|
||||
base_path: ./src
|
||||
node: true
|
||||
- project_name: Api
|
||||
base_path: ./src
|
||||
- project_name: Billing
|
||||
base_path: ./src
|
||||
- project_name: Events
|
||||
base_path: ./src
|
||||
- project_name: EventsProcessor
|
||||
base_path: ./src
|
||||
- project_name: Icons
|
||||
base_path: ./src
|
||||
- project_name: Identity
|
||||
base_path: ./src
|
||||
- project_name: MsSqlMigratorUtility
|
||||
base_path: ./util
|
||||
dotnet: true
|
||||
- project_name: Notifications
|
||||
base_path: ./src
|
||||
- project_name: Scim
|
||||
base_path: ./bitwarden_license/src
|
||||
dotnet: true
|
||||
- project_name: Server
|
||||
base_path: ./util
|
||||
- project_name: Setup
|
||||
base_path: ./util
|
||||
- project_name: Sso
|
||||
base_path: ./bitwarden_license/src
|
||||
node: true
|
||||
steps:
|
||||
- name: Check secrets
|
||||
id: check-secrets
|
||||
env:
|
||||
AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||
run: |
|
||||
has_secrets=${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL != '' }}
|
||||
echo "has_secrets=$has_secrets" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Set up .NET
|
||||
uses: actions/setup-dotnet@87b7050bc53ea08284295505d98d2aa94301e852 # v4.2.0
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||
with:
|
||||
cache: "npm"
|
||||
cache-dependency-path: "**/package-lock.json"
|
||||
node-version: "16"
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
whoami
|
||||
dotnet --info
|
||||
node --version
|
||||
npm --version
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
echo "GitHub event: $GITHUB_EVENT"
|
||||
|
||||
- name: Build node
|
||||
if: ${{ matrix.node }}
|
||||
working-directory: ${{ matrix.base_path }}/${{ matrix.project_name }}
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
|
||||
- name: Publish project
|
||||
working-directory: ${{ matrix.base_path }}/${{ matrix.project_name }}
|
||||
run: |
|
||||
echo "Publish"
|
||||
dotnet publish -c "Release" -o obj/build-output/publish
|
||||
|
||||
cd obj/build-output/publish
|
||||
zip -r ${{ matrix.project_name }}.zip .
|
||||
mv ${{ matrix.project_name }}.zip ../../../
|
||||
|
||||
pwd
|
||||
ls -atlh ../../../
|
||||
|
||||
- name: Upload project artifact
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
name: ${{ matrix.project_name }}.zip
|
||||
path: ${{ matrix.base_path }}/${{ matrix.project_name }}/${{ matrix.project_name }}.zip
|
||||
if-no-files-found: error
|
||||
|
||||
build-docker:
|
||||
name: Build Docker images
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
security-events: write
|
||||
id-token: write
|
||||
needs:
|
||||
- build-artifacts
|
||||
if: ${{ needs.build-artifacts.outputs.has_secrets == 'true' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@ -149,6 +52,7 @@ jobs:
|
||||
- project_name: Admin
|
||||
base_path: ./src
|
||||
dotnet: true
|
||||
node: true
|
||||
- project_name: Api
|
||||
base_path: ./src
|
||||
dotnet: true
|
||||
@ -182,9 +86,6 @@ jobs:
|
||||
- project_name: Scim
|
||||
base_path: ./bitwarden_license/src
|
||||
dotnet: true
|
||||
- project_name: Server
|
||||
base_path: ./util
|
||||
dotnet: true
|
||||
- project_name: Setup
|
||||
base_path: ./util
|
||||
dotnet: true
|
||||
@ -192,6 +93,14 @@ jobs:
|
||||
base_path: ./bitwarden_license/src
|
||||
dotnet: true
|
||||
steps:
|
||||
- name: Check secrets
|
||||
id: check-secrets
|
||||
env:
|
||||
AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
|
||||
run: |
|
||||
has_secrets=${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL != '' }}
|
||||
echo "has_secrets=$has_secrets" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
with:
|
||||
@ -203,13 +112,67 @@ jobs:
|
||||
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
|
||||
|
||||
- name: Set up .NET
|
||||
uses: actions/setup-dotnet@87b7050bc53ea08284295505d98d2aa94301e852 # v4.2.0
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
||||
with:
|
||||
cache: "npm"
|
||||
cache-dependency-path: "**/package-lock.json"
|
||||
node-version: "16"
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
whoami
|
||||
dotnet --info
|
||||
node --version
|
||||
npm --version
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
echo "GitHub event: $GITHUB_EVENT"
|
||||
|
||||
- name: Build node
|
||||
if: ${{ matrix.node }}
|
||||
working-directory: ${{ matrix.base_path }}/${{ matrix.project_name }}
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
|
||||
- name: Publish project
|
||||
working-directory: ${{ matrix.base_path }}/${{ matrix.project_name }}
|
||||
if: ${{ matrix.dotnet }}
|
||||
run: |
|
||||
echo "Publish"
|
||||
dotnet publish -c "Release" -o obj/build-output/publish
|
||||
|
||||
cd obj/build-output/publish
|
||||
zip -r ${{ matrix.project_name }}.zip .
|
||||
mv ${{ matrix.project_name }}.zip ../../../
|
||||
|
||||
pwd
|
||||
ls -atlh ../../../
|
||||
|
||||
- name: Upload project artifact
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
if: ${{ matrix.dotnet }}
|
||||
with:
|
||||
name: ${{ matrix.project_name }}.zip
|
||||
path: ${{ matrix.base_path }}/${{ matrix.project_name }}/${{ matrix.project_name }}.zip
|
||||
if-no-files-found: error
|
||||
|
||||
########## Set up Docker ##########
|
||||
- name: Set up QEMU emulators
|
||||
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||
|
||||
########## ACRs ##########
|
||||
- name: Log in to Azure - production subscription
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
@ -277,26 +240,16 @@ jobs:
|
||||
fi
|
||||
echo "tags=$TAGS" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get build artifact
|
||||
if: ${{ matrix.dotnet }}
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: ${{ matrix.project_name }}.zip
|
||||
|
||||
- name: Set up build artifact
|
||||
if: ${{ matrix.dotnet }}
|
||||
run: |
|
||||
mkdir -p ${{ matrix.base_path}}/${{ matrix.project_name }}/obj/build-output/publish
|
||||
unzip ${{ matrix.project_name }}.zip \
|
||||
-d ${{ matrix.base_path }}/${{ matrix.project_name }}/obj/build-output/publish
|
||||
|
||||
- name: Build Docker image
|
||||
id: build-docker
|
||||
id: build-artifacts
|
||||
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
|
||||
with:
|
||||
context: ${{ matrix.base_path }}/${{ matrix.project_name }}
|
||||
context: .
|
||||
file: ${{ matrix.base_path }}/${{ matrix.project_name }}/Dockerfile
|
||||
platforms: linux/amd64
|
||||
platforms: |
|
||||
linux/amd64,
|
||||
linux/arm/v7,
|
||||
linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.image-tags.outputs.tags }}
|
||||
secrets: |
|
||||
@ -309,7 +262,7 @@ jobs:
|
||||
- name: Sign image with Cosign
|
||||
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
||||
env:
|
||||
DIGEST: ${{ steps.build-docker.outputs.digest }}
|
||||
DIGEST: ${{ steps.build-artifacts.outputs.digest }}
|
||||
TAGS: ${{ steps.image-tags.outputs.tags }}
|
||||
run: |
|
||||
IFS="," read -a tags <<< "${TAGS}"
|
||||
@ -336,8 +289,8 @@ jobs:
|
||||
|
||||
upload:
|
||||
name: Upload
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build-docker
|
||||
runs-on: ubuntu-24.04
|
||||
needs: build-artifacts
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
@ -377,9 +330,9 @@ jobs:
|
||||
|
||||
# Run setup
|
||||
docker run -i --rm --name setup -v $STUB_OUTPUT/US:/bitwarden $SETUP_IMAGE \
|
||||
dotnet Setup.dll -stub 1 -install 1 -domain bitwarden.example.com -os lin -cloud-region US
|
||||
/app/Setup -stub 1 -install 1 -domain bitwarden.example.com -os lin -cloud-region US
|
||||
docker run -i --rm --name setup -v $STUB_OUTPUT/EU:/bitwarden $SETUP_IMAGE \
|
||||
dotnet Setup.dll -stub 1 -install 1 -domain bitwarden.example.com -os lin -cloud-region EU
|
||||
/app/Setup -stub 1 -install 1 -domain bitwarden.example.com -os lin -cloud-region EU
|
||||
|
||||
sudo chown -R $(whoami):$(whoami) $STUB_OUTPUT
|
||||
|
||||
@ -397,14 +350,6 @@ jobs:
|
||||
cd docker-stub/US; zip -r ../../docker-stub-US.zip *; cd ../..
|
||||
cd docker-stub/EU; zip -r ../../docker-stub-EU.zip *; cd ../..
|
||||
|
||||
- name: Make Docker stub checksums
|
||||
if: |
|
||||
github.event_name != 'pull_request'
|
||||
&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
|
||||
run: |
|
||||
sha256sum docker-stub-US.zip > docker-stub-US-sha256.txt
|
||||
sha256sum docker-stub-EU.zip > docker-stub-EU-sha256.txt
|
||||
|
||||
- name: Upload Docker stub US artifact
|
||||
if: |
|
||||
github.event_name != 'pull_request'
|
||||
@ -425,26 +370,6 @@ jobs:
|
||||
path: docker-stub-EU.zip
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Docker stub US checksum artifact
|
||||
if: |
|
||||
github.event_name != 'pull_request'
|
||||
&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
name: docker-stub-US-sha256.txt
|
||||
path: docker-stub-US-sha256.txt
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Docker stub EU checksum artifact
|
||||
if: |
|
||||
github.event_name != 'pull_request'
|
||||
&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
name: docker-stub-EU-sha256.txt
|
||||
path: docker-stub-EU-sha256.txt
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Build Public API Swagger
|
||||
run: |
|
||||
cd ./src/Api
|
||||
@ -512,7 +437,7 @@ jobs:
|
||||
|
||||
build-mssqlmigratorutility:
|
||||
name: Build MSSQL migrator utility
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- lint
|
||||
defaults:
|
||||
@ -568,9 +493,9 @@ jobs:
|
||||
if: |
|
||||
github.event_name != 'pull_request'
|
||||
&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- build-docker
|
||||
- build-artifacts
|
||||
steps:
|
||||
- name: Log in to Azure - CI subscription
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
@ -604,7 +529,7 @@ jobs:
|
||||
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-22.04
|
||||
needs:
|
||||
- build-docker
|
||||
- build-artifacts
|
||||
steps:
|
||||
- name: Log in to Azure - CI subscription
|
||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||
@ -638,7 +563,6 @@ jobs:
|
||||
name: Setup Ephemeral Environment
|
||||
needs:
|
||||
- build-artifacts
|
||||
- build-docker
|
||||
if: |
|
||||
needs.build-artifacts.outputs.has_secrets == 'true'
|
||||
&& github.event_name == 'pull_request'
|
||||
@ -646,8 +570,9 @@ jobs:
|
||||
uses: bitwarden/gh-actions/.github/workflows/_ephemeral_environment_manager.yml@main
|
||||
with:
|
||||
project: server
|
||||
pull_request_number: ${{ github.event.number }}
|
||||
pull_request_number: ${{ github.event.number || 0 }}
|
||||
secrets: inherit
|
||||
permissions: read-all
|
||||
|
||||
check-failures:
|
||||
name: Check for failures
|
||||
@ -656,7 +581,6 @@ jobs:
|
||||
needs:
|
||||
- lint
|
||||
- build-artifacts
|
||||
- build-docker
|
||||
- upload
|
||||
- build-mssqlmigratorutility
|
||||
- self-host-build
|
||||
|
1
.github/workflows/code-references.yml
vendored
1
.github/workflows/code-references.yml
vendored
@ -44,6 +44,7 @@ jobs:
|
||||
with:
|
||||
accessToken: ${{ secrets.LD_ACCESS_TOKEN }}
|
||||
projKey: default
|
||||
allowTags: true
|
||||
|
||||
- name: Add label
|
||||
if: steps.collect.outputs.any-changed == 'true'
|
||||
|
9
.github/workflows/release.yml
vendored
9
.github/workflows/release.yml
vendored
@ -17,6 +17,9 @@ on:
|
||||
env:
|
||||
_AZ_REGISTRY: "bitwardenprod.azurecr.io"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
name: Setup
|
||||
@ -65,9 +68,7 @@ jobs:
|
||||
workflow_conclusion: success
|
||||
branch: ${{ needs.setup.outputs.branch-name }}
|
||||
artifacts: "docker-stub-US.zip,
|
||||
docker-stub-US-sha256.txt,
|
||||
docker-stub-EU.zip,
|
||||
docker-stub-EU-sha256.txt,
|
||||
swagger.json"
|
||||
|
||||
- name: Dry Run - Download latest release Docker stubs
|
||||
@ -78,9 +79,7 @@ jobs:
|
||||
workflow_conclusion: success
|
||||
branch: main
|
||||
artifacts: "docker-stub-US.zip,
|
||||
docker-stub-US-sha256.txt,
|
||||
docker-stub-EU.zip,
|
||||
docker-stub-EU-sha256.txt,
|
||||
swagger.json"
|
||||
|
||||
- name: Create release
|
||||
@ -88,9 +87,7 @@ jobs:
|
||||
uses: ncipollo/release-action@cdcc88a9acf3ca41c16c37bb7d21b9ad48560d87 # v1.15.0
|
||||
with:
|
||||
artifacts: "docker-stub-US.zip,
|
||||
docker-stub-US-sha256.txt,
|
||||
docker-stub-EU.zip,
|
||||
docker-stub-EU-sha256.txt,
|
||||
swagger.json"
|
||||
commit: ${{ github.sha }}
|
||||
tag: "v${{ needs.setup.outputs.release_version }}"
|
||||
|
@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
|
||||
<Version>2025.5.1</Version>
|
||||
<Version>2025.6.1</Version>
|
||||
|
||||
<RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
@ -69,5 +69,4 @@
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
</Project>
|
@ -5,9 +5,6 @@
|
||||
<a href="https://github.com/bitwarden/server/actions/workflows/build.yml?query=branch:main" target="_blank">
|
||||
<img src="https://github.com/bitwarden/server/actions/workflows/build.yml/badge.svg?branch=main" alt="Github Workflow build on main" />
|
||||
</a>
|
||||
<a href="https://hub.docker.com/u/bitwarden/" target="_blank">
|
||||
<img src="https://img.shields.io/docker/pulls/bitwarden/api.svg" alt="DockerHub" />
|
||||
</a>
|
||||
<a href="https://gitter.im/bitwarden/Lobby" target="_blank">
|
||||
<img src="https://badges.gitter.im/bitwarden/Lobby.svg" alt="gitter chat" />
|
||||
</a>
|
||||
@ -26,12 +23,12 @@ Please refer to the [Server Setup Guide](https://contributing.bitwarden.com/gett
|
||||
## Deploy
|
||||
|
||||
<p align="center">
|
||||
<a href="https://hub.docker.com/u/bitwarden/" target="_blank">
|
||||
<a href="https://github.com/orgs/bitwarden/packages" target="_blank">
|
||||
<img src="https://i.imgur.com/SZc8JnH.png" alt="docker" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
You can deploy Bitwarden using Docker containers on Windows, macOS, and Linux distributions. Use the provided PowerShell and Bash scripts to get started quickly. Find all of the Bitwarden images on [Docker Hub](https://hub.docker.com/u/bitwarden/).
|
||||
You can deploy Bitwarden using Docker containers on Windows, macOS, and Linux distributions. Use the provided PowerShell and Bash scripts to get started quickly. Find all of the Bitwarden images on [GitHub Container Registry](https://github.com/orgs/bitwarden/packages).
|
||||
|
||||
Full documentation for deploying Bitwarden with Docker can be found in our help center at: https://help.bitwarden.com/article/install-on-premise/
|
||||
|
||||
|
@ -287,11 +287,10 @@ public class ProviderService : IProviderService
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
if (!keyedFilteredUsers.ContainsKey(user.Id))
|
||||
if (!keyedFilteredUsers.TryGetValue(user.Id, out var providerUser))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var providerUser = keyedFilteredUsers[user.Id];
|
||||
try
|
||||
{
|
||||
if (providerUser.Status != ProviderUserStatusType.Accepted || providerUser.ProviderId != providerId)
|
||||
|
@ -1,6 +1,5 @@
|
||||
#nullable enable
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Bit.Core;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Entities.Provider;
|
||||
using Bit.Core.AdminConsole.Enums.Provider;
|
||||
@ -27,7 +26,6 @@ using Stripe;
|
||||
|
||||
namespace Bit.Commercial.Core.Billing.Providers.Services;
|
||||
|
||||
[RequireFeature(FeatureFlagKeys.PM18770_EnableOrganizationBusinessUnitConversion)]
|
||||
public class BusinessUnitConverter(
|
||||
IDataProtectionProvider dataProtectionProvider,
|
||||
GlobalSettings globalSettings,
|
||||
|
@ -550,6 +550,15 @@ public class ProviderBillingService(
|
||||
[
|
||||
new CustomerTaxIdDataOptions { Type = taxIdType, Value = taxInfo.TaxIdNumber }
|
||||
];
|
||||
|
||||
if (taxIdType == StripeConstants.TaxIdType.SpanishNIF)
|
||||
{
|
||||
options.TaxIdData.Add(new CustomerTaxIdDataOptions
|
||||
{
|
||||
Type = StripeConstants.TaxIdType.EUVAT,
|
||||
Value = $"ES{taxInfo.TaxIdNumber}"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(provider.DiscountId))
|
||||
|
@ -1,4 +0,0 @@
|
||||
*
|
||||
!obj/build-output/publish/*
|
||||
!obj/Docker/empty/
|
||||
!entrypoint.sh
|
@ -1,6 +1,50 @@
|
||||
###############################################
|
||||
# Build stage #
|
||||
###############################################
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS 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
|
||||
|
||||
# Copy required project files
|
||||
WORKDIR /source
|
||||
COPY . ./
|
||||
|
||||
# Restore project dependencies and tools
|
||||
WORKDIR /source/bitwarden_license/src/Scim
|
||||
RUN . /tmp/rid.txt && dotnet restore -r $RID
|
||||
|
||||
# Build project
|
||||
RUN . /tmp/rid.txt && dotnet publish \
|
||||
-c release \
|
||||
--no-restore \
|
||||
--self-contained \
|
||||
/p:PublishSingleFile=true \
|
||||
-r $RID \
|
||||
-o out
|
||||
|
||||
###############################################
|
||||
# App stage #
|
||||
###############################################
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
LABEL com.bitwarden.product="bitwarden"
|
||||
ENV ASPNETCORE_ENVIRONMENT=Production
|
||||
ENV ASPNETCORE_URLS=http://+:5000
|
||||
ENV SSL_CERT_DIR=/etc/bitwarden/ca-certificates
|
||||
EXPOSE 5000
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
@ -9,11 +53,10 @@ RUN apt-get update \
|
||||
krb5-user \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV ASPNETCORE_URLS http://+:5000
|
||||
# Copy app from the build stage
|
||||
WORKDIR /app
|
||||
EXPOSE 5000
|
||||
COPY obj/build-output/publish .
|
||||
COPY entrypoint.sh /
|
||||
COPY --from=build /source/bitwarden_license/src/Scim/out /app
|
||||
COPY ./bitwarden_license/src/Scim/entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
HEALTHCHECK CMD curl -f http://localhost:5000/alive || exit 1
|
||||
|
@ -16,8 +16,8 @@ public class Program
|
||||
{
|
||||
var context = e.Properties["SourceContext"].ToString();
|
||||
|
||||
if (e.Properties.ContainsKey("RequestPath") &&
|
||||
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
|
||||
if (e.Properties.TryGetValue("RequestPath", out var requestPath) &&
|
||||
!string.IsNullOrWhiteSpace(requestPath?.ToString()) &&
|
||||
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
||||
{
|
||||
return false;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Setup
|
||||
|
||||
@ -19,31 +19,42 @@ then
|
||||
LGID=65534
|
||||
fi
|
||||
|
||||
# Create user and group
|
||||
if [ "$(id -u)" = "0" ]
|
||||
then
|
||||
# Create user and group
|
||||
|
||||
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
||||
groupmod -o -g $LGID $GROUPNAME >/dev/null 2>&1
|
||||
useradd -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1 ||
|
||||
usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1
|
||||
mkhomedir_helper $USERNAME
|
||||
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
||||
groupmod -o -g $LGID $GROUPNAME >/dev/null 2>&1
|
||||
useradd -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1 ||
|
||||
usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1
|
||||
mkhomedir_helper $USERNAME
|
||||
|
||||
# The rest...
|
||||
# The rest...
|
||||
|
||||
chown -R $USERNAME:$GROUPNAME /app
|
||||
mkdir -p /etc/bitwarden/core
|
||||
mkdir -p /etc/bitwarden/logs
|
||||
mkdir -p /etc/bitwarden/ca-certificates
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||
chown -R $USERNAME:$GROUPNAME /app
|
||||
mkdir -p /etc/bitwarden/core
|
||||
mkdir -p /etc/bitwarden/logs
|
||||
mkdir -p /etc/bitwarden/ca-certificates
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||
|
||||
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
||||
&& update-ca-certificates
|
||||
if [[ -f "/etc/bitwarden/kerberos/bitwarden.keytab" && -f "/etc/bitwarden/kerberos/krb5.conf" ]]; then
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden/kerberos
|
||||
fi
|
||||
|
||||
gosu_cmd="gosu $USERNAME:$GROUPNAME"
|
||||
else
|
||||
gosu_cmd=""
|
||||
fi
|
||||
|
||||
if [[ -f "/etc/bitwarden/kerberos/bitwarden.keytab" && -f "/etc/bitwarden/kerberos/krb5.conf" ]]; then
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden/kerberos
|
||||
cp -f /etc/bitwarden/kerberos/krb5.conf /etc/krb5.conf
|
||||
gosu $USERNAME:$GROUPNAME kinit $globalSettings__kerberosUser -k -t /etc/bitwarden/kerberos/bitwarden.keytab
|
||||
cp -f /etc/bitwarden/kerberos/krb5.conf /etc/krb5.conf
|
||||
$gosu_cmd kinit $globalSettings__kerberosUser -k -t /etc/bitwarden/kerberos/bitwarden.keytab
|
||||
fi
|
||||
|
||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/Scim.dll
|
||||
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||
if [[ -z $globalSettings__identityServer__certificateLocation ]]; then
|
||||
export globalSettings__identityServer__certificateLocation=/etc/bitwarden/identity/identity.pfx
|
||||
fi
|
||||
fi
|
||||
|
||||
exec $gosu_cmd /app/Scim
|
||||
|
@ -370,8 +370,8 @@ public class AccountController : Controller
|
||||
// for the user identifier.
|
||||
static bool nameIdIsNotTransient(Claim c) => c.Type == ClaimTypes.NameIdentifier
|
||||
&& (c.Properties == null
|
||||
|| !c.Properties.ContainsKey(SamlPropertyKeys.ClaimFormat)
|
||||
|| c.Properties[SamlPropertyKeys.ClaimFormat] != SamlNameIdFormats.Transient);
|
||||
|| !c.Properties.TryGetValue(SamlPropertyKeys.ClaimFormat, out var claimFormat)
|
||||
|| claimFormat != SamlNameIdFormats.Transient);
|
||||
|
||||
// Try to determine the unique id of the external user (issued by the provider)
|
||||
// the most common claim type for that are the sub claim and the NameIdentifier
|
||||
@ -499,9 +499,9 @@ public class AccountController : Controller
|
||||
// Before any user creation - if Org User doesn't exist at this point - make sure there are enough seats to add one
|
||||
if (orgUser == null && organization.Seats.HasValue)
|
||||
{
|
||||
var occupiedSeats = await _organizationUserRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id);
|
||||
var occupiedSeats = await _organizationRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id);
|
||||
var initialSeatCount = organization.Seats.Value;
|
||||
var availableSeats = initialSeatCount - occupiedSeats;
|
||||
var availableSeats = initialSeatCount - occupiedSeats.Total;
|
||||
if (availableSeats < 1)
|
||||
{
|
||||
try
|
||||
|
@ -1,6 +1,50 @@
|
||||
###############################################
|
||||
# Build stage #
|
||||
###############################################
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS 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
|
||||
|
||||
# Copy required project files
|
||||
WORKDIR /source
|
||||
COPY . ./
|
||||
|
||||
# Restore project dependencies and tools
|
||||
WORKDIR /source/bitwarden_license/src/Sso
|
||||
RUN . /tmp/rid.txt && dotnet restore -r $RID
|
||||
|
||||
# Build project
|
||||
RUN . /tmp/rid.txt && dotnet publish \
|
||||
-c release \
|
||||
--no-restore \
|
||||
--self-contained \
|
||||
/p:PublishSingleFile=true \
|
||||
-r $RID \
|
||||
-o out
|
||||
|
||||
###############################################
|
||||
# App stage #
|
||||
###############################################
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
LABEL com.bitwarden.product="bitwarden"
|
||||
ENV ASPNETCORE_ENVIRONMENT=Production
|
||||
ENV ASPNETCORE_URLS=http://+:5000
|
||||
ENV SSL_CERT_DIR=/etc/bitwarden/ca-certificates
|
||||
EXPOSE 5000
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
@ -9,11 +53,10 @@ RUN apt-get update \
|
||||
krb5-user \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV ASPNETCORE_URLS http://+:5000
|
||||
# Copy app from the build stage
|
||||
WORKDIR /app
|
||||
EXPOSE 5000
|
||||
COPY obj/build-output/publish .
|
||||
COPY entrypoint.sh /
|
||||
COPY --from=build /source/bitwarden_license/src/Sso/out /app
|
||||
COPY ./bitwarden_license/src/Sso/entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
HEALTHCHECK CMD curl -f http://localhost:5000/alive || exit 1
|
||||
|
@ -17,8 +17,8 @@ public class Program
|
||||
logging.AddSerilog(hostingContext, (e, globalSettings) =>
|
||||
{
|
||||
var context = e.Properties["SourceContext"].ToString();
|
||||
if (e.Properties.ContainsKey("RequestPath") &&
|
||||
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
|
||||
if (e.Properties.TryGetValue("RequestPath", out var requestPath) &&
|
||||
!string.IsNullOrWhiteSpace(requestPath?.ToString()) &&
|
||||
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
||||
{
|
||||
return false;
|
||||
|
@ -46,9 +46,9 @@ public static class OpenIdConnectOptionsExtensions
|
||||
|
||||
// Handle State if we've gotten that back
|
||||
var decodedState = options.StateDataFormat.Unprotect(state);
|
||||
if (decodedState != null && decodedState.Items.ContainsKey("scheme"))
|
||||
if (decodedState != null && decodedState.Items.TryGetValue("scheme", out var stateScheme))
|
||||
{
|
||||
return decodedState.Items["scheme"] == scheme;
|
||||
return stateScheme == scheme;
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Setup
|
||||
|
||||
@ -19,37 +19,42 @@ then
|
||||
LGID=65534
|
||||
fi
|
||||
|
||||
# Create user and group
|
||||
if [ "$(id -u)" = "0" ]
|
||||
then
|
||||
# Create user and group
|
||||
|
||||
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
||||
groupmod -o -g $LGID $GROUPNAME >/dev/null 2>&1
|
||||
useradd -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1 ||
|
||||
usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1
|
||||
mkhomedir_helper $USERNAME
|
||||
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
||||
groupmod -o -g $LGID $GROUPNAME >/dev/null 2>&1
|
||||
useradd -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1 ||
|
||||
usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1
|
||||
mkhomedir_helper $USERNAME
|
||||
|
||||
# The rest...
|
||||
# The rest...
|
||||
|
||||
mkdir -p /etc/bitwarden/identity
|
||||
mkdir -p /etc/bitwarden/core
|
||||
mkdir -p /etc/bitwarden/logs
|
||||
mkdir -p /etc/bitwarden/ca-certificates
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||
chown -R $USERNAME:$GROUPNAME /app
|
||||
mkdir -p /etc/bitwarden/core
|
||||
mkdir -p /etc/bitwarden/logs
|
||||
mkdir -p /etc/bitwarden/ca-certificates
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||
|
||||
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||
cp /etc/bitwarden/identity/identity.pfx /app/identity.pfx
|
||||
fi
|
||||
if [[ -f "/etc/bitwarden/kerberos/bitwarden.keytab" && -f "/etc/bitwarden/kerberos/krb5.conf" ]]; then
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden/kerberos
|
||||
fi
|
||||
|
||||
chown -R $USERNAME:$GROUPNAME /app
|
||||
|
||||
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
||||
&& update-ca-certificates
|
||||
gosu_cmd="gosu $USERNAME:$GROUPNAME"
|
||||
else
|
||||
gosu_cmd=""
|
||||
fi
|
||||
|
||||
if [[ -f "/etc/bitwarden/kerberos/bitwarden.keytab" && -f "/etc/bitwarden/kerberos/krb5.conf" ]]; then
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden/kerberos
|
||||
cp -f /etc/bitwarden/kerberos/krb5.conf /etc/krb5.conf
|
||||
gosu $USERNAME:$GROUPNAME kinit $globalSettings__kerberosUser -k -t /etc/bitwarden/kerberos/bitwarden.keytab
|
||||
cp -f /etc/bitwarden/kerberos/krb5.conf /etc/krb5.conf
|
||||
$gosu_cmd kinit $globalSettings__kerberosUser -k -t /etc/bitwarden/kerberos/bitwarden.keytab
|
||||
fi
|
||||
|
||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/Sso.dll
|
||||
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||
if [[ -z $globalSettings__identityServer__certificateLocation ]]; then
|
||||
export globalSettings__identityServer__certificateLocation=/etc/bitwarden/identity/identity.pfx
|
||||
fi
|
||||
fi
|
||||
|
||||
exec $gosu_cmd /app/Sso
|
||||
|
199
bitwarden_license/src/Sso/package-lock.json
generated
199
bitwarden_license/src/Sso/package-lock.json
generated
@ -9,7 +9,7 @@
|
||||
"version": "0.0.0",
|
||||
"license": "-",
|
||||
"dependencies": {
|
||||
"bootstrap": "5.3.3",
|
||||
"bootstrap": "5.3.6",
|
||||
"font-awesome": "4.7.0",
|
||||
"jquery": "3.7.1"
|
||||
},
|
||||
@ -17,9 +17,9 @@
|
||||
"css-loader": "7.1.2",
|
||||
"expose-loader": "5.0.1",
|
||||
"mini-css-extract-plugin": "2.9.2",
|
||||
"sass": "1.85.0",
|
||||
"sass-loader": "16.0.4",
|
||||
"webpack": "5.97.1",
|
||||
"sass": "1.88.0",
|
||||
"sass-loader": "16.0.5",
|
||||
"webpack": "5.99.8",
|
||||
"webpack-cli": "5.1.4"
|
||||
}
|
||||
},
|
||||
@ -455,13 +455,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.13.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz",
|
||||
"integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==",
|
||||
"version": "22.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
|
||||
"integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/ast": {
|
||||
@ -748,9 +748,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/bootstrap": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
|
||||
"integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
|
||||
"version": "5.3.6",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.6.tgz",
|
||||
"integrity": "sha512-jX0GAcRzvdwISuvArXn3m7KZscWWFAf1MKBcnzaN02qWMb3jpMoUX4/qgeiGzqyIb4ojulRzs89UCUmGcFSzTA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@ -781,9 +781,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
|
||||
"integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
|
||||
"version": "4.24.5",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz",
|
||||
"integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -801,10 +801,10 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001688",
|
||||
"electron-to-chromium": "^1.5.73",
|
||||
"caniuse-lite": "^1.0.30001716",
|
||||
"electron-to-chromium": "^1.5.149",
|
||||
"node-releases": "^2.0.19",
|
||||
"update-browserslist-db": "^1.1.1"
|
||||
"update-browserslist-db": "^1.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
@ -821,9 +821,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001707",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz",
|
||||
"integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==",
|
||||
"version": "1.0.30001718",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
|
||||
"integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -975,9 +975,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.128",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.128.tgz",
|
||||
"integrity": "sha512-bo1A4HH/NS522Ws0QNFIzyPcyUUNV/yyy70Ho1xqfGYzPUme2F/xr4tlEOuM6/A538U1vDA7a4XfCd1CKRegKQ==",
|
||||
"version": "1.5.155",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz",
|
||||
"integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@ -1009,9 +1009,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
|
||||
"integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==",
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
|
||||
"integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@ -1106,13 +1106,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
|
||||
@ -1248,9 +1241,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/immutable": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.1.tgz",
|
||||
"integrity": "sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.2.tgz",
|
||||
"integrity": "sha512-qHKXW1q6liAk1Oys6umoaZbDRqjcjgSrbnrifHsfsttza7zcvRAsL7mMV6xWcyhwQy7Xj5v4hhbr6b+iDYwlmQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@ -1754,16 +1747,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
@ -1877,9 +1860,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.85.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.85.0.tgz",
|
||||
"integrity": "sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww==",
|
||||
"version": "1.88.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.88.0.tgz",
|
||||
"integrity": "sha512-sF6TWQqjFvr4JILXzG4ucGOLELkESHL+I5QJhh7CNaE+Yge0SI+ehCatsXhJ7ymU1hAFcIS3/PBpjdIbXoyVbg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -1898,9 +1881,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sass-loader": {
|
||||
"version": "16.0.4",
|
||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.4.tgz",
|
||||
"integrity": "sha512-LavLbgbBGUt3wCiYzhuLLu65+fWXaXLmq7YxivLhEqmiupCFZ5sKUAipK3do6V80YSU0jvSxNhEdT13IXNr3rg==",
|
||||
"version": "16.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.5.tgz",
|
||||
"integrity": "sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -1939,9 +1922,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
|
||||
"integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
|
||||
"integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -1959,9 +1942,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@ -2078,9 +2061,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
|
||||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz",
|
||||
"integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -2088,14 +2071,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.39.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz",
|
||||
"integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==",
|
||||
"version": "5.39.2",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.39.2.tgz",
|
||||
"integrity": "sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.8.2",
|
||||
"acorn": "^8.14.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
@ -2156,9 +2139,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@ -2193,16 +2176,6 @@
|
||||
"browserslist": ">= 4.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@ -2211,9 +2184,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
|
||||
"integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz",
|
||||
"integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -2225,14 +2198,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.97.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz",
|
||||
"integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==",
|
||||
"version": "5.99.8",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.8.tgz",
|
||||
"integrity": "sha512-lQ3CPiSTpfOnrEGeXDwoq5hIGzSjmwD72GdfVzF7CQAI7t47rJG9eDWvcEkEn3CUQymAElVvDg3YNTlCYj+qUQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.6",
|
||||
"@types/json-schema": "^7.0.15",
|
||||
"@webassemblyjs/ast": "^1.14.1",
|
||||
"@webassemblyjs/wasm-edit": "^1.14.1",
|
||||
"@webassemblyjs/wasm-parser": "^1.14.1",
|
||||
@ -2249,9 +2223,9 @@
|
||||
"loader-runner": "^4.2.0",
|
||||
"mime-types": "^2.1.27",
|
||||
"neo-async": "^2.6.2",
|
||||
"schema-utils": "^3.2.0",
|
||||
"schema-utils": "^4.3.2",
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"terser-webpack-plugin": "^5.3.11",
|
||||
"watchpack": "^2.4.1",
|
||||
"webpack-sources": "^3.2.3"
|
||||
},
|
||||
@ -2352,59 +2326,6 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/ajv-keywords": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"ajv": "^6.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/webpack/node_modules/schema-utils": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
||||
"integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.8",
|
||||
"ajv": "^6.12.5",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
@ -8,7 +8,7 @@
|
||||
"build": "webpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "5.3.3",
|
||||
"bootstrap": "5.3.6",
|
||||
"font-awesome": "4.7.0",
|
||||
"jquery": "3.7.1"
|
||||
},
|
||||
@ -16,9 +16,9 @@
|
||||
"css-loader": "7.1.2",
|
||||
"expose-loader": "5.0.1",
|
||||
"mini-css-extract-plugin": "2.9.2",
|
||||
"sass": "1.85.0",
|
||||
"sass-loader": "16.0.4",
|
||||
"webpack": "5.97.1",
|
||||
"sass": "1.88.0",
|
||||
"sass-loader": "16.0.5",
|
||||
"webpack": "5.99.8",
|
||||
"webpack-cli": "5.1.4"
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ services:
|
||||
- idp
|
||||
|
||||
rabbitmq:
|
||||
image: rabbitmq:management
|
||||
image: rabbitmq:4.1.0-management
|
||||
container_name: rabbitmq
|
||||
ports:
|
||||
- "5672:5672"
|
||||
@ -108,7 +108,7 @@ services:
|
||||
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER}
|
||||
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}
|
||||
volumes:
|
||||
- rabbitmq_data:/var/lib/rabbitmq_data
|
||||
- rabbitmq_data:/var/lib/rabbitmq
|
||||
profiles:
|
||||
- rabbitmq
|
||||
|
||||
|
@ -33,6 +33,39 @@
|
||||
"Name": "events-webhook-subscription"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "event-integrations",
|
||||
"Subscriptions": [
|
||||
{
|
||||
"Name": "integration-slack-subscription",
|
||||
"Rules": [
|
||||
{
|
||||
"Name": "slack-integration-filter",
|
||||
"Properties": {
|
||||
"FilterType": "Correlation",
|
||||
"CorrelationFilter": {
|
||||
"Label": "slack"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "integration-webhook-subscription",
|
||||
"Rules": [
|
||||
{
|
||||
"Name": "webhook-integration-filter",
|
||||
"Properties": {
|
||||
"FilterType": "Correlation",
|
||||
"CorrelationFilter": {
|
||||
"Label": "webhook"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ $corsRules = (@{
|
||||
AllowedMethods = @("Get", "PUT");
|
||||
});
|
||||
$containers = "attachments", "sendfiles", "misc";
|
||||
$queues = "event", "notifications", "reference-events", "mail";
|
||||
$queues = "event", "notifications", "mail";
|
||||
$tables = "event", "metadata", "installationdevice";
|
||||
# End configuration
|
||||
|
||||
|
@ -5,6 +5,6 @@
|
||||
},
|
||||
"msbuild-sdks": {
|
||||
"Microsoft.Build.Traversal": "4.1.0",
|
||||
"Microsoft.Build.Sql": "0.1.9-preview"
|
||||
"Microsoft.Build.Sql": "1.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +0,0 @@
|
||||
*
|
||||
!obj/build-output/publish/*
|
||||
!obj/Docker/empty/
|
||||
!entrypoint.sh
|
@ -12,7 +12,6 @@ using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Billing.Extensions;
|
||||
using Bit.Core.Billing.Pricing;
|
||||
using Bit.Core.Billing.Providers.Services;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.OrganizationConnectionConfigs;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
|
||||
@ -20,9 +19,6 @@ using Bit.Core.Repositories;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tools.Enums;
|
||||
using Bit.Core.Tools.Models.Business;
|
||||
using Bit.Core.Tools.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core.Vault.Repositories;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -45,12 +41,9 @@ public class OrganizationsController : Controller
|
||||
private readonly IPaymentService _paymentService;
|
||||
private readonly IApplicationCacheService _applicationCacheService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IReferenceEventService _referenceEventService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
private readonly ILogger<OrganizationsController> _logger;
|
||||
private readonly IAccessControlService _accessControlService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly ISecretRepository _secretRepository;
|
||||
private readonly IProjectRepository _projectRepository;
|
||||
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||
@ -73,12 +66,9 @@ public class OrganizationsController : Controller
|
||||
IPaymentService paymentService,
|
||||
IApplicationCacheService applicationCacheService,
|
||||
GlobalSettings globalSettings,
|
||||
IReferenceEventService referenceEventService,
|
||||
IUserService userService,
|
||||
IProviderRepository providerRepository,
|
||||
ILogger<OrganizationsController> logger,
|
||||
IAccessControlService accessControlService,
|
||||
ICurrentContext currentContext,
|
||||
ISecretRepository secretRepository,
|
||||
IProjectRepository projectRepository,
|
||||
IServiceAccountRepository serviceAccountRepository,
|
||||
@ -100,12 +90,9 @@ public class OrganizationsController : Controller
|
||||
_paymentService = paymentService;
|
||||
_applicationCacheService = applicationCacheService;
|
||||
_globalSettings = globalSettings;
|
||||
_referenceEventService = referenceEventService;
|
||||
_userService = userService;
|
||||
_providerRepository = providerRepository;
|
||||
_logger = logger;
|
||||
_accessControlService = accessControlService;
|
||||
_currentContext = currentContext;
|
||||
_secretRepository = secretRepository;
|
||||
_projectRepository = projectRepository;
|
||||
_serviceAccountRepository = serviceAccountRepository;
|
||||
@ -255,10 +242,32 @@ public class OrganizationsController : Controller
|
||||
Seats = organization.Seats
|
||||
};
|
||||
|
||||
if (model.PlanType.HasValue)
|
||||
{
|
||||
var freePlan = await _pricingClient.GetPlanOrThrow(model.PlanType.Value);
|
||||
var isDowngradingToFree = organization.PlanType != PlanType.Free && model.PlanType.Value == PlanType.Free;
|
||||
if (isDowngradingToFree)
|
||||
{
|
||||
if (model.Seats.HasValue && model.Seats.Value > freePlan.PasswordManager.MaxSeats)
|
||||
{
|
||||
TempData["Error"] = $"Organizations with more than {freePlan.PasswordManager.MaxSeats} seats cannot be downgraded to the Free plan";
|
||||
return RedirectToAction("Edit", new { id });
|
||||
}
|
||||
|
||||
if (model.MaxCollections > freePlan.PasswordManager.MaxCollections)
|
||||
{
|
||||
TempData["Error"] = $"Organizations with more than {freePlan.PasswordManager.MaxCollections} collections cannot be downgraded to the Free plan. Your organization currently has {organization.MaxCollections} collections.";
|
||||
return RedirectToAction("Edit", new { id });
|
||||
}
|
||||
|
||||
model.MaxStorageGb = null;
|
||||
model.ExpirationDate = null;
|
||||
model.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateOrganization(organization, model);
|
||||
|
||||
var plan = await _pricingClient.GetPlanOrThrow(organization.PlanType);
|
||||
|
||||
if (organization.UseSecretsManager && !plan.SupportsSecretsManager)
|
||||
{
|
||||
TempData["Error"] = "Plan does not support Secrets Manager";
|
||||
@ -272,11 +281,6 @@ public class OrganizationsController : Controller
|
||||
await _organizationRepository.ReplaceAsync(organization);
|
||||
|
||||
await _applicationCacheService.UpsertOrganizationAbilityAsync(organization);
|
||||
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.OrganizationEditedByAdmin, organization, _currentContext)
|
||||
{
|
||||
EventRaisedByUser = _userService.GetUserName(User),
|
||||
SalesAssistedTrialStarted = model.SalesAssistedTrialStarted,
|
||||
});
|
||||
|
||||
return RedirectToAction("Edit", new { id });
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ public class OrganizationViewModel
|
||||
orgUsers
|
||||
.Where(u => u.Type == OrganizationUserType.Admin && u.Status == organizationUserStatus)
|
||||
.Select(u => u.Email));
|
||||
OwnersDetails = orgUsers.Where(u => u.Type == OrganizationUserType.Owner && u.Status == organizationUserStatus);
|
||||
AdminsDetails = orgUsers.Where(u => u.Type == OrganizationUserType.Admin && u.Status == organizationUserStatus);
|
||||
SecretsCount = secretsCount;
|
||||
ProjectsCount = projectCount;
|
||||
ServiceAccountsCount = serviceAccountsCount;
|
||||
@ -70,4 +72,6 @@ public class OrganizationViewModel
|
||||
public int OccupiedSmSeatsCount { get; set; }
|
||||
public bool UseSecretsManager => Organization.UseSecretsManager;
|
||||
public bool UseRiskInsights => Organization.UseRiskInsights;
|
||||
public IEnumerable<OrganizationUserUserDetails> OwnersDetails { get; set; }
|
||||
public IEnumerable<OrganizationUserUserDetails> AdminsDetails { get; set; }
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ public class ProviderViewModel
|
||||
{
|
||||
Provider = provider;
|
||||
UserCount = providerUsers.Count();
|
||||
ProviderAdmins = providerUsers.Where(u => u.Type == ProviderUserType.ProviderAdmin);
|
||||
ProviderUsers = providerUsers;
|
||||
ProviderOrganizations = organizations.Where(o => o.ProviderId == provider.Id);
|
||||
|
||||
if (Provider.Type == ProviderType.Msp)
|
||||
@ -61,7 +61,7 @@ public class ProviderViewModel
|
||||
|
||||
public int UserCount { get; set; }
|
||||
public Provider Provider { get; set; }
|
||||
public IEnumerable<ProviderUserUserDetails> ProviderAdmins { get; set; }
|
||||
public IEnumerable<ProviderUserUserDetails> ProviderUsers { get; set; }
|
||||
public IEnumerable<ProviderOrganizationOrganizationDetails> ProviderOrganizations { get; set; }
|
||||
public List<ProviderPlanViewModel> ProviderPlanViewModels { get; set; } = [];
|
||||
}
|
||||
|
@ -1,13 +1,9 @@
|
||||
@using Bit.Admin.Enums;
|
||||
@using Bit.Admin.Models
|
||||
@using Bit.Core
|
||||
@using Bit.Core.AdminConsole.Enums.Provider
|
||||
@using Bit.Core.Billing.Enums
|
||||
@using Bit.Core.Billing.Extensions
|
||||
@using Bit.Core.Services
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@inject Bit.Admin.Services.IAccessControlService AccessControlService
|
||||
@inject IFeatureService FeatureService
|
||||
@model OrganizationEditModel
|
||||
@{
|
||||
ViewData["Title"] = (Model.Provider != null ? "Client " : string.Empty) + "Organization: " + Model.Name;
|
||||
@ -19,12 +15,10 @@
|
||||
var canDelete = AccessControlService.UserHasPermission(Permission.Org_Delete);
|
||||
var canUnlinkFromProvider = AccessControlService.UserHasPermission(Permission.Provider_Edit);
|
||||
|
||||
var canConvertToBusinessUnit =
|
||||
FeatureService.IsEnabled(FeatureFlagKeys.PM18770_EnableOrganizationBusinessUnitConversion) &&
|
||||
AccessControlService.UserHasPermission(Permission.Org_Billing_ConvertToBusinessUnit) &&
|
||||
Model.Organization.PlanType.GetProductTier() == ProductTierType.Enterprise &&
|
||||
!string.IsNullOrEmpty(Model.Organization.GatewaySubscriptionId) &&
|
||||
Model.Provider is null or { Type: ProviderType.BusinessUnit, Status: ProviderStatusType.Pending };
|
||||
var canConvertToBusinessUnit = AccessControlService.UserHasPermission(Permission.Org_Billing_ConvertToBusinessUnit) &&
|
||||
Model.Organization.PlanType.GetProductTier() == ProductTierType.Enterprise &&
|
||||
!string.IsNullOrEmpty(Model.Organization.GatewaySubscriptionId) &&
|
||||
Model.Provider is null or { Type: ProviderType.BusinessUnit, Status: ProviderStatusType.Pending };
|
||||
}
|
||||
|
||||
@section Scripts {
|
||||
|
@ -19,12 +19,6 @@
|
||||
<span id="org-confirmed-users" title="Confirmed">@Model.UserConfirmedCount</span>)
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-4 col-lg-3">Owners</dt>
|
||||
<dd id="org-owner" class="col-sm-8 col-lg-9">@(string.IsNullOrWhiteSpace(Model.Owners) ? "None" : Model.Owners)</dd>
|
||||
|
||||
<dt class="col-sm-4 col-lg-3">Admins</dt>
|
||||
<dd id="org-admins" class="col-sm-8 col-lg-9">@(string.IsNullOrWhiteSpace(Model.Admins) ? "None" : Model.Admins)</dd>
|
||||
|
||||
<dt class="col-sm-4 col-lg-3">Using 2FA</dt>
|
||||
<dd id="org-2fa" class="col-sm-8 col-lg-9">@(Model.Organization.TwoFactorIsEnabled() ? "Yes" : "No")</dd>
|
||||
|
||||
@ -76,3 +70,49 @@
|
||||
<dt class="col-sm-4 col-lg-3">Secrets Manager Seats</dt>
|
||||
<dd id="sm-seat-count" class="col-sm-8 col-lg-9">@(Model.UseSecretsManager ? Model.OccupiedSmSeatsCount: "N/A" )</dd>
|
||||
</dl>
|
||||
|
||||
<h2>Administrators</h2>
|
||||
<dl class="row">
|
||||
<div class="table-responsive">
|
||||
<div class="col-8">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 190px;">Email</th>
|
||||
<th style="width: 60px;">Role</th>
|
||||
<th style="width: 40px;">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if(!Model.Admins.Any() && !Model.Owners.Any())
|
||||
{
|
||||
<tr>
|
||||
<td colspan="6">No results to list.</td>
|
||||
</tr>
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach(var owner in Model.OwnersDetails)
|
||||
{
|
||||
<tr>
|
||||
<td class="align-middle">@owner.Email</td>
|
||||
<td class="align-middle">Owner</td>
|
||||
<td class="align-middle">@owner.Status</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
@foreach(var admin in Model.AdminsDetails)
|
||||
{
|
||||
<tr>
|
||||
<td class="align-middle">@admin.Email</td>
|
||||
<td class="align-middle">Admin</td>
|
||||
<td class="align-middle">@admin.Status</td>
|
||||
</tr>
|
||||
|
||||
}
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</dl>
|
||||
|
@ -7,7 +7,7 @@
|
||||
var canResendEmailInvite = AccessControlService.UserHasPermission(Permission.Provider_ResendEmailInvite);
|
||||
}
|
||||
|
||||
<h2>Provider Admins</h2>
|
||||
<h2>Administrators</h2>
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<div class="table-responsive">
|
||||
@ -15,12 +15,13 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 190px;">Email</th>
|
||||
<th style="width: 160px;">Role</th>
|
||||
<th style="width: 40px;">Status</th>
|
||||
<th style="width: 30px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if(!Model.ProviderAdmins.Any())
|
||||
@if(!Model.ProviderUsers.Any())
|
||||
{
|
||||
<tr>
|
||||
<td colspan="6">No results to list.</td>
|
||||
@ -28,29 +29,39 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach(var admin in Model.ProviderAdmins)
|
||||
@foreach(var user in Model.ProviderUsers)
|
||||
{
|
||||
<tr>
|
||||
<td class="align-middle">
|
||||
@admin.Email
|
||||
@user.Email
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
@admin.Status
|
||||
@if(@user.Type == 0)
|
||||
{
|
||||
<span>Provider Admin</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>Service User</span>
|
||||
}
|
||||
</td>
|
||||
<td class="align-middle">
|
||||
@user.Status
|
||||
</td>
|
||||
<td>
|
||||
@if(admin.Status.Equals(ProviderUserStatusType.Confirmed)
|
||||
@if(user.Status.Equals(ProviderUserStatusType.Confirmed)
|
||||
&& @Model.Provider.Status.Equals(ProviderStatusType.Pending)
|
||||
&& canResendEmailInvite)
|
||||
{
|
||||
@if(@TempData["InviteResentTo"] != null && @TempData["InviteResentTo"].ToString() == @admin.UserId.Value.ToString())
|
||||
@if(@TempData["InviteResentTo"] != null && @TempData["InviteResentTo"].ToString() == @user.UserId.Value.ToString())
|
||||
{
|
||||
<button class="btn btn-outline-success btn-sm disabled" disabled>Invite Resent!</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a class="btn btn-outline-secondary btn-sm"
|
||||
data-id="@admin.Id" asp-controller="Providers"
|
||||
asp-action="ResendInvite" asp-route-ownerId="@admin.UserId"
|
||||
data-id="@user.Id" asp-controller="Providers"
|
||||
asp-action="ResendInvite" asp-route-ownerId="@user.UserId"
|
||||
asp-route-providerId="@Model.Provider.Id">
|
||||
Resend Setup Invite
|
||||
</a>
|
||||
|
@ -2,7 +2,6 @@
|
||||
using Bit.Admin.Billing.Models;
|
||||
using Bit.Admin.Enums;
|
||||
using Bit.Admin.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Entities.Provider;
|
||||
using Bit.Core.AdminConsole.Enums.Provider;
|
||||
@ -18,7 +17,6 @@ namespace Bit.Admin.Billing.Controllers;
|
||||
|
||||
[Authorize]
|
||||
[Route("organizations/billing/{organizationId:guid}/business-unit")]
|
||||
[RequireFeature(FeatureFlagKeys.PM18770_EnableOrganizationBusinessUnitConversion)]
|
||||
public class BusinessUnitConversionController(
|
||||
IBusinessUnitConverter businessUnitConverter,
|
||||
IOrganizationRepository organizationRepository,
|
||||
|
@ -1,21 +1,71 @@
|
||||
###############################################
|
||||
# Build stage #
|
||||
###############################################
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
|
||||
# Docker buildx supplies the value for this arg
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
# Determine proper runtime value for .NET
|
||||
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
|
||||
|
||||
# Set up Node
|
||||
ARG NODE_VERSION=20
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y nodejs \
|
||||
&& npm install -g npm@latest && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Copy required project files
|
||||
WORKDIR /source
|
||||
COPY . ./
|
||||
|
||||
# Restore project dependencies and tools
|
||||
WORKDIR /source/src/Admin
|
||||
RUN npm ci
|
||||
RUN . /tmp/rid.txt && dotnet restore -r $RID
|
||||
|
||||
# Build project
|
||||
RUN npm run build
|
||||
RUN . /tmp/rid.txt && dotnet publish \
|
||||
-c release \
|
||||
--no-restore \
|
||||
--self-contained \
|
||||
/p:PublishSingleFile=true \
|
||||
-r $RID \
|
||||
-o out
|
||||
|
||||
###############################################
|
||||
# App stage #
|
||||
###############################################
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
LABEL com.bitwarden.product="bitwarden"
|
||||
ENV ASPNETCORE_ENVIRONMENT=Production
|
||||
ENV ASPNETCORE_URLS=http://+:5000
|
||||
ENV SSL_CERT_DIR=/etc/bitwarden/ca-certificates
|
||||
EXPOSE 5000
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
gosu \
|
||||
curl \
|
||||
krb5-user \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV ASPNETCORE_URLS http://+:5000
|
||||
# Copy app from the build stage
|
||||
WORKDIR /app
|
||||
EXPOSE 5000
|
||||
COPY obj/build-output/publish .
|
||||
COPY entrypoint.sh /
|
||||
COPY --from=build /source/src/Admin/out /app
|
||||
COPY ./src/Admin/entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
HEALTHCHECK CMD curl -f http://localhost:5000 || exit 1
|
||||
HEALTHCHECK CMD curl -f http://localhost:5000/alive || exit 1
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
@ -39,7 +39,7 @@ public class ReadOnlyEnvIdentityUserStore : ReadOnlyIdentityUserStore
|
||||
}
|
||||
}
|
||||
|
||||
var userStamp = usersDict.ContainsKey(normalizedEmail) ? usersDict[normalizedEmail] : null;
|
||||
var userStamp = usersDict.GetValueOrDefault(normalizedEmail);
|
||||
if (userStamp == null)
|
||||
{
|
||||
return Task.FromResult<IdentityUser>(null);
|
||||
|
@ -20,8 +20,8 @@ public class Program
|
||||
logging.AddSerilog(hostingContext, (e, globalSettings) =>
|
||||
{
|
||||
var context = e.Properties["SourceContext"].ToString();
|
||||
if (e.Properties.ContainsKey("RequestPath") &&
|
||||
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
|
||||
if (e.Properties.TryGetValue("RequestPath", out var requestPath) &&
|
||||
!string.IsNullOrWhiteSpace(requestPath?.ToString()) &&
|
||||
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
||||
{
|
||||
return false;
|
||||
|
@ -29,12 +29,12 @@ public class AccessControlService : IAccessControlService
|
||||
}
|
||||
|
||||
var userRole = GetUserRoleFromClaim();
|
||||
if (string.IsNullOrEmpty(userRole) || !RolePermissionMapping.RolePermissions.ContainsKey(userRole))
|
||||
if (string.IsNullOrEmpty(userRole) || !RolePermissionMapping.RolePermissions.TryGetValue(userRole, out var rolePermissions))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return RolePermissionMapping.RolePermissions[userRole].Contains(permission);
|
||||
return rolePermissions.Contains(permission);
|
||||
}
|
||||
|
||||
public string GetUserRole(string userEmail)
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Setup
|
||||
|
||||
@ -19,31 +19,36 @@ then
|
||||
LGID=65534
|
||||
fi
|
||||
|
||||
# Create user and group
|
||||
if [ "$(id -u)" = "0" ]
|
||||
then
|
||||
# Create user and group
|
||||
|
||||
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
||||
groupmod -o -g $LGID $GROUPNAME >/dev/null 2>&1
|
||||
useradd -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1 ||
|
||||
usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1
|
||||
mkhomedir_helper $USERNAME
|
||||
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
||||
groupmod -o -g $LGID $GROUPNAME >/dev/null 2>&1
|
||||
useradd -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1 ||
|
||||
usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1
|
||||
mkhomedir_helper $USERNAME
|
||||
|
||||
# The rest...
|
||||
# The rest...
|
||||
|
||||
chown -R $USERNAME:$GROUPNAME /app
|
||||
mkdir -p /etc/bitwarden/core
|
||||
mkdir -p /etc/bitwarden/logs
|
||||
mkdir -p /etc/bitwarden/ca-certificates
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||
chown -R $USERNAME:$GROUPNAME /app
|
||||
mkdir -p /etc/bitwarden/core
|
||||
mkdir -p /etc/bitwarden/logs
|
||||
mkdir -p /etc/bitwarden/ca-certificates
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||
|
||||
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
||||
&& update-ca-certificates
|
||||
if [[ -f "/etc/bitwarden/kerberos/bitwarden.keytab" && -f "/etc/bitwarden/kerberos/krb5.conf" ]]; then
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden/kerberos
|
||||
fi
|
||||
|
||||
gosu_cmd="gosu $USERNAME:$GROUPNAME"
|
||||
else
|
||||
gosu_cmd=""
|
||||
fi
|
||||
|
||||
if [[ -f "/etc/bitwarden/kerberos/bitwarden.keytab" && -f "/etc/bitwarden/kerberos/krb5.conf" ]]; then
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden/kerberos
|
||||
cp -f /etc/bitwarden/kerberos/krb5.conf /etc/krb5.conf
|
||||
gosu $USERNAME:$GROUPNAME kinit $globalSettings__kerberosUser -k -t /etc/bitwarden/kerberos/bitwarden.keytab
|
||||
cp -f /etc/bitwarden/kerberos/krb5.conf /etc/krb5.conf
|
||||
$gosu_cmd kinit $globalSettings__kerberosUser -k -t /etc/bitwarden/kerberos/bitwarden.keytab
|
||||
fi
|
||||
|
||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/Admin.dll
|
||||
exec $gosu_cmd /app/Admin
|
||||
|
199
src/Admin/package-lock.json
generated
199
src/Admin/package-lock.json
generated
@ -9,7 +9,7 @@
|
||||
"version": "0.0.0",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"bootstrap": "5.3.3",
|
||||
"bootstrap": "5.3.6",
|
||||
"font-awesome": "4.7.0",
|
||||
"jquery": "3.7.1",
|
||||
"toastr": "2.1.4"
|
||||
@ -18,9 +18,9 @@
|
||||
"css-loader": "7.1.2",
|
||||
"expose-loader": "5.0.1",
|
||||
"mini-css-extract-plugin": "2.9.2",
|
||||
"sass": "1.85.0",
|
||||
"sass-loader": "16.0.4",
|
||||
"webpack": "5.97.1",
|
||||
"sass": "1.88.0",
|
||||
"sass-loader": "16.0.5",
|
||||
"webpack": "5.99.8",
|
||||
"webpack-cli": "5.1.4"
|
||||
}
|
||||
},
|
||||
@ -456,13 +456,13 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.13.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz",
|
||||
"integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==",
|
||||
"version": "22.15.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
|
||||
"integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@webassemblyjs/ast": {
|
||||
@ -749,9 +749,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/bootstrap": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
|
||||
"integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
|
||||
"version": "5.3.6",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.6.tgz",
|
||||
"integrity": "sha512-jX0GAcRzvdwISuvArXn3m7KZscWWFAf1MKBcnzaN02qWMb3jpMoUX4/qgeiGzqyIb4ojulRzs89UCUmGcFSzTA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@ -782,9 +782,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.24.4",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
|
||||
"integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
|
||||
"version": "4.24.5",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz",
|
||||
"integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -802,10 +802,10 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001688",
|
||||
"electron-to-chromium": "^1.5.73",
|
||||
"caniuse-lite": "^1.0.30001716",
|
||||
"electron-to-chromium": "^1.5.149",
|
||||
"node-releases": "^2.0.19",
|
||||
"update-browserslist-db": "^1.1.1"
|
||||
"update-browserslist-db": "^1.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
@ -822,9 +822,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001707",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz",
|
||||
"integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==",
|
||||
"version": "1.0.30001718",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
|
||||
"integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@ -976,9 +976,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.128",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.128.tgz",
|
||||
"integrity": "sha512-bo1A4HH/NS522Ws0QNFIzyPcyUUNV/yyy70Ho1xqfGYzPUme2F/xr4tlEOuM6/A538U1vDA7a4XfCd1CKRegKQ==",
|
||||
"version": "1.5.155",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz",
|
||||
"integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@ -1010,9 +1010,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
|
||||
"integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==",
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
|
||||
"integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@ -1107,13 +1107,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
|
||||
@ -1249,9 +1242,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/immutable": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.1.tgz",
|
||||
"integrity": "sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.2.tgz",
|
||||
"integrity": "sha512-qHKXW1q6liAk1Oys6umoaZbDRqjcjgSrbnrifHsfsttza7zcvRAsL7mMV6xWcyhwQy7Xj5v4hhbr6b+iDYwlmQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@ -1755,16 +1748,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
@ -1878,9 +1861,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.85.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.85.0.tgz",
|
||||
"integrity": "sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww==",
|
||||
"version": "1.88.0",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.88.0.tgz",
|
||||
"integrity": "sha512-sF6TWQqjFvr4JILXzG4ucGOLELkESHL+I5QJhh7CNaE+Yge0SI+ehCatsXhJ7ymU1hAFcIS3/PBpjdIbXoyVbg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -1899,9 +1882,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sass-loader": {
|
||||
"version": "16.0.4",
|
||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.4.tgz",
|
||||
"integrity": "sha512-LavLbgbBGUt3wCiYzhuLLu65+fWXaXLmq7YxivLhEqmiupCFZ5sKUAipK3do6V80YSU0jvSxNhEdT13IXNr3rg==",
|
||||
"version": "16.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.5.tgz",
|
||||
"integrity": "sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -1940,9 +1923,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/schema-utils": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
|
||||
"integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
|
||||
"integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -1960,9 +1943,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
@ -2079,9 +2062,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
|
||||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz",
|
||||
"integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@ -2089,14 +2072,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.39.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz",
|
||||
"integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==",
|
||||
"version": "5.39.2",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.39.2.tgz",
|
||||
"integrity": "sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.8.2",
|
||||
"acorn": "^8.14.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
@ -2165,9 +2148,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@ -2202,16 +2185,6 @@
|
||||
"browserslist": ">= 4.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
@ -2220,9 +2193,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
|
||||
"integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz",
|
||||
"integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -2234,14 +2207,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.97.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz",
|
||||
"integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==",
|
||||
"version": "5.99.8",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.8.tgz",
|
||||
"integrity": "sha512-lQ3CPiSTpfOnrEGeXDwoq5hIGzSjmwD72GdfVzF7CQAI7t47rJG9eDWvcEkEn3CUQymAElVvDg3YNTlCYj+qUQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.6",
|
||||
"@types/json-schema": "^7.0.15",
|
||||
"@webassemblyjs/ast": "^1.14.1",
|
||||
"@webassemblyjs/wasm-edit": "^1.14.1",
|
||||
"@webassemblyjs/wasm-parser": "^1.14.1",
|
||||
@ -2258,9 +2232,9 @@
|
||||
"loader-runner": "^4.2.0",
|
||||
"mime-types": "^2.1.27",
|
||||
"neo-async": "^2.6.2",
|
||||
"schema-utils": "^3.2.0",
|
||||
"schema-utils": "^4.3.2",
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"terser-webpack-plugin": "^5.3.11",
|
||||
"watchpack": "^2.4.1",
|
||||
"webpack-sources": "^3.2.3"
|
||||
},
|
||||
@ -2361,59 +2335,6 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/ajv-keywords": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"ajv": "^6.9.1"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack/node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/webpack/node_modules/schema-utils": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz",
|
||||
"integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.8",
|
||||
"ajv": "^6.12.5",
|
||||
"ajv-keywords": "^3.5.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
@ -8,7 +8,7 @@
|
||||
"build": "webpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "5.3.3",
|
||||
"bootstrap": "5.3.6",
|
||||
"font-awesome": "4.7.0",
|
||||
"jquery": "3.7.1",
|
||||
"toastr": "2.1.4"
|
||||
@ -17,9 +17,9 @@
|
||||
"css-loader": "7.1.2",
|
||||
"expose-loader": "5.0.1",
|
||||
"mini-css-extract-plugin": "2.9.2",
|
||||
"sass": "1.85.0",
|
||||
"sass-loader": "16.0.4",
|
||||
"webpack": "5.97.1",
|
||||
"sass": "1.88.0",
|
||||
"sass-loader": "16.0.5",
|
||||
"webpack": "5.99.8",
|
||||
"webpack-cli": "5.1.4"
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +0,0 @@
|
||||
*
|
||||
!obj/build-output/publish/*
|
||||
!obj/Docker/empty/
|
||||
!entrypoint.sh
|
@ -2,10 +2,10 @@
|
||||
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
||||
using Bit.Core;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Models.Data.Integrations;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data.Integrations;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
|
@ -1,8 +1,8 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Models.Data.Integrations;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data.Integrations;
|
||||
|
||||
#nullable enable
|
||||
|
||||
|
@ -34,7 +34,7 @@
|
||||
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="8.0.2" />
|
||||
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="8.0.1" />
|
||||
<PackageReference Include="Azure.Messaging.EventGrid" Version="4.25.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.3.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -25,7 +25,7 @@ public class UpdateTwoFactorAuthenticatorRequestModel : SecretVerificationReques
|
||||
{
|
||||
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
||||
}
|
||||
else if (providers.ContainsKey(TwoFactorProviderType.Authenticator))
|
||||
else
|
||||
{
|
||||
providers.Remove(TwoFactorProviderType.Authenticator);
|
||||
}
|
||||
@ -62,7 +62,7 @@ public class UpdateTwoFactorDuoRequestModel : SecretVerificationRequestModel, IV
|
||||
{
|
||||
providers = [];
|
||||
}
|
||||
else if (providers.ContainsKey(TwoFactorProviderType.Duo))
|
||||
else
|
||||
{
|
||||
providers.Remove(TwoFactorProviderType.Duo);
|
||||
}
|
||||
@ -88,7 +88,7 @@ public class UpdateTwoFactorDuoRequestModel : SecretVerificationRequestModel, IV
|
||||
{
|
||||
providers = [];
|
||||
}
|
||||
else if (providers.ContainsKey(TwoFactorProviderType.OrganizationDuo))
|
||||
else
|
||||
{
|
||||
providers.Remove(TwoFactorProviderType.OrganizationDuo);
|
||||
}
|
||||
@ -145,7 +145,7 @@ public class UpdateTwoFactorYubicoOtpRequestModel : SecretVerificationRequestMod
|
||||
{
|
||||
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
||||
}
|
||||
else if (providers.ContainsKey(TwoFactorProviderType.YubiKey))
|
||||
else
|
||||
{
|
||||
providers.Remove(TwoFactorProviderType.YubiKey);
|
||||
}
|
||||
@ -228,7 +228,7 @@ public class TwoFactorEmailRequestModel : SecretVerificationRequestModel
|
||||
{
|
||||
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
||||
}
|
||||
else if (providers.ContainsKey(TwoFactorProviderType.Email))
|
||||
else
|
||||
{
|
||||
providers.Remove(TwoFactorProviderType.Email);
|
||||
}
|
||||
|
@ -90,6 +90,13 @@ public class EmergencyAccessGrantorDetailsResponseModel : EmergencyAccessRespons
|
||||
|
||||
public class EmergencyAccessTakeoverResponseModel : ResponseModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="EmergencyAccessTakeoverResponseModel"/> class.
|
||||
/// </summary>
|
||||
/// <param name="emergencyAccess">Consumed for the Encrypted Key value</param>
|
||||
/// <param name="grantor">consumed for the KDF configuration</param>
|
||||
/// <param name="obj">name of the object</param>
|
||||
/// <exception cref="ArgumentNullException">emergencyAccess cannot be null</exception>
|
||||
public EmergencyAccessTakeoverResponseModel(EmergencyAccess emergencyAccess, User grantor, string obj = "emergencyAccessTakeover") : base(obj)
|
||||
{
|
||||
if (emergencyAccess == null)
|
||||
|
@ -13,9 +13,9 @@ public class TwoFactorAuthenticatorResponseModel : ResponseModel
|
||||
ArgumentNullException.ThrowIfNull(user);
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Authenticator);
|
||||
if (provider?.MetaData?.ContainsKey("Key") ?? false)
|
||||
if (provider?.MetaData?.TryGetValue("Key", out var keyValue) ?? false)
|
||||
{
|
||||
Key = (string)provider.MetaData["Key"];
|
||||
Key = (string)keyValue;
|
||||
Enabled = provider.Enabled;
|
||||
}
|
||||
else
|
||||
|
@ -15,9 +15,9 @@ public class TwoFactorEmailResponseModel : ResponseModel
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
|
||||
if (provider?.MetaData?.ContainsKey("Email") ?? false)
|
||||
if (provider?.MetaData?.TryGetValue("Email", out var email) ?? false)
|
||||
{
|
||||
Email = (string)provider.MetaData["Email"];
|
||||
Email = (string)email;
|
||||
Enabled = provider.Enabled;
|
||||
}
|
||||
else
|
||||
|
@ -19,29 +19,29 @@ public class TwoFactorYubiKeyResponseModel : ResponseModel
|
||||
{
|
||||
Enabled = provider.Enabled;
|
||||
|
||||
if (provider.MetaData.ContainsKey("Key1"))
|
||||
if (provider.MetaData.TryGetValue("Key1", out var key1))
|
||||
{
|
||||
Key1 = (string)provider.MetaData["Key1"];
|
||||
Key1 = (string)key1;
|
||||
}
|
||||
if (provider.MetaData.ContainsKey("Key2"))
|
||||
if (provider.MetaData.TryGetValue("Key2", out var key2))
|
||||
{
|
||||
Key2 = (string)provider.MetaData["Key2"];
|
||||
Key2 = (string)key2;
|
||||
}
|
||||
if (provider.MetaData.ContainsKey("Key3"))
|
||||
if (provider.MetaData.TryGetValue("Key3", out var key3))
|
||||
{
|
||||
Key3 = (string)provider.MetaData["Key3"];
|
||||
Key3 = (string)key3;
|
||||
}
|
||||
if (provider.MetaData.ContainsKey("Key4"))
|
||||
if (provider.MetaData.TryGetValue("Key4", out var key4))
|
||||
{
|
||||
Key4 = (string)provider.MetaData["Key4"];
|
||||
Key4 = (string)key4;
|
||||
}
|
||||
if (provider.MetaData.ContainsKey("Key5"))
|
||||
if (provider.MetaData.TryGetValue("Key5", out var key5))
|
||||
{
|
||||
Key5 = (string)provider.MetaData["Key5"];
|
||||
Key5 = (string)key5;
|
||||
}
|
||||
if (provider.MetaData.ContainsKey("Nfc"))
|
||||
if (provider.MetaData.TryGetValue("Nfc", out var nfc))
|
||||
{
|
||||
Nfc = (bool)provider.MetaData["Nfc"];
|
||||
Nfc = (bool)nfc;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -6,14 +6,10 @@ using Bit.Api.Utilities;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
using Bit.Core.Billing.Models;
|
||||
using Bit.Core.Billing.Services;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tools.Enums;
|
||||
using Bit.Core.Tools.Models.Business;
|
||||
using Bit.Core.Tools.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@ -161,8 +157,6 @@ public class AccountsController(
|
||||
[HttpPost("cancel")]
|
||||
public async Task PostCancelAsync(
|
||||
[FromBody] SubscriptionCancellationRequestModel request,
|
||||
[FromServices] ICurrentContext currentContext,
|
||||
[FromServices] IReferenceEventService referenceEventService,
|
||||
[FromServices] ISubscriberService subscriberService)
|
||||
{
|
||||
var user = await userService.GetUserByPrincipalAsync(User);
|
||||
@ -175,12 +169,6 @@ public class AccountsController(
|
||||
await subscriberService.CancelSubscription(user,
|
||||
new OffboardingSurveyResponse { UserId = user.Id, Reason = request.Reason, Feedback = request.Feedback },
|
||||
user.IsExpired());
|
||||
|
||||
await referenceEventService.RaiseEventAsync(new ReferenceEvent(
|
||||
ReferenceEventType.CancelSubscription,
|
||||
user,
|
||||
currentContext)
|
||||
{ EndOfPeriod = user.IsExpired() });
|
||||
}
|
||||
|
||||
[HttpPost("reinstate-premium")]
|
||||
|
@ -4,7 +4,7 @@ using Bit.Api.AdminConsole.Models.Request.Organizations;
|
||||
using Bit.Api.Billing.Models.Requests;
|
||||
using Bit.Api.Billing.Models.Responses;
|
||||
using Bit.Api.Billing.Queries.Organizations;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Billing.Models;
|
||||
using Bit.Core.Billing.Models.Sales;
|
||||
using Bit.Core.Billing.Pricing;
|
||||
@ -25,7 +25,6 @@ namespace Bit.Api.Billing.Controllers;
|
||||
public class OrganizationBillingController(
|
||||
IBusinessUnitConverter businessUnitConverter,
|
||||
ICurrentContext currentContext,
|
||||
IFeatureService featureService,
|
||||
IOrganizationBillingService organizationBillingService,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IOrganizationWarningsQuery organizationWarningsQuery,
|
||||
@ -282,17 +281,36 @@ public class OrganizationBillingController(
|
||||
}
|
||||
|
||||
var organization = await organizationRepository.GetByIdAsync(organizationId);
|
||||
|
||||
if (organization == null)
|
||||
{
|
||||
return Error.NotFound();
|
||||
}
|
||||
var existingPlan = organization.PlanType;
|
||||
var organizationSignup = model.ToOrganizationSignup(user);
|
||||
var sale = OrganizationSale.From(organization, organizationSignup);
|
||||
var plan = await pricingClient.GetPlanOrThrow(model.PlanType);
|
||||
sale.Organization.PlanType = plan.Type;
|
||||
sale.Organization.Plan = plan.Name;
|
||||
sale.SubscriptionSetup.SkipTrial = true;
|
||||
if (existingPlan == PlanType.Free && organization.GatewaySubscriptionId is not null)
|
||||
{
|
||||
sale.Organization.UseTotp = plan.HasTotp;
|
||||
sale.Organization.UseGroups = plan.HasGroups;
|
||||
sale.Organization.UseDirectory = plan.HasDirectory;
|
||||
sale.Organization.SelfHost = plan.HasSelfHost;
|
||||
sale.Organization.UsersGetPremium = plan.UsersGetPremium;
|
||||
sale.Organization.UseEvents = plan.HasEvents;
|
||||
sale.Organization.Use2fa = plan.Has2fa;
|
||||
sale.Organization.UseApi = plan.HasApi;
|
||||
sale.Organization.UsePolicies = plan.HasPolicies;
|
||||
sale.Organization.UseSso = plan.HasSso;
|
||||
sale.Organization.UseResetPassword = plan.HasResetPassword;
|
||||
sale.Organization.UseKeyConnector = plan.HasKeyConnector;
|
||||
sale.Organization.UseScim = plan.HasScim;
|
||||
sale.Organization.UseCustomPermissions = plan.HasCustomPermissions;
|
||||
sale.Organization.UseOrganizationDomains = plan.HasOrganizationDomains;
|
||||
sale.Organization.MaxCollections = plan.PasswordManager.MaxCollections;
|
||||
}
|
||||
|
||||
if (organizationSignup.PaymentMethodType == null || string.IsNullOrEmpty(organizationSignup.PaymentToken))
|
||||
{
|
||||
@ -318,14 +336,6 @@ public class OrganizationBillingController(
|
||||
[FromRoute] Guid organizationId,
|
||||
[FromBody] SetupBusinessUnitRequestBody requestBody)
|
||||
{
|
||||
var enableOrganizationBusinessUnitConversion =
|
||||
featureService.IsEnabled(FeatureFlagKeys.PM18770_EnableOrganizationBusinessUnitConversion);
|
||||
|
||||
if (!enableOrganizationBusinessUnitConversion)
|
||||
{
|
||||
return Error.NotFound();
|
||||
}
|
||||
|
||||
var organization = await organizationRepository.GetByIdAsync(organizationId);
|
||||
|
||||
if (organization == null)
|
||||
|
@ -20,9 +20,6 @@ using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tools.Enums;
|
||||
using Bit.Core.Tools.Models.Business;
|
||||
using Bit.Core.Tools.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@ -44,7 +41,6 @@ public class OrganizationsController(
|
||||
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
|
||||
IUpgradeOrganizationPlanCommand upgradeOrganizationPlanCommand,
|
||||
IAddSecretsManagerSubscriptionCommand addSecretsManagerSubscriptionCommand,
|
||||
IReferenceEventService referenceEventService,
|
||||
ISubscriberService subscriberService,
|
||||
IOrganizationInstallationRepository organizationInstallationRepository,
|
||||
IPricingClient pricingClient)
|
||||
@ -246,14 +242,6 @@ public class OrganizationsController(
|
||||
Feedback = request.Feedback
|
||||
},
|
||||
organization.IsExpired());
|
||||
|
||||
await referenceEventService.RaiseEventAsync(new ReferenceEvent(
|
||||
ReferenceEventType.CancelSubscription,
|
||||
organization,
|
||||
currentContext)
|
||||
{
|
||||
EndOfPeriod = organization.IsExpired()
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("{id:guid}/reinstate")]
|
||||
|
@ -81,13 +81,6 @@ public class ProviderBillingController(
|
||||
[FromRoute] Guid providerId,
|
||||
[FromBody] UpdatePaymentMethodRequestBody requestBody)
|
||||
{
|
||||
var allowProviderPaymentMethod = featureService.IsEnabled(FeatureFlagKeys.PM18794_ProviderPaymentMethod);
|
||||
|
||||
if (!allowProviderPaymentMethod)
|
||||
{
|
||||
return TypedResults.NotFound();
|
||||
}
|
||||
|
||||
var (provider, result) = await TryGetBillableProviderForAdminOperation(providerId);
|
||||
|
||||
if (provider == null)
|
||||
@ -111,13 +104,6 @@ public class ProviderBillingController(
|
||||
[FromRoute] Guid providerId,
|
||||
[FromBody] VerifyBankAccountRequestBody requestBody)
|
||||
{
|
||||
var allowProviderPaymentMethod = featureService.IsEnabled(FeatureFlagKeys.PM18794_ProviderPaymentMethod);
|
||||
|
||||
if (!allowProviderPaymentMethod)
|
||||
{
|
||||
return TypedResults.NotFound();
|
||||
}
|
||||
|
||||
var (provider, result) = await TryGetBillableProviderForAdminOperation(providerId);
|
||||
|
||||
if (provider == null)
|
||||
|
@ -12,7 +12,8 @@ public record OrganizationMetadataResponse(
|
||||
bool IsSubscriptionCanceled,
|
||||
DateTime? InvoiceDueDate,
|
||||
DateTime? InvoiceCreatedDate,
|
||||
DateTime? SubPeriodEndDate)
|
||||
DateTime? SubPeriodEndDate,
|
||||
int OrganizationOccupiedSeats)
|
||||
{
|
||||
public static OrganizationMetadataResponse From(OrganizationMetadata metadata)
|
||||
=> new(
|
||||
@ -25,5 +26,6 @@ public record OrganizationMetadataResponse(
|
||||
metadata.IsSubscriptionCanceled,
|
||||
metadata.InvoiceDueDate,
|
||||
metadata.InvoiceCreatedDate,
|
||||
metadata.SubPeriodEndDate);
|
||||
metadata.SubPeriodEndDate,
|
||||
metadata.OrganizationOccupiedSeats);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Tools.Controllers;
|
||||
namespace Bit.Api.Dirt.Controllers;
|
||||
|
||||
[Route("hibp")]
|
||||
[Authorize("Application")]
|
||||
|
@ -1,16 +1,16 @@
|
||||
using Bit.Api.Tools.Models;
|
||||
using Bit.Api.Tools.Models.Response;
|
||||
using Bit.Api.Dirt.Models;
|
||||
using Bit.Api.Dirt.Models.Response;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Dirt.Reports.Entities;
|
||||
using Bit.Core.Dirt.Reports.Models.Data;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.OrganizationReportMembers.Interfaces;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures.Requests;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Tools.ReportFeatures.Interfaces;
|
||||
using Bit.Core.Tools.ReportFeatures.OrganizationReportMembers.Interfaces;
|
||||
using Bit.Core.Tools.ReportFeatures.Requests;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Bit.Api.Tools.Controllers;
|
||||
namespace Bit.Api.Dirt.Controllers;
|
||||
|
||||
[Route("reports")]
|
||||
[Authorize("Application")]
|
||||
@ -47,7 +47,7 @@ public class ReportsController : Controller
|
||||
[HttpGet("member-cipher-details/{orgId}")]
|
||||
public async Task<IEnumerable<MemberCipherDetailsResponseModel>> GetMemberCipherDetails(Guid orgId)
|
||||
{
|
||||
// Using the AccessReports permission here until new permissions
|
||||
// Using the AccessReports permission here until new permissions
|
||||
// are needed for more control over reports
|
||||
if (!await _currentContext.AccessReports(orgId))
|
||||
{
|
||||
@ -84,7 +84,7 @@ public class ReportsController : Controller
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains the organization member info, the cipher ids associated with the member,
|
||||
/// Contains the organization member info, the cipher ids associated with the member,
|
||||
/// and details on their collections, groups, and permissions
|
||||
/// </summary>
|
||||
/// <param name="request">Request to the MemberAccessCipherDetailsQuery</param>
|
||||
|
@ -1,4 +1,4 @@
|
||||
namespace Bit.Api.Tools.Models;
|
||||
namespace Bit.Api.Dirt.Models;
|
||||
|
||||
public class PasswordHealthReportApplicationModel
|
||||
{
|
||||
|
@ -1,10 +1,10 @@
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Dirt.Reports.Models.Data;
|
||||
|
||||
namespace Bit.Api.Tools.Models.Response;
|
||||
namespace Bit.Api.Dirt.Models.Response;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the collections and group collections a user has access to including
|
||||
/// the permission level for the collection and group collection.
|
||||
/// the permission level for the collection and group collection.
|
||||
/// </summary>
|
||||
public class MemberAccessReportResponseModel
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Dirt.Reports.Models.Data;
|
||||
|
||||
namespace Bit.Api.Tools.Models.Response;
|
||||
namespace Bit.Api.Dirt.Models.Response;
|
||||
|
||||
public class MemberCipherDetailsResponseModel
|
||||
{
|
||||
|
@ -1,6 +1,50 @@
|
||||
###############################################
|
||||
# Build stage #
|
||||
###############################################
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS 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
|
||||
|
||||
# Copy required project files
|
||||
WORKDIR /source
|
||||
COPY . ./
|
||||
|
||||
# Restore project dependencies and tools
|
||||
WORKDIR /source/src/Api
|
||||
RUN . /tmp/rid.txt && dotnet restore -r $RID
|
||||
|
||||
# Build project
|
||||
RUN . /tmp/rid.txt && dotnet publish \
|
||||
-c release \
|
||||
--no-restore \
|
||||
--self-contained \
|
||||
/p:PublishSingleFile=true \
|
||||
-r $RID \
|
||||
-o out
|
||||
|
||||
###############################################
|
||||
# App stage #
|
||||
###############################################
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
LABEL com.bitwarden.product="bitwarden"
|
||||
ENV ASPNETCORE_ENVIRONMENT=Production
|
||||
ENV ASPNETCORE_URLS=http://+:5000
|
||||
ENV SSL_CERT_DIR=/etc/bitwarden/ca-certificates
|
||||
EXPOSE 5000
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
@ -9,13 +53,11 @@ RUN apt-get update \
|
||||
krb5-user \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV ASPNETCORE_URLS http://+:5000
|
||||
# Copy app from the build stage
|
||||
WORKDIR /app
|
||||
EXPOSE 5000
|
||||
COPY obj/build-output/publish .
|
||||
COPY entrypoint.sh /
|
||||
COPY --from=build /source/src/Api/out /app
|
||||
COPY ./src/Api/entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
HEALTHCHECK CMD curl -f http://localhost:5000/alive || exit 1
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Api.AdminConsole.Public.Models.Response;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Api.Models.Public.Response;
|
||||
@ -20,6 +21,7 @@ public class CollectionResponseModel : CollectionBaseModel, IResponseModel
|
||||
Id = collection.Id;
|
||||
ExternalId = collection.ExternalId;
|
||||
Groups = groups?.Select(c => new AssociationWithPermissionsResponseModel(c));
|
||||
Type = collection.Type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -38,4 +40,8 @@ public class CollectionResponseModel : CollectionBaseModel, IResponseModel
|
||||
/// The associated groups that this collection is assigned to.
|
||||
/// </summary>
|
||||
public IEnumerable<AssociationWithPermissionsResponseModel> Groups { get; set; }
|
||||
/// <summary>
|
||||
/// The type of this collection
|
||||
/// </summary>
|
||||
public CollectionType Type { get; set; }
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
@ -18,12 +19,14 @@ public class CollectionResponseModel : ResponseModel
|
||||
OrganizationId = collection.OrganizationId;
|
||||
Name = collection.Name;
|
||||
ExternalId = collection.ExternalId;
|
||||
Type = collection.Type;
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public Guid OrganizationId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string ExternalId { get; set; }
|
||||
public CollectionType Type { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -5,7 +5,6 @@ using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Identity;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
||||
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
@ -16,9 +15,6 @@ using Bit.Core.SecretsManager.Queries.Interfaces;
|
||||
using Bit.Core.SecretsManager.Queries.Secrets.Interfaces;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Enums;
|
||||
using Bit.Core.Tools.Models.Business;
|
||||
using Bit.Core.Tools.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@ -30,7 +26,6 @@ public class SecretsController : Controller
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IProjectRepository _projectRepository;
|
||||
private readonly ISecretRepository _secretRepository;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly ICreateSecretCommand _createSecretCommand;
|
||||
private readonly IUpdateSecretCommand _updateSecretCommand;
|
||||
private readonly IDeleteSecretCommand _deleteSecretCommand;
|
||||
@ -39,14 +34,12 @@ public class SecretsController : Controller
|
||||
private readonly ISecretAccessPoliciesUpdatesQuery _secretAccessPoliciesUpdatesQuery;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IEventService _eventService;
|
||||
private readonly IReferenceEventService _referenceEventService;
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
|
||||
public SecretsController(
|
||||
ICurrentContext currentContext,
|
||||
IProjectRepository projectRepository,
|
||||
ISecretRepository secretRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
ICreateSecretCommand createSecretCommand,
|
||||
IUpdateSecretCommand updateSecretCommand,
|
||||
IDeleteSecretCommand deleteSecretCommand,
|
||||
@ -55,13 +48,11 @@ public class SecretsController : Controller
|
||||
ISecretAccessPoliciesUpdatesQuery secretAccessPoliciesUpdatesQuery,
|
||||
IUserService userService,
|
||||
IEventService eventService,
|
||||
IReferenceEventService referenceEventService,
|
||||
IAuthorizationService authorizationService)
|
||||
{
|
||||
_currentContext = currentContext;
|
||||
_projectRepository = projectRepository;
|
||||
_secretRepository = secretRepository;
|
||||
_organizationRepository = organizationRepository;
|
||||
_createSecretCommand = createSecretCommand;
|
||||
_updateSecretCommand = updateSecretCommand;
|
||||
_deleteSecretCommand = deleteSecretCommand;
|
||||
@ -70,7 +61,6 @@ public class SecretsController : Controller
|
||||
_secretAccessPoliciesUpdatesQuery = secretAccessPoliciesUpdatesQuery;
|
||||
_userService = userService;
|
||||
_eventService = eventService;
|
||||
_referenceEventService = referenceEventService;
|
||||
_authorizationService = authorizationService;
|
||||
|
||||
}
|
||||
@ -148,9 +138,6 @@ public class SecretsController : Controller
|
||||
if (_currentContext.IdentityClientType == IdentityClientType.ServiceAccount)
|
||||
{
|
||||
await _eventService.LogServiceAccountSecretEventAsync(userId, secret, EventType.Secret_Retrieved);
|
||||
|
||||
var org = await _organizationRepository.GetByIdAsync(secret.OrganizationId);
|
||||
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.SmServiceAccountAccessedSecret, org, _currentContext));
|
||||
}
|
||||
|
||||
return new SecretResponseModel(secret, access.Read, access.Write);
|
||||
@ -266,7 +253,7 @@ public class SecretsController : Controller
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await LogSecretsRetrievalAsync(secrets.First().OrganizationId, secrets);
|
||||
await LogSecretsRetrievalAsync(secrets);
|
||||
|
||||
var responses = secrets.Select(s => new BaseSecretResponseModel(s));
|
||||
return new ListResponseModel<BaseSecretResponseModel>(responses);
|
||||
@ -303,21 +290,18 @@ public class SecretsController : Controller
|
||||
|
||||
if (syncResult.HasChanges)
|
||||
{
|
||||
await LogSecretsRetrievalAsync(organizationId, syncResult.Secrets);
|
||||
await LogSecretsRetrievalAsync(syncResult.Secrets);
|
||||
}
|
||||
|
||||
return new SecretsSyncResponseModel(syncResult.HasChanges, syncResult.Secrets);
|
||||
}
|
||||
|
||||
private async Task LogSecretsRetrievalAsync(Guid organizationId, IEnumerable<Secret> secrets)
|
||||
private async Task LogSecretsRetrievalAsync(IEnumerable<Secret> secrets)
|
||||
{
|
||||
if (_currentContext.IdentityClientType == IdentityClientType.ServiceAccount)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User)!.Value;
|
||||
var org = await _organizationRepository.GetByIdAsync(organizationId);
|
||||
await _eventService.LogServiceAccountSecretsEventAsync(userId, secrets, EventType.Secret_Retrieved);
|
||||
await _referenceEventService.RaiseEventAsync(
|
||||
new ReferenceEvent(ReferenceEventType.SmServiceAccountAccessedSecret, org, _currentContext));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,13 +28,11 @@ using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Bit.Api.Auth.Models.Request.WebAuthn;
|
||||
using Bit.Api.Billing;
|
||||
using Bit.Core.AdminConsole.Services.NoopImplementations;
|
||||
using Bit.Core.Auth.Models.Data;
|
||||
using Bit.Core.Auth.Identity.TokenProviders;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.ImportFeatures;
|
||||
using Bit.Core.Tools.ReportFeatures;
|
||||
using Bit.Core.Auth.Models.Api.Request;
|
||||
using Bit.Core.Dirt.Reports.ReportFeatures;
|
||||
using Bit.Core.Tools.SendFeatures;
|
||||
|
||||
#if !OSS
|
||||
@ -224,18 +222,8 @@ public class Startup
|
||||
services.AddHostedService<Core.HostedServices.ApplicationCacheHostedService>();
|
||||
}
|
||||
|
||||
// Slack
|
||||
if (CoreHelpers.SettingHasValue(globalSettings.Slack.ClientId) &&
|
||||
CoreHelpers.SettingHasValue(globalSettings.Slack.ClientSecret) &&
|
||||
CoreHelpers.SettingHasValue(globalSettings.Slack.Scopes))
|
||||
{
|
||||
services.AddHttpClient(SlackService.HttpClientName);
|
||||
services.AddSingleton<ISlackService, SlackService>();
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddSingleton<ISlackService, NoopSlackService>();
|
||||
}
|
||||
// Add SlackService for OAuth API requests - if configured
|
||||
services.AddSlackService(globalSettings);
|
||||
}
|
||||
|
||||
public void Configure(
|
||||
|
@ -5,7 +5,6 @@ using Bit.Api.Tools.Models.Request;
|
||||
using Bit.Api.Tools.Models.Response;
|
||||
using Bit.Api.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
@ -33,7 +32,6 @@ public class SendsController : Controller
|
||||
private readonly INonAnonymousSendCommand _nonAnonymousSendCommand;
|
||||
private readonly ILogger<SendsController> _logger;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public SendsController(
|
||||
ISendRepository sendRepository,
|
||||
@ -43,8 +41,7 @@ public class SendsController : Controller
|
||||
INonAnonymousSendCommand nonAnonymousSendCommand,
|
||||
ISendFileStorageService sendFileStorageService,
|
||||
ILogger<SendsController> logger,
|
||||
GlobalSettings globalSettings,
|
||||
ICurrentContext currentContext)
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
_sendRepository = sendRepository;
|
||||
_userService = userService;
|
||||
@ -54,7 +51,6 @@ public class SendsController : Controller
|
||||
_sendFileStorageService = sendFileStorageService;
|
||||
_logger = logger;
|
||||
_globalSettings = globalSettings;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
#region Anonymous endpoints
|
||||
|
@ -62,9 +62,9 @@ public static class ApiHelpers
|
||||
}
|
||||
}
|
||||
|
||||
if (eventTypeHandlers.ContainsKey(eventGridEvent.EventType))
|
||||
if (eventTypeHandlers.TryGetValue(eventGridEvent.EventType, out var eventTypeHandler))
|
||||
{
|
||||
await eventTypeHandlers[eventGridEvent.EventType](eventGridEvent);
|
||||
await eventTypeHandler(eventGridEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,6 @@ public class CiphersController : Controller
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly ILogger<CiphersController> _logger;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IFeatureService _featureService;
|
||||
private readonly IOrganizationCiphersQuery _organizationCiphersQuery;
|
||||
private readonly IApplicationCacheService _applicationCacheService;
|
||||
private readonly ICollectionRepository _collectionRepository;
|
||||
@ -57,7 +56,6 @@ public class CiphersController : Controller
|
||||
ICurrentContext currentContext,
|
||||
ILogger<CiphersController> logger,
|
||||
GlobalSettings globalSettings,
|
||||
IFeatureService featureService,
|
||||
IOrganizationCiphersQuery organizationCiphersQuery,
|
||||
IApplicationCacheService applicationCacheService,
|
||||
ICollectionRepository collectionRepository)
|
||||
@ -71,7 +69,6 @@ public class CiphersController : Controller
|
||||
_currentContext = currentContext;
|
||||
_logger = logger;
|
||||
_globalSettings = globalSettings;
|
||||
_featureService = featureService;
|
||||
_organizationCiphersQuery = organizationCiphersQuery;
|
||||
_applicationCacheService = applicationCacheService;
|
||||
_collectionRepository = collectionRepository;
|
||||
@ -151,6 +148,16 @@ public class CiphersController : Controller
|
||||
public async Task<CipherResponseModel> Post([FromBody] CipherRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
|
||||
// Validate the model was encrypted for the posting user
|
||||
if (model.EncryptedFor != null)
|
||||
{
|
||||
if (model.EncryptedFor != user.Id)
|
||||
{
|
||||
throw new BadRequestException("Cipher was not encrypted for the current user. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
var cipher = model.ToCipherDetails(user.Id);
|
||||
if (cipher.OrganizationId.HasValue && !await _currentContext.OrganizationUser(cipher.OrganizationId.Value))
|
||||
{
|
||||
@ -170,6 +177,16 @@ public class CiphersController : Controller
|
||||
public async Task<CipherResponseModel> PostCreate([FromBody] CipherCreateRequestModel model)
|
||||
{
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
|
||||
// Validate the model was encrypted for the posting user
|
||||
if (model.Cipher.EncryptedFor != null)
|
||||
{
|
||||
if (model.Cipher.EncryptedFor != user.Id)
|
||||
{
|
||||
throw new BadRequestException("Cipher was not encrypted for the current user. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
var cipher = model.Cipher.ToCipherDetails(user.Id);
|
||||
if (cipher.OrganizationId.HasValue && !await _currentContext.OrganizationUser(cipher.OrganizationId.Value))
|
||||
{
|
||||
@ -192,6 +209,16 @@ public class CiphersController : Controller
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
|
||||
// Validate the model was encrypted for the posting user
|
||||
if (model.Cipher.EncryptedFor != null)
|
||||
{
|
||||
if (model.Cipher.EncryptedFor != userId)
|
||||
{
|
||||
throw new BadRequestException("Cipher was not encrypted for the current user. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
await _cipherService.SaveAsync(cipher, userId, model.Cipher.LastKnownRevisionDate, model.CollectionIds, true, false);
|
||||
|
||||
var response = new CipherMiniResponseModel(cipher, _globalSettings, false);
|
||||
@ -209,6 +236,15 @@ public class CiphersController : Controller
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
// Validate the model was encrypted for the posting user
|
||||
if (model.EncryptedFor != null)
|
||||
{
|
||||
if (model.EncryptedFor != user.Id)
|
||||
{
|
||||
throw new BadRequestException("Cipher was not encrypted for the current user. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
ValidateClientVersionForFido2CredentialSupport(cipher);
|
||||
|
||||
var collectionIds = (await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(user.Id, id)).Select(c => c.CollectionId).ToList();
|
||||
@ -237,6 +273,15 @@ public class CiphersController : Controller
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var cipher = await _cipherRepository.GetOrganizationDetailsByIdAsync(id);
|
||||
|
||||
// Validate the model was encrypted for the posting user
|
||||
if (model.EncryptedFor != null)
|
||||
{
|
||||
if (model.EncryptedFor != userId)
|
||||
{
|
||||
throw new BadRequestException("Cipher was not encrypted for the current user. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
ValidateClientVersionForFido2CredentialSupport(cipher);
|
||||
|
||||
if (cipher == null || !cipher.OrganizationId.HasValue ||
|
||||
@ -327,11 +372,6 @@ public class CiphersController : Controller
|
||||
|
||||
private async Task<bool> CanDeleteOrRestoreCipherAsAdminAsync(Guid organizationId, IEnumerable<Guid> cipherIds)
|
||||
{
|
||||
if (!_featureService.IsEnabled(FeatureFlagKeys.LimitItemDeletion))
|
||||
{
|
||||
return await CanEditCipherAsAdminAsync(organizationId, cipherIds);
|
||||
}
|
||||
|
||||
var org = _currentContext.GetOrganization(organizationId);
|
||||
|
||||
// If we're not an "admin" or if we're a provider user we don't need to check the ciphers
|
||||
@ -658,6 +698,15 @@ public class CiphersController : Controller
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
// Validate the model was encrypted for the posting user
|
||||
if (model.Cipher.EncryptedFor != null)
|
||||
{
|
||||
if (model.Cipher.EncryptedFor != user.Id)
|
||||
{
|
||||
throw new BadRequestException("Cipher was not encrypted for the current user. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
ValidateClientVersionForFido2CredentialSupport(cipher);
|
||||
|
||||
var original = cipher.Clone();
|
||||
@ -1007,7 +1056,7 @@ public class CiphersController : Controller
|
||||
|
||||
[HttpPut("share")]
|
||||
[HttpPost("share")]
|
||||
public async Task PutShareMany([FromBody] CipherBulkShareRequestModel model)
|
||||
public async Task<ListResponseModel<CipherMiniResponseModel>> PutShareMany([FromBody] CipherBulkShareRequestModel model)
|
||||
{
|
||||
var organizationId = new Guid(model.Ciphers.First().OrganizationId);
|
||||
if (!await _currentContext.OrganizationUser(organizationId))
|
||||
@ -1016,26 +1065,41 @@ public class CiphersController : Controller
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
|
||||
var ciphers = await _cipherRepository.GetManyByUserIdAsync(userId, withOrganizations: false);
|
||||
var ciphersDict = ciphers.ToDictionary(c => c.Id);
|
||||
|
||||
var shareCiphers = new List<(Cipher, DateTime?)>();
|
||||
// Validate the model was encrypted for the posting user
|
||||
foreach (var cipher in model.Ciphers)
|
||||
{
|
||||
if (!ciphersDict.ContainsKey(cipher.Id.Value))
|
||||
if (cipher.EncryptedFor.HasValue && cipher.EncryptedFor.Value != userId)
|
||||
{
|
||||
throw new BadRequestException("Trying to move ciphers that you do not own.");
|
||||
throw new BadRequestException("Cipher was not encrypted for the current user. Please try again.");
|
||||
}
|
||||
}
|
||||
|
||||
var existingCipher = ciphersDict[cipher.Id.Value];
|
||||
var shareCiphers = new List<(CipherDetails, DateTime?)>();
|
||||
foreach (var cipher in model.Ciphers)
|
||||
{
|
||||
if (!ciphersDict.TryGetValue(cipher.Id.Value, out var existingCipher))
|
||||
{
|
||||
throw new BadRequestException("Trying to share ciphers that you do not own.");
|
||||
}
|
||||
|
||||
ValidateClientVersionForFido2CredentialSupport(existingCipher);
|
||||
|
||||
shareCiphers.Add((cipher.ToCipher(existingCipher), cipher.LastKnownRevisionDate));
|
||||
shareCiphers.Add((cipher.ToCipherDetails(existingCipher), cipher.LastKnownRevisionDate));
|
||||
}
|
||||
|
||||
await _cipherService.ShareManyAsync(shareCiphers, organizationId,
|
||||
model.CollectionIds.Select(c => new Guid(c)), userId);
|
||||
var updated = await _cipherService.ShareManyAsync(
|
||||
shareCiphers,
|
||||
organizationId,
|
||||
model.CollectionIds.Select(Guid.Parse),
|
||||
userId
|
||||
);
|
||||
|
||||
var response = updated.Select(c => new CipherMiniResponseModel(c, _globalSettings, c.OrganizationUseTotp));
|
||||
return new ListResponseModel<CipherMiniResponseModel>(response);
|
||||
}
|
||||
|
||||
[HttpPost("purge")]
|
||||
@ -1117,14 +1181,14 @@ public class CiphersController : Controller
|
||||
var cipher = await GetByIdAsync(id, userId);
|
||||
var attachments = cipher?.GetAttachments();
|
||||
|
||||
if (attachments == null || !attachments.ContainsKey(attachmentId) || attachments[attachmentId].Validated)
|
||||
if (attachments == null || !attachments.TryGetValue(attachmentId, out var attachment) || attachment.Validated)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return new AttachmentUploadDataResponseModel
|
||||
{
|
||||
Url = await _attachmentStorageService.GetAttachmentUploadUrlAsync(cipher, attachments[attachmentId]),
|
||||
Url = await _attachmentStorageService.GetAttachmentUploadUrlAsync(cipher, attachment),
|
||||
FileUploadType = _attachmentStorageService.FileUploadType,
|
||||
};
|
||||
}
|
||||
@ -1143,11 +1207,10 @@ public class CiphersController : Controller
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var cipher = await GetByIdAsync(id, userId);
|
||||
var attachments = cipher?.GetAttachments();
|
||||
if (attachments == null || !attachments.ContainsKey(attachmentId))
|
||||
if (attachments == null || !attachments.TryGetValue(attachmentId, out var attachmentData))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
var attachmentData = attachments[attachmentId];
|
||||
|
||||
await Request.GetFileAsync(async (stream) =>
|
||||
{
|
||||
@ -1297,7 +1360,7 @@ public class CiphersController : Controller
|
||||
var cipher = await _cipherRepository.GetByIdAsync(new Guid(cipherId));
|
||||
var attachments = cipher?.GetAttachments() ?? new Dictionary<string, CipherAttachment.MetaData>();
|
||||
|
||||
if (cipher == null || !attachments.ContainsKey(attachmentId) || attachments[attachmentId].Validated)
|
||||
if (cipher == null || !attachments.TryGetValue(attachmentId, out var attachment) || attachment.Validated)
|
||||
{
|
||||
if (_attachmentStorageService is AzureSendFileStorageService azureFileStorageService)
|
||||
{
|
||||
@ -1307,7 +1370,7 @@ public class CiphersController : Controller
|
||||
return;
|
||||
}
|
||||
|
||||
await _cipherService.ValidateCipherAttachmentFile(cipher, attachments[attachmentId]);
|
||||
await _cipherService.ValidateCipherAttachmentFile(cipher, attachment);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -1,9 +1,7 @@
|
||||
using Bit.Api.Models.Response;
|
||||
using Bit.Api.Vault.Models.Request;
|
||||
using Bit.Api.Vault.Models.Response;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core.Vault.Commands.Interfaces;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Bit.Core.Vault.Enums;
|
||||
@ -15,7 +13,6 @@ namespace Bit.Api.Vault.Controllers;
|
||||
|
||||
[Route("tasks")]
|
||||
[Authorize("Application")]
|
||||
[RequireFeature(FeatureFlagKeys.SecurityTasks)]
|
||||
public class SecurityTaskController : Controller
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
|
@ -11,6 +11,10 @@ namespace Bit.Api.Vault.Models.Request;
|
||||
|
||||
public class CipherRequestModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The Id of the user that encrypted the cipher. It should always represent a UserId.
|
||||
/// </summary>
|
||||
public Guid? EncryptedFor { get; set; }
|
||||
public CipherType Type { get; set; }
|
||||
|
||||
[StringLength(36)]
|
||||
@ -109,18 +113,25 @@ public class CipherRequestModel
|
||||
|
||||
if (hasAttachments2)
|
||||
{
|
||||
foreach (var attachment in attachments.Where(a => Attachments2.ContainsKey(a.Key)))
|
||||
foreach (var attachment in attachments)
|
||||
{
|
||||
var attachment2 = Attachments2[attachment.Key];
|
||||
if (!Attachments2.TryGetValue(attachment.Key, out var attachment2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
attachment.Value.FileName = attachment2.FileName;
|
||||
attachment.Value.Key = attachment2.Key;
|
||||
}
|
||||
}
|
||||
else if (hasAttachments)
|
||||
{
|
||||
foreach (var attachment in attachments.Where(a => Attachments.ContainsKey(a.Key)))
|
||||
foreach (var attachment in attachments)
|
||||
{
|
||||
attachment.Value.FileName = Attachments[attachment.Key];
|
||||
if (!Attachments.TryGetValue(attachment.Key, out var attachmentForKey))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
attachment.Value.FileName = attachmentForKey;
|
||||
attachment.Value.Key = null;
|
||||
}
|
||||
}
|
||||
|
@ -129,13 +129,13 @@ public class CipherDetailsResponseModel : CipherResponseModel
|
||||
IDictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphers, string obj = "cipherDetails")
|
||||
: base(cipher, user, organizationAbilities, globalSettings, obj)
|
||||
{
|
||||
if (collectionCiphers?.ContainsKey(cipher.Id) ?? false)
|
||||
if (collectionCiphers?.TryGetValue(cipher.Id, out var collectionCipher) ?? false)
|
||||
{
|
||||
CollectionIds = collectionCiphers[cipher.Id].Select(c => c.CollectionId);
|
||||
CollectionIds = collectionCipher.Select(c => c.CollectionId);
|
||||
}
|
||||
else
|
||||
{
|
||||
CollectionIds = new Guid[] { };
|
||||
CollectionIds = [];
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,7 +147,7 @@ public class CipherDetailsResponseModel : CipherResponseModel
|
||||
IEnumerable<CollectionCipher> collectionCiphers, string obj = "cipherDetails")
|
||||
: base(cipher, user, organizationAbilities, globalSettings, obj)
|
||||
{
|
||||
CollectionIds = collectionCiphers?.Select(c => c.CollectionId) ?? new List<Guid>();
|
||||
CollectionIds = collectionCiphers?.Select(c => c.CollectionId) ?? [];
|
||||
}
|
||||
|
||||
public CipherDetailsResponseModel(
|
||||
@ -158,7 +158,7 @@ public class CipherDetailsResponseModel : CipherResponseModel
|
||||
string obj = "cipherDetails")
|
||||
: base(cipher, user, organizationAbilities, globalSettings, obj)
|
||||
{
|
||||
CollectionIds = cipher.CollectionIds ?? new List<Guid>();
|
||||
CollectionIds = cipher.CollectionIds ?? [];
|
||||
}
|
||||
|
||||
public IEnumerable<Guid> CollectionIds { get; set; }
|
||||
@ -170,13 +170,13 @@ public class CipherMiniDetailsResponseModel : CipherMiniResponseModel
|
||||
IDictionary<Guid, IGrouping<Guid, CollectionCipher>> collectionCiphers, bool orgUseTotp, string obj = "cipherMiniDetails")
|
||||
: base(cipher, globalSettings, orgUseTotp, obj)
|
||||
{
|
||||
if (collectionCiphers?.ContainsKey(cipher.Id) ?? false)
|
||||
if (collectionCiphers?.TryGetValue(cipher.Id, out var collectionCipher) ?? false)
|
||||
{
|
||||
CollectionIds = collectionCiphers[cipher.Id].Select(c => c.CollectionId);
|
||||
CollectionIds = collectionCipher.Select(c => c.CollectionId);
|
||||
}
|
||||
else
|
||||
{
|
||||
CollectionIds = new Guid[] { };
|
||||
CollectionIds = [];
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,7 +184,7 @@ public class CipherMiniDetailsResponseModel : CipherMiniResponseModel
|
||||
GlobalSettings globalSettings, bool orgUseTotp, string obj = "cipherMiniDetails")
|
||||
: base(cipher, globalSettings, orgUseTotp, obj)
|
||||
{
|
||||
CollectionIds = cipher.CollectionIds ?? new List<Guid>();
|
||||
CollectionIds = cipher.CollectionIds ?? [];
|
||||
}
|
||||
|
||||
public CipherMiniDetailsResponseModel(CipherOrganizationDetailsWithCollections cipher,
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Setup
|
||||
|
||||
@ -19,31 +19,36 @@ then
|
||||
LGID=65534
|
||||
fi
|
||||
|
||||
# Create user and group
|
||||
if [ "$(id -u)" = "0" ]
|
||||
then
|
||||
# Create user and group
|
||||
|
||||
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
||||
groupmod -o -g $LGID $GROUPNAME >/dev/null 2>&1
|
||||
useradd -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1 ||
|
||||
usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1
|
||||
mkhomedir_helper $USERNAME
|
||||
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
||||
groupmod -o -g $LGID $GROUPNAME >/dev/null 2>&1
|
||||
useradd -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1 ||
|
||||
usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1
|
||||
mkhomedir_helper $USERNAME
|
||||
|
||||
# The rest...
|
||||
# The rest...
|
||||
|
||||
chown -R $USERNAME:$GROUPNAME /app
|
||||
mkdir -p /etc/bitwarden/core
|
||||
mkdir -p /etc/bitwarden/logs
|
||||
mkdir -p /etc/bitwarden/ca-certificates
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||
chown -R $USERNAME:$GROUPNAME /app
|
||||
mkdir -p /etc/bitwarden/core
|
||||
mkdir -p /etc/bitwarden/logs
|
||||
mkdir -p /etc/bitwarden/ca-certificates
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||
|
||||
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
||||
&& update-ca-certificates
|
||||
if [[ -f "/etc/bitwarden/kerberos/bitwarden.keytab" && -f "/etc/bitwarden/kerberos/krb5.conf" ]]; then
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden/kerberos
|
||||
fi
|
||||
|
||||
gosu_cmd="gosu $USERNAME:$GROUPNAME"
|
||||
else
|
||||
gosu_cmd=""
|
||||
fi
|
||||
|
||||
if [[ -f "/etc/bitwarden/kerberos/bitwarden.keytab" && -f "/etc/bitwarden/kerberos/krb5.conf" ]]; then
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden/kerberos
|
||||
cp -f /etc/bitwarden/kerberos/krb5.conf /etc/krb5.conf
|
||||
gosu $USERNAME:$GROUPNAME kinit $globalSettings__kerberosUser -k -t /etc/bitwarden/kerberos/bitwarden.keytab
|
||||
cp -f /etc/bitwarden/kerberos/krb5.conf /etc/krb5.conf
|
||||
$gosu_cmd kinit $globalSettings__kerberosUser -k -t /etc/bitwarden/kerberos/bitwarden.keytab
|
||||
fi
|
||||
|
||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/Api.dll
|
||||
exec $gosu_cmd /app/Api
|
||||
|
@ -1,4 +0,0 @@
|
||||
*
|
||||
!obj/build-output/publish/*
|
||||
!obj/Docker/empty/
|
||||
!entrypoint.sh
|
@ -10,7 +10,7 @@
|
||||
<ProjectReference Include="..\Core\Core.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.3.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -28,8 +28,8 @@ public class AppleController : Controller
|
||||
return new BadRequestResult();
|
||||
}
|
||||
|
||||
var key = HttpContext.Request.Query.ContainsKey("key") ?
|
||||
HttpContext.Request.Query["key"].ToString() : null;
|
||||
var key = HttpContext.Request.Query.TryGetValue("key", out var keyValue) ?
|
||||
keyValue.ToString() : null;
|
||||
if (!CoreHelpers.FixedTimeEquals(key, _billingSettings.AppleWebhookKey))
|
||||
{
|
||||
return new BadRequestResult();
|
||||
|
@ -51,8 +51,8 @@ public class PayPalController : Controller
|
||||
[HttpPost("ipn")]
|
||||
public async Task<IActionResult> PostIpn()
|
||||
{
|
||||
var key = HttpContext.Request.Query.ContainsKey("key")
|
||||
? HttpContext.Request.Query["key"].ToString()
|
||||
var key = HttpContext.Request.Query.TryGetValue("key", out var keyValue)
|
||||
? keyValue.ToString()
|
||||
: null;
|
||||
|
||||
if (string.IsNullOrEmpty(key))
|
||||
|
@ -1,6 +1,50 @@
|
||||
###############################################
|
||||
# Build stage #
|
||||
###############################################
|
||||
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS 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
|
||||
|
||||
# Copy required project files
|
||||
WORKDIR /source
|
||||
COPY . ./
|
||||
|
||||
# Restore project dependencies and tools
|
||||
WORKDIR /source/src/Billing
|
||||
RUN . /tmp/rid.txt && dotnet restore -r $RID
|
||||
|
||||
# Build project
|
||||
RUN . /tmp/rid.txt && dotnet publish \
|
||||
-c release \
|
||||
--no-restore \
|
||||
--self-contained \
|
||||
/p:PublishSingleFile=true \
|
||||
-r $RID \
|
||||
-o out
|
||||
|
||||
###############################################
|
||||
# App stage #
|
||||
###############################################
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
LABEL com.bitwarden.product="bitwarden"
|
||||
ENV ASPNETCORE_ENVIRONMENT=Production
|
||||
ENV ASPNETCORE_URLS=http://+:5000
|
||||
ENV SSL_CERT_DIR=/etc/bitwarden/ca-certificates
|
||||
EXPOSE 5000
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
@ -8,14 +52,11 @@ RUN apt-get update \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV ASPNETCORE_URLS http://+:5000
|
||||
# Copy app from the build stage
|
||||
WORKDIR /app
|
||||
EXPOSE 5000
|
||||
COPY entrypoint.sh /
|
||||
COPY --from=build /source/src/Billing/out /app
|
||||
COPY ./src/Billing/entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
COPY obj/build-output/publish .
|
||||
|
||||
HEALTHCHECK CMD curl -f http://localhost:5000/alive || exit 1
|
||||
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
@ -20,8 +20,8 @@ public class Program
|
||||
return e.Level >= globalSettings.MinLogLevel.BillingSettings.Jobs;
|
||||
}
|
||||
|
||||
if (e.Properties.ContainsKey("RequestPath") &&
|
||||
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
|
||||
if (e.Properties.TryGetValue("RequestPath", out var requestPath) &&
|
||||
!string.IsNullOrWhiteSpace(requestPath?.ToString()) &&
|
||||
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
||||
{
|
||||
return false;
|
||||
|
@ -1,8 +1,4 @@
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Tools.Enums;
|
||||
using Bit.Core.Tools.Models.Business;
|
||||
using Bit.Core.Tools.Services;
|
||||
using Bit.Core.Repositories;
|
||||
using Event = Stripe.Event;
|
||||
|
||||
namespace Bit.Billing.Services.Implementations;
|
||||
@ -10,23 +6,17 @@ namespace Bit.Billing.Services.Implementations;
|
||||
public class CustomerUpdatedHandler : ICustomerUpdatedHandler
|
||||
{
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IReferenceEventService _referenceEventService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IStripeEventService _stripeEventService;
|
||||
private readonly IStripeEventUtilityService _stripeEventUtilityService;
|
||||
private readonly ILogger<CustomerUpdatedHandler> _logger;
|
||||
|
||||
public CustomerUpdatedHandler(
|
||||
IOrganizationRepository organizationRepository,
|
||||
IReferenceEventService referenceEventService,
|
||||
ICurrentContext currentContext,
|
||||
IStripeEventService stripeEventService,
|
||||
IStripeEventUtilityService stripeEventUtilityService,
|
||||
ILogger<CustomerUpdatedHandler> logger)
|
||||
{
|
||||
_organizationRepository = organizationRepository ?? throw new ArgumentNullException(nameof(organizationRepository));
|
||||
_referenceEventService = referenceEventService;
|
||||
_currentContext = currentContext;
|
||||
_stripeEventService = stripeEventService;
|
||||
_stripeEventUtilityService = stripeEventUtilityService;
|
||||
_logger = logger;
|
||||
@ -95,20 +85,5 @@ public class CustomerUpdatedHandler : ICustomerUpdatedHandler
|
||||
|
||||
organization.BillingEmail = customer.Email;
|
||||
await _organizationRepository.ReplaceAsync(organization);
|
||||
|
||||
if (_referenceEventService == null)
|
||||
{
|
||||
_logger.LogError("ReferenceEventService was not initialized in CustomerUpdatedHandler");
|
||||
throw new InvalidOperationException($"{nameof(_referenceEventService)} is not initialized");
|
||||
}
|
||||
|
||||
if (_currentContext == null)
|
||||
{
|
||||
_logger.LogError("CurrentContext was not initialized in CustomerUpdatedHandler");
|
||||
throw new InvalidOperationException($"{nameof(_currentContext)} is not initialized");
|
||||
}
|
||||
|
||||
await _referenceEventService.RaiseEventAsync(
|
||||
new ReferenceEvent(ReferenceEventType.OrganizationEditedInStripe, organization, _currentContext));
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,9 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Billing.Pricing;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Platform.Push;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Enums;
|
||||
using Bit.Core.Tools.Models.Business;
|
||||
using Bit.Core.Tools.Services;
|
||||
using Event = Stripe.Event;
|
||||
|
||||
namespace Bit.Billing.Services.Implementations;
|
||||
@ -22,9 +18,6 @@ public class PaymentSucceededHandler : IPaymentSucceededHandler
|
||||
private readonly IStripeFacade _stripeFacade;
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IReferenceEventService _referenceEventService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IStripeEventUtilityService _stripeEventUtilityService;
|
||||
private readonly IPushNotificationService _pushNotificationService;
|
||||
private readonly IOrganizationEnableCommand _organizationEnableCommand;
|
||||
@ -36,9 +29,6 @@ public class PaymentSucceededHandler : IPaymentSucceededHandler
|
||||
IStripeFacade stripeFacade,
|
||||
IProviderRepository providerRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IReferenceEventService referenceEventService,
|
||||
ICurrentContext currentContext,
|
||||
IUserRepository userRepository,
|
||||
IStripeEventUtilityService stripeEventUtilityService,
|
||||
IUserService userService,
|
||||
IPushNotificationService pushNotificationService,
|
||||
@ -50,9 +40,6 @@ public class PaymentSucceededHandler : IPaymentSucceededHandler
|
||||
_stripeFacade = stripeFacade;
|
||||
_providerRepository = providerRepository;
|
||||
_organizationRepository = organizationRepository;
|
||||
_referenceEventService = referenceEventService;
|
||||
_currentContext = currentContext;
|
||||
_userRepository = userRepository;
|
||||
_stripeEventUtilityService = stripeEventUtilityService;
|
||||
_userService = userService;
|
||||
_pushNotificationService = pushNotificationService;
|
||||
@ -116,27 +103,7 @@ public class PaymentSucceededHandler : IPaymentSucceededHandler
|
||||
_logger.LogError("invoice.payment_succeeded webhook ({EventID}) for Provider ({ProviderID}) indicates missing subscription line items",
|
||||
parsedEvent.Id,
|
||||
provider.Id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await _referenceEventService.RaiseEventAsync(new ReferenceEvent
|
||||
{
|
||||
Type = ReferenceEventType.Rebilled,
|
||||
Source = ReferenceEventSource.Provider,
|
||||
Id = provider.Id,
|
||||
PlanType = PlanType.TeamsMonthly,
|
||||
Seats = (int)teamsMonthlyLineItem.Quantity
|
||||
});
|
||||
|
||||
await _referenceEventService.RaiseEventAsync(new ReferenceEvent
|
||||
{
|
||||
Type = ReferenceEventType.Rebilled,
|
||||
Source = ReferenceEventSource.Provider,
|
||||
Id = provider.Id,
|
||||
PlanType = PlanType.EnterpriseMonthly,
|
||||
Seats = (int)enterpriseMonthlyLineItem.Quantity
|
||||
});
|
||||
}
|
||||
else if (organizationId.HasValue)
|
||||
{
|
||||
@ -156,15 +123,6 @@ public class PaymentSucceededHandler : IPaymentSucceededHandler
|
||||
|
||||
await _organizationEnableCommand.EnableAsync(organizationId.Value, subscription.CurrentPeriodEnd);
|
||||
await _pushNotificationService.PushSyncOrganizationStatusAsync(organization);
|
||||
|
||||
await _referenceEventService.RaiseEventAsync(
|
||||
new ReferenceEvent(ReferenceEventType.Rebilled, organization, _currentContext)
|
||||
{
|
||||
PlanName = organization?.Plan,
|
||||
PlanType = organization?.PlanType,
|
||||
Seats = organization?.Seats,
|
||||
Storage = organization?.MaxStorageGb,
|
||||
});
|
||||
}
|
||||
else if (userId.HasValue)
|
||||
{
|
||||
@ -174,14 +132,6 @@ public class PaymentSucceededHandler : IPaymentSucceededHandler
|
||||
}
|
||||
|
||||
await _userService.EnablePremiumAsync(userId.Value, subscription.CurrentPeriodEnd);
|
||||
|
||||
var user = await _userRepository.GetByIdAsync(userId.Value);
|
||||
await _referenceEventService.RaiseEventAsync(
|
||||
new ReferenceEvent(ReferenceEventType.Rebilled, user, _currentContext)
|
||||
{
|
||||
PlanName = IStripeEventUtilityService.PremiumPlanId,
|
||||
Storage = user?.MaxStorageGb,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Setup
|
||||
|
||||
@ -19,25 +19,27 @@ then
|
||||
LGID=65534
|
||||
fi
|
||||
|
||||
# Create user and group
|
||||
if [ "$(id -u)" = "0" ]
|
||||
then
|
||||
# Create user and group
|
||||
|
||||
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
||||
groupmod -o -g $LGID $GROUPNAME >/dev/null 2>&1
|
||||
useradd -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1 ||
|
||||
usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1
|
||||
mkhomedir_helper $USERNAME
|
||||
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
||||
groupmod -o -g $LGID $GROUPNAME >/dev/null 2>&1
|
||||
useradd -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1 ||
|
||||
usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1
|
||||
mkhomedir_helper $USERNAME
|
||||
|
||||
# The rest...
|
||||
# The rest...
|
||||
|
||||
chown -R $USERNAME:$GROUPNAME /app
|
||||
mkdir -p /etc/bitwarden/core
|
||||
mkdir -p /etc/bitwarden/logs
|
||||
mkdir -p /etc/bitwarden/ca-certificates
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||
chown -R $USERNAME:$GROUPNAME /app
|
||||
mkdir -p /etc/bitwarden/core
|
||||
mkdir -p /etc/bitwarden/logs
|
||||
mkdir -p /etc/bitwarden/ca-certificates
|
||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||
|
||||
if [[ $globalSettings__selfHosted == "true" ]]; then
|
||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
||||
&& update-ca-certificates
|
||||
gosu_cmd="gosu $USERNAME:$GROUPNAME"
|
||||
else
|
||||
gosu_cmd=""
|
||||
fi
|
||||
|
||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/Billing.dll
|
||||
exec $gosu_cmd /app/Billing
|
||||
|
@ -8,14 +8,13 @@ using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.Core.AdminConsole.Entities;
|
||||
|
||||
public class Organization : ITableObject<Guid>, IStorableSubscriber, IRevisable, IReferenceable
|
||||
public class Organization : ITableObject<Guid>, IStorableSubscriber, IRevisable
|
||||
{
|
||||
private Dictionary<TwoFactorProviderType, TwoFactorProvider>? _twoFactorProviders;
|
||||
|
||||
@ -258,12 +257,12 @@ public class Organization : ITableObject<Guid>, IStorableSubscriber, IRevisable,
|
||||
public bool TwoFactorProviderIsEnabled(TwoFactorProviderType provider)
|
||||
{
|
||||
var providers = GetTwoFactorProviders();
|
||||
if (providers == null || !providers.ContainsKey(provider))
|
||||
if (providers == null || !providers.TryGetValue(provider, out var twoFactorProvider))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return providers[provider].Enabled && Use2fa;
|
||||
return twoFactorProvider.Enabled && Use2fa;
|
||||
}
|
||||
|
||||
public bool TwoFactorIsEnabled()
|
||||
@ -280,12 +279,7 @@ public class Organization : ITableObject<Guid>, IStorableSubscriber, IRevisable,
|
||||
public TwoFactorProvider? GetTwoFactorProvider(TwoFactorProviderType provider)
|
||||
{
|
||||
var providers = GetTwoFactorProviders();
|
||||
if (providers == null || !providers.ContainsKey(provider))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return providers[provider];
|
||||
return providers?.GetValueOrDefault(provider);
|
||||
}
|
||||
|
||||
public void UpdateFromLicense(OrganizationLicense license, IFeatureService featureService)
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Interfaces;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
@ -9,23 +10,75 @@ using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Entities;
|
||||
|
||||
/// <summary>
|
||||
/// An association table between one <see cref="User"/> and one <see cref="Organization"/>, representing that user's
|
||||
/// membership in the organization. "Member" refers to the OrganizationUser object.
|
||||
/// </summary>
|
||||
public class OrganizationUser : ITableObject<Guid>, IExternal, IOrganizationUser
|
||||
{
|
||||
/// <summary>
|
||||
/// A unique random identifier.
|
||||
/// </summary>
|
||||
public Guid Id { get; set; }
|
||||
/// <summary>
|
||||
/// The ID of the Organization that the user is a member of.
|
||||
/// </summary>
|
||||
public Guid OrganizationId { get; set; }
|
||||
/// <summary>
|
||||
/// The ID of the User that is the member. This is NULL if the Status is Invited (or Invited and then Revoked), because
|
||||
/// it is not linked to a specific User yet.
|
||||
/// </summary>
|
||||
public Guid? UserId { get; set; }
|
||||
/// <summary>
|
||||
/// The email address of the user invited to the organization. This is NULL if the Status is not Invited (or
|
||||
/// Invited and then Revoked), because in that case the OrganizationUser is linked to a User
|
||||
/// and the email is stored on the User object.
|
||||
/// </summary>
|
||||
[MaxLength(256)]
|
||||
public string? Email { get; set; }
|
||||
/// <summary>
|
||||
/// The Organization symmetric key encrypted with the User's public key. NULL if the user is not in a Confirmed
|
||||
/// (or Confirmed and then Revoked) status.
|
||||
/// </summary>
|
||||
public string? Key { get; set; }
|
||||
/// <summary>
|
||||
/// The User's symmetric key encrypted with the Organization's public key. NULL if the OrganizationUser
|
||||
/// is not enrolled in account recovery.
|
||||
/// </summary>
|
||||
public string? ResetPasswordKey { get; set; }
|
||||
/// <inheritdoc cref="OrganizationUserStatusType"/>
|
||||
public OrganizationUserStatusType Status { get; set; }
|
||||
/// <summary>
|
||||
/// The User's role in the Organization.
|
||||
/// </summary>
|
||||
public OrganizationUserType Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An ID used to identify the OrganizationUser with an external directory service. Used by Directory Connector
|
||||
/// and SCIM.
|
||||
/// </summary>
|
||||
[MaxLength(300)]
|
||||
public string? ExternalId { get; set; }
|
||||
/// <summary>
|
||||
/// The date the OrganizationUser was created, i.e. when the User was first invited to the Organization.
|
||||
/// </summary>
|
||||
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
||||
/// <summary>
|
||||
/// The last date the OrganizationUser entry was updated.
|
||||
/// </summary>
|
||||
public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow;
|
||||
/// <summary>
|
||||
/// A json blob representing the <see cref="Bit.Core.Models.Data.Permissions"/> of the OrganizationUser if they
|
||||
/// are a Custom user role (i.e. the <see cref="OrganizationUserType"/> is Custom). MAY be NULL if they are not
|
||||
/// a custom user, but this is not guaranteed; do not use this to determine their role.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Avoid using this property directly - instead use the <see cref="GetPermissions"/> and <see cref="SetPermissions"/>
|
||||
/// helper methods.
|
||||
/// </remarks>
|
||||
public string? Permissions { get; set; }
|
||||
/// <summary>
|
||||
/// True if the User has access to Secrets Manager for this Organization, false otherwise.
|
||||
/// </summary>
|
||||
public bool AccessSecretsManager { get; set; }
|
||||
|
||||
public void SetNewId()
|
||||
|
@ -7,3 +7,19 @@ public enum IntegrationType : int
|
||||
Slack = 3,
|
||||
Webhook = 4,
|
||||
}
|
||||
|
||||
public static class IntegrationTypeExtensions
|
||||
{
|
||||
public static string ToRoutingKey(this IntegrationType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case IntegrationType.Slack:
|
||||
return "slack";
|
||||
case IntegrationType.Webhook:
|
||||
return "webhook";
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), $"Unsupported integration type: {type}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,34 @@
|
||||
namespace Bit.Core.Enums;
|
||||
using Bit.Core.Entities;
|
||||
|
||||
namespace Bit.Core.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the different stages of a member's lifecycle in an organization.
|
||||
/// The <see cref="OrganizationUser"/> object is populated differently depending on their Status.
|
||||
/// </summary>
|
||||
public enum OrganizationUserStatusType : short
|
||||
{
|
||||
/// <summary>
|
||||
/// The OrganizationUser entry only represents an invitation to join the organization. It is not linked to a
|
||||
/// specific User yet.
|
||||
/// </summary>
|
||||
Invited = 0,
|
||||
/// <summary>
|
||||
/// The User has accepted the invitation and linked their User account to the OrganizationUser entry.
|
||||
/// </summary>
|
||||
Accepted = 1,
|
||||
/// <summary>
|
||||
/// An administrator has granted the User access to the organization. This is the final step in the User becoming
|
||||
/// a "full" member of the organization, including a key exchange so that they can decrypt organization data.
|
||||
/// </summary>
|
||||
Confirmed = 2,
|
||||
/// <summary>
|
||||
/// The OrganizationUser has been revoked from the organization and cannot access organization data while in this state.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An OrganizationUser may move into this status from any other status, and will move back to their original status
|
||||
/// if restored. This allows an administrator to easily suspend and restore access without going through the
|
||||
/// Invite flow again.
|
||||
/// </remarks>
|
||||
Revoked = -1,
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ public enum PolicyType : byte
|
||||
AutomaticAppLogIn = 12,
|
||||
FreeFamiliesSponsorshipPolicy = 13,
|
||||
RemoveUnlockWithPin = 14,
|
||||
RestrictedItemTypesPolicy = 15,
|
||||
}
|
||||
|
||||
public static class PolicyTypeExtensions
|
||||
@ -43,7 +44,8 @@ public static class PolicyTypeExtensions
|
||||
PolicyType.ActivateAutofill => "Active auto-fill",
|
||||
PolicyType.AutomaticAppLogIn => "Automatically log in users for allowed applications",
|
||||
PolicyType.FreeFamiliesSponsorshipPolicy => "Remove Free Bitwarden Families sponsorship",
|
||||
PolicyType.RemoveUnlockWithPin => "Remove unlock with PIN"
|
||||
PolicyType.RemoveUnlockWithPin => "Remove unlock with PIN",
|
||||
PolicyType.RestrictedItemTypesPolicy => "Restricted item types",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
#nullable enable
|
||||
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.AdminConsole.Models.Data.Integrations;
|
||||
|
||||
public interface IIntegrationMessage
|
||||
{
|
||||
IntegrationType IntegrationType { get; }
|
||||
string MessageId { get; set; }
|
||||
int RetryCount { get; }
|
||||
DateTime? DelayUntilDate { get; }
|
||||
void ApplyRetry(DateTime? handlerDelayUntilDate);
|
||||
string ToJson();
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.Core.AdminConsole.Models.Data.Integrations;
|
||||
|
||||
public class IntegrationHandlerResult
|
||||
{
|
||||
public IntegrationHandlerResult(bool success, IIntegrationMessage message)
|
||||
{
|
||||
Success = success;
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public bool Success { get; set; } = false;
|
||||
public bool Retryable { get; set; } = false;
|
||||
public IIntegrationMessage Message { get; set; }
|
||||
public DateTime? DelayUntilDate { get; set; }
|
||||
public string FailureReason { get; set; } = string.Empty;
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.AdminConsole.Models.Data.Integrations;
|
||||
|
||||
public class IntegrationMessage : IIntegrationMessage
|
||||
{
|
||||
public IntegrationType IntegrationType { get; set; }
|
||||
public required string MessageId { get; set; }
|
||||
public required string RenderedTemplate { get; set; }
|
||||
public int RetryCount { get; set; } = 0;
|
||||
public DateTime? DelayUntilDate { get; set; }
|
||||
|
||||
public void ApplyRetry(DateTime? handlerDelayUntilDate)
|
||||
{
|
||||
RetryCount++;
|
||||
|
||||
var baseTime = handlerDelayUntilDate ?? DateTime.UtcNow;
|
||||
var backoffSeconds = Math.Pow(2, RetryCount);
|
||||
var jitterSeconds = Random.Shared.Next(0, 3);
|
||||
|
||||
DelayUntilDate = baseTime.AddSeconds(backoffSeconds + jitterSeconds);
|
||||
}
|
||||
|
||||
public virtual string ToJson()
|
||||
{
|
||||
return JsonSerializer.Serialize(this);
|
||||
}
|
||||
}
|
||||
|
||||
public class IntegrationMessage<T> : IntegrationMessage
|
||||
{
|
||||
public required T Configuration { get; set; }
|
||||
|
||||
public override string ToJson()
|
||||
{
|
||||
return JsonSerializer.Serialize(this);
|
||||
}
|
||||
|
||||
public static IntegrationMessage<T>? FromJson(string json)
|
||||
{
|
||||
return JsonSerializer.Deserialize<IntegrationMessage<T>>(json);
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
#nullable enable
|
||||
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.Core.Models.Data.Integrations;
|
||||
namespace Bit.Core.AdminConsole.Models.Data.Integrations;
|
||||
|
||||
public class IntegrationTemplateContext(EventMessage eventMessage)
|
||||
{
|
||||
|
@ -1,3 +1,5 @@
|
||||
namespace Bit.Core.Models.Data.Integrations;
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.Core.AdminConsole.Models.Data.Integrations;
|
||||
|
||||
public record SlackIntegration(string token);
|
||||
|
@ -1,3 +1,5 @@
|
||||
namespace Bit.Core.Models.Data.Integrations;
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.Core.AdminConsole.Models.Data.Integrations;
|
||||
|
||||
public record SlackIntegrationConfiguration(string channelId);
|
||||
|
@ -1,3 +1,5 @@
|
||||
namespace Bit.Core.Models.Data.Integrations;
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.Core.AdminConsole.Models.Data.Integrations;
|
||||
|
||||
public record SlackIntegrationConfigurationDetails(string channelId, string token);
|
||||
|
@ -1,3 +1,5 @@
|
||||
namespace Bit.Core.Models.Data.Integrations;
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.Core.AdminConsole.Models.Data.Integrations;
|
||||
|
||||
public record WebhookIntegrationConfiguration(string url);
|
||||
|
@ -0,0 +1,5 @@
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.Core.AdminConsole.Models.Data.Integrations;
|
||||
|
||||
public record WebhookIntegrationConfigurationDetails(string url);
|
@ -1,3 +0,0 @@
|
||||
namespace Bit.Core.Models.Data.Integrations;
|
||||
|
||||
public record WebhookIntegrationConfigurationDetils(string url);
|
@ -1,4 +1,5 @@
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Bit.Core.Models.Slack;
|
||||
|
@ -1,15 +1,11 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Groups.Interfaces;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Enums;
|
||||
using Bit.Core.Tools.Models.Business;
|
||||
using Bit.Core.Tools.Services;
|
||||
|
||||
namespace Bit.Core.AdminConsole.OrganizationFeatures.Groups;
|
||||
|
||||
@ -18,21 +14,16 @@ public class CreateGroupCommand : ICreateGroupCommand
|
||||
private readonly IEventService _eventService;
|
||||
private readonly IGroupRepository _groupRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IReferenceEventService _referenceEventService;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
|
||||
public CreateGroupCommand(
|
||||
IEventService eventService,
|
||||
IGroupRepository groupRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IReferenceEventService referenceEventService,
|
||||
ICurrentContext currentContext)
|
||||
IOrganizationUserRepository organizationUserRepository
|
||||
)
|
||||
{
|
||||
_eventService = eventService;
|
||||
_groupRepository = groupRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_referenceEventService = referenceEventService;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
public async Task CreateGroupAsync(Group group, Organization organization,
|
||||
@ -77,8 +68,6 @@ public class CreateGroupCommand : ICreateGroupCommand
|
||||
{
|
||||
await _groupRepository.CreateAsync(group, collections);
|
||||
}
|
||||
|
||||
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.GroupCreated, organization, _currentContext));
|
||||
}
|
||||
|
||||
private async Task GroupRepositoryUpdateUsersAsync(Group group, IEnumerable<Guid> userIds,
|
||||
|
@ -1,4 +1,6 @@
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
@ -27,6 +29,8 @@ public class AcceptOrgUserCommand : IAcceptOrgUserCommand
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
|
||||
private readonly IFeatureService _featureService;
|
||||
private readonly IPolicyRequirementQuery _policyRequirementQuery;
|
||||
|
||||
public AcceptOrgUserCommand(
|
||||
IDataProtectionProvider dataProtectionProvider,
|
||||
@ -37,9 +41,10 @@ public class AcceptOrgUserCommand : IAcceptOrgUserCommand
|
||||
IMailService mailService,
|
||||
IUserRepository userRepository,
|
||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory)
|
||||
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
|
||||
IFeatureService featureService,
|
||||
IPolicyRequirementQuery policyRequirementQuery)
|
||||
{
|
||||
|
||||
// TODO: remove data protector when old token validation removed
|
||||
_dataProtector = dataProtectionProvider.CreateProtector(OrgUserInviteTokenable.DataProtectorPurpose);
|
||||
_globalSettings = globalSettings;
|
||||
@ -50,6 +55,8 @@ public class AcceptOrgUserCommand : IAcceptOrgUserCommand
|
||||
_userRepository = userRepository;
|
||||
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
||||
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
|
||||
_featureService = featureService;
|
||||
_policyRequirementQuery = policyRequirementQuery;
|
||||
}
|
||||
|
||||
public async Task<OrganizationUser> AcceptOrgUserByEmailTokenAsync(Guid organizationUserId, User user, string emailToken,
|
||||
@ -196,15 +203,7 @@ public class AcceptOrgUserCommand : IAcceptOrgUserCommand
|
||||
}
|
||||
|
||||
// Enforce Two Factor Authentication Policy of organization user is trying to join
|
||||
if (!await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user))
|
||||
{
|
||||
var invitedTwoFactorPolicies = await _policyService.GetPoliciesApplicableToUserAsync(user.Id,
|
||||
PolicyType.TwoFactorAuthentication, OrganizationUserStatusType.Invited);
|
||||
if (invitedTwoFactorPolicies.Any(p => p.OrganizationId == orgUser.OrganizationId))
|
||||
{
|
||||
throw new BadRequestException("You cannot join this organization until you enable two-step login on your user account.");
|
||||
}
|
||||
}
|
||||
await ValidateTwoFactorAuthenticationPolicyAsync(user, orgUser.OrganizationId);
|
||||
|
||||
orgUser.Status = OrganizationUserStatusType.Accepted;
|
||||
orgUser.UserId = user.Id;
|
||||
@ -224,4 +223,33 @@ public class AcceptOrgUserCommand : IAcceptOrgUserCommand
|
||||
return orgUser;
|
||||
}
|
||||
|
||||
private async Task ValidateTwoFactorAuthenticationPolicyAsync(User user, Guid organizationId)
|
||||
{
|
||||
if (_featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements))
|
||||
{
|
||||
if (await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user))
|
||||
{
|
||||
// If the user has two-step login enabled, we skip checking the 2FA policy
|
||||
return;
|
||||
}
|
||||
|
||||
var twoFactorPolicyRequirement = await _policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id);
|
||||
if (twoFactorPolicyRequirement.IsTwoFactorRequiredForOrganization(organizationId))
|
||||
{
|
||||
throw new BadRequestException("You cannot join this organization until you enable two-step login on your user account.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user))
|
||||
{
|
||||
var invitedTwoFactorPolicies = await _policyService.GetPoliciesApplicableToUserAsync(user.Id,
|
||||
PolicyType.TwoFactorAuthentication, OrganizationUserStatusType.Invited);
|
||||
if (invitedTwoFactorPolicies.Any(p => p.OrganizationId == organizationId))
|
||||
{
|
||||
throw new BadRequestException("You cannot join this organization until you enable two-step login on your user account.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
||||
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
using Bit.Core.Billing.Enums;
|
||||
@ -24,6 +26,8 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand
|
||||
private readonly IPushRegistrationService _pushRegistrationService;
|
||||
private readonly IPolicyService _policyService;
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
private readonly IPolicyRequirementQuery _policyRequirementQuery;
|
||||
private readonly IFeatureService _featureService;
|
||||
|
||||
public ConfirmOrganizationUserCommand(
|
||||
IOrganizationRepository organizationRepository,
|
||||
@ -35,7 +39,9 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand
|
||||
IPushNotificationService pushNotificationService,
|
||||
IPushRegistrationService pushRegistrationService,
|
||||
IPolicyService policyService,
|
||||
IDeviceRepository deviceRepository)
|
||||
IDeviceRepository deviceRepository,
|
||||
IPolicyRequirementQuery policyRequirementQuery,
|
||||
IFeatureService featureService)
|
||||
{
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
@ -47,6 +53,8 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand
|
||||
_pushRegistrationService = pushRegistrationService;
|
||||
_policyService = policyService;
|
||||
_deviceRepository = deviceRepository;
|
||||
_policyRequirementQuery = policyRequirementQuery;
|
||||
_featureService = featureService;
|
||||
}
|
||||
|
||||
public async Task<OrganizationUser> ConfirmUserAsync(Guid organizationId, Guid organizationUserId, string key,
|
||||
@ -118,8 +126,8 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand
|
||||
}
|
||||
}
|
||||
|
||||
var twoFactorEnabled = usersTwoFactorEnabled.FirstOrDefault(tuple => tuple.userId == user.Id).twoFactorIsEnabled;
|
||||
await CheckPoliciesAsync(organizationId, user, orgUsers, twoFactorEnabled);
|
||||
var userTwoFactorEnabled = usersTwoFactorEnabled.FirstOrDefault(tuple => tuple.userId == user.Id).twoFactorIsEnabled;
|
||||
await CheckPoliciesAsync(organizationId, user, orgUsers, userTwoFactorEnabled);
|
||||
orgUser.Status = OrganizationUserStatusType.Confirmed;
|
||||
orgUser.Key = keys[orgUser.Id];
|
||||
orgUser.Email = null;
|
||||
@ -142,15 +150,10 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand
|
||||
}
|
||||
|
||||
private async Task CheckPoliciesAsync(Guid organizationId, User user,
|
||||
ICollection<OrganizationUser> userOrgs, bool twoFactorEnabled)
|
||||
ICollection<OrganizationUser> userOrgs, bool userTwoFactorEnabled)
|
||||
{
|
||||
// Enforce Two Factor Authentication Policy for this organization
|
||||
var orgRequiresTwoFactor = (await _policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication))
|
||||
.Any(p => p.OrganizationId == organizationId);
|
||||
if (orgRequiresTwoFactor && !twoFactorEnabled)
|
||||
{
|
||||
throw new BadRequestException("User does not have two-step login enabled.");
|
||||
}
|
||||
await ValidateTwoFactorAuthenticationPolicyAsync(user, organizationId, userTwoFactorEnabled);
|
||||
|
||||
var hasOtherOrgs = userOrgs.Any(ou => ou.OrganizationId != organizationId);
|
||||
var singleOrgPolicies = await _policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.SingleOrg);
|
||||
@ -168,6 +171,33 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ValidateTwoFactorAuthenticationPolicyAsync(User user, Guid organizationId, bool userTwoFactorEnabled)
|
||||
{
|
||||
if (_featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements))
|
||||
{
|
||||
if (userTwoFactorEnabled)
|
||||
{
|
||||
// If the user has two-step login enabled, we skip checking the 2FA policy
|
||||
return;
|
||||
}
|
||||
|
||||
var twoFactorPolicyRequirement = await _policyRequirementQuery.GetAsync<RequireTwoFactorPolicyRequirement>(user.Id);
|
||||
if (twoFactorPolicyRequirement.IsTwoFactorRequiredForOrganization(organizationId))
|
||||
{
|
||||
throw new BadRequestException("User does not have two-step login enabled.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var orgRequiresTwoFactor = (await _policyService.GetPoliciesApplicableToUserAsync(user.Id, PolicyType.TwoFactorAuthentication))
|
||||
.Any(p => p.OrganizationId == organizationId);
|
||||
if (orgRequiresTwoFactor && !userTwoFactorEnabled)
|
||||
{
|
||||
throw new BadRequestException("User does not have two-step login enabled.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteAndPushUserRegistrationAsync(Guid organizationId, Guid userId)
|
||||
{
|
||||
var devices = await GetUserDeviceIdsAsync(userId);
|
||||
|
@ -7,9 +7,6 @@ using Bit.Core.Exceptions;
|
||||
using Bit.Core.Platform.Push;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Enums;
|
||||
using Bit.Core.Tools.Models.Business;
|
||||
using Bit.Core.Tools.Services;
|
||||
|
||||
#nullable enable
|
||||
|
||||
@ -24,7 +21,6 @@ public class DeleteClaimedOrganizationUserAccountCommand : IDeleteClaimedOrganiz
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IHasConfirmedOwnersExceptQuery _hasConfirmedOwnersExceptQuery;
|
||||
private readonly IReferenceEventService _referenceEventService;
|
||||
private readonly IPushNotificationService _pushService;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
@ -36,7 +32,6 @@ public class DeleteClaimedOrganizationUserAccountCommand : IDeleteClaimedOrganiz
|
||||
IUserRepository userRepository,
|
||||
ICurrentContext currentContext,
|
||||
IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery,
|
||||
IReferenceEventService referenceEventService,
|
||||
IPushNotificationService pushService,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IProviderUserRepository providerUserRepository)
|
||||
@ -48,7 +43,6 @@ public class DeleteClaimedOrganizationUserAccountCommand : IDeleteClaimedOrganiz
|
||||
_userRepository = userRepository;
|
||||
_currentContext = currentContext;
|
||||
_hasConfirmedOwnersExceptQuery = hasConfirmedOwnersExceptQuery;
|
||||
_referenceEventService = referenceEventService;
|
||||
_pushService = pushService;
|
||||
_organizationRepository = organizationRepository;
|
||||
_providerUserRepository = providerUserRepository;
|
||||
@ -195,8 +189,6 @@ public class DeleteClaimedOrganizationUserAccountCommand : IDeleteClaimedOrganiz
|
||||
await _userRepository.DeleteManyAsync(users);
|
||||
foreach (var user in users)
|
||||
{
|
||||
await _referenceEventService.RaiseEventAsync(
|
||||
new ReferenceEvent(ReferenceEventType.DeleteAccount, user, _currentContext));
|
||||
await _pushService.PushLogOutAsync(user.Id);
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user