mirror of
https://github.com/bitwarden/server.git
synced 2025-06-20 02:48:03 -05:00
Merge branch 'main' into PM-21044
This commit is contained in:
commit
30dce2f73b
@ -3,7 +3,7 @@
|
|||||||
"isRoot": true,
|
"isRoot": true,
|
||||||
"tools": {
|
"tools": {
|
||||||
"swashbuckle.aspnetcore.cli": {
|
"swashbuckle.aspnetcore.cli": {
|
||||||
"version": "7.2.0",
|
"version": "7.3.2",
|
||||||
"commands": ["swagger"]
|
"commands": ["swagger"]
|
||||||
},
|
},
|
||||||
"dotnet-ef": {
|
"dotnet-ef": {
|
||||||
|
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
@ -90,6 +90,9 @@ src/Admin/Views/Tools @bitwarden/team-billing-dev
|
|||||||
.github/workflows/test-database.yml @bitwarden/team-platform-dev
|
.github/workflows/test-database.yml @bitwarden/team-platform-dev
|
||||||
.github/workflows/test.yml @bitwarden/team-platform-dev
|
.github/workflows/test.yml @bitwarden/team-platform-dev
|
||||||
**/*Platform* @bitwarden/team-platform-dev
|
**/*Platform* @bitwarden/team-platform-dev
|
||||||
|
**/.dockerignore @bitwarden/team-platform-dev
|
||||||
|
**/Dockerfile @bitwarden/team-platform-dev
|
||||||
|
**/entrypoint.sh @bitwarden/team-platform-dev
|
||||||
|
|
||||||
# Multiple owners - DO NOT REMOVE (BRE)
|
# Multiple owners - DO NOT REMOVE (BRE)
|
||||||
**/packages.lock.json
|
**/packages.lock.json
|
||||||
|
73
.github/renovate.json5
vendored
73
.github/renovate.json5
vendored
@ -15,12 +15,11 @@
|
|||||||
matchManagers: ["github-actions"],
|
matchManagers: ["github-actions"],
|
||||||
matchFileNames: [
|
matchFileNames: [
|
||||||
".github/workflows/publish.yml",
|
".github/workflows/publish.yml",
|
||||||
".github/workflows/release.yml",
|
".github/workflows/release.yml"
|
||||||
".github/workflows/repository-management.yml"
|
|
||||||
],
|
],
|
||||||
commitMessagePrefix: "[deps] BRE:",
|
commitMessagePrefix: "[deps] BRE:",
|
||||||
reviewers: ["team:dept-bre"],
|
reviewers: ["team:dept-bre"],
|
||||||
addLabels: ["hold"]
|
addLabels: ["hold"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
groupName: "dockerfile minor",
|
groupName: "dockerfile minor",
|
||||||
@ -37,6 +36,16 @@
|
|||||||
matchManagers: ["github-actions"],
|
matchManagers: ["github-actions"],
|
||||||
matchUpdateTypes: ["minor"],
|
matchUpdateTypes: ["minor"],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// For any Microsoft.Extensions.* and Microsoft.AspNetCore.* packages, we want to create PRs for patch updates.
|
||||||
|
// This overrides the default that ignores patch updates for nuget dependencies.
|
||||||
|
matchPackageNames: [
|
||||||
|
"/^Microsoft\\.Extensions\\./",
|
||||||
|
"/^Microsoft\\.AspNetCore\\./",
|
||||||
|
],
|
||||||
|
matchUpdateTypes: ["patch"],
|
||||||
|
dependencyDashboardApproval: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
matchManagers: ["dockerfile", "docker-compose"],
|
matchManagers: ["dockerfile", "docker-compose"],
|
||||||
commitMessagePrefix: "[deps] BRE:",
|
commitMessagePrefix: "[deps] BRE:",
|
||||||
@ -59,6 +68,7 @@
|
|||||||
"DuoUniversal",
|
"DuoUniversal",
|
||||||
"Fido2.AspNet",
|
"Fido2.AspNet",
|
||||||
"Duende.IdentityServer",
|
"Duende.IdentityServer",
|
||||||
|
"Microsoft.AspNetCore.Authentication.JwtBearer",
|
||||||
"Microsoft.Extensions.Identity.Stores",
|
"Microsoft.Extensions.Identity.Stores",
|
||||||
"Otp.NET",
|
"Otp.NET",
|
||||||
"Sustainsys.Saml2.AspNetCore2",
|
"Sustainsys.Saml2.AspNetCore2",
|
||||||
@ -79,8 +89,6 @@
|
|||||||
"CsvHelper",
|
"CsvHelper",
|
||||||
"Kralizek.AutoFixture.Extensions.MockHttp",
|
"Kralizek.AutoFixture.Extensions.MockHttp",
|
||||||
"Microsoft.AspNetCore.Mvc.Testing",
|
"Microsoft.AspNetCore.Mvc.Testing",
|
||||||
"Microsoft.Extensions.Logging",
|
|
||||||
"Microsoft.Extensions.Logging.Console",
|
|
||||||
"Newtonsoft.Json",
|
"Newtonsoft.Json",
|
||||||
"NSubstitute",
|
"NSubstitute",
|
||||||
"Sentry.Serilog",
|
"Sentry.Serilog",
|
||||||
@ -100,9 +108,9 @@
|
|||||||
reviewers: ["team:team-billing-dev"],
|
reviewers: ["team:team-billing-dev"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
matchPackagePatterns: ["^Microsoft.Extensions.Logging"],
|
matchPackageNames: ["/^Microsoft\\.EntityFrameworkCore\\./", "/^dotnet-ef/"],
|
||||||
groupName: "Microsoft.Extensions.Logging",
|
groupName: "EntityFrameworkCore",
|
||||||
description: "Group Microsoft.Extensions.Logging to exclude them from the dotnet monorepo preset",
|
description: "Group EntityFrameworkCore to exclude them from the dotnet monorepo preset",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
matchPackageNames: [
|
matchPackageNames: [
|
||||||
@ -117,9 +125,6 @@
|
|||||||
"Microsoft.EntityFrameworkCore.Relational",
|
"Microsoft.EntityFrameworkCore.Relational",
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite",
|
"Microsoft.EntityFrameworkCore.Sqlite",
|
||||||
"Microsoft.EntityFrameworkCore.SqlServer",
|
"Microsoft.EntityFrameworkCore.SqlServer",
|
||||||
"Microsoft.Extensions.Caching.Cosmos",
|
|
||||||
"Microsoft.Extensions.Caching.SqlServer",
|
|
||||||
"Microsoft.Extensions.Caching.StackExchangeRedis",
|
|
||||||
"Npgsql.EntityFrameworkCore.PostgreSQL",
|
"Npgsql.EntityFrameworkCore.PostgreSQL",
|
||||||
"Pomelo.EntityFrameworkCore.MySql",
|
"Pomelo.EntityFrameworkCore.MySql",
|
||||||
],
|
],
|
||||||
@ -128,8 +133,8 @@
|
|||||||
reviewers: ["team:dept-dbops"],
|
reviewers: ["team:dept-dbops"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
matchPackageNames: ["CommandDotNet", "YamlDotNet"],
|
matchPackageNames: ["YamlDotNet"],
|
||||||
description: "DevOps owned dependencies",
|
description: "BRE owned dependencies",
|
||||||
commitMessagePrefix: "[deps] BRE:",
|
commitMessagePrefix: "[deps] BRE:",
|
||||||
reviewers: ["team:dept-bre"],
|
reviewers: ["team:dept-bre"],
|
||||||
},
|
},
|
||||||
@ -142,56 +147,40 @@
|
|||||||
"Azure.Messaging.ServiceBus",
|
"Azure.Messaging.ServiceBus",
|
||||||
"Azure.Storage.Blobs",
|
"Azure.Storage.Blobs",
|
||||||
"Azure.Storage.Queues",
|
"Azure.Storage.Queues",
|
||||||
"Microsoft.AspNetCore.Authentication.JwtBearer",
|
"LaunchDarkly.ServerSdk",
|
||||||
"Microsoft.AspNetCore.Http",
|
"Microsoft.AspNetCore.Http",
|
||||||
|
"Microsoft.AspNetCore.SignalR.Protocols.MessagePack",
|
||||||
|
"Microsoft.AspNetCore.SignalR.StackExchangeRedis",
|
||||||
|
"Microsoft.Extensions.Configuration.EnvironmentVariables",
|
||||||
|
"Microsoft.Extensions.Configuration.UserSecrets",
|
||||||
|
"Microsoft.Extensions.Configuration",
|
||||||
|
"Microsoft.Extensions.DependencyInjection.Abstractions",
|
||||||
|
"Microsoft.Extensions.DependencyInjection",
|
||||||
|
"Microsoft.Extensions.Logging",
|
||||||
|
"Microsoft.Extensions.Logging.Console",
|
||||||
|
"Microsoft.Extensions.Caching.Cosmos",
|
||||||
|
"Microsoft.Extensions.Caching.SqlServer",
|
||||||
|
"Microsoft.Extensions.Caching.StackExchangeRedis",
|
||||||
"Quartz",
|
"Quartz",
|
||||||
],
|
],
|
||||||
description: "Platform owned dependencies",
|
description: "Platform owned dependencies",
|
||||||
commitMessagePrefix: "[deps] Platform:",
|
commitMessagePrefix: "[deps] Platform:",
|
||||||
reviewers: ["team:team-platform-dev"],
|
reviewers: ["team:team-platform-dev"],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
matchPackagePatterns: ["EntityFrameworkCore", "^dotnet-ef"],
|
|
||||||
groupName: "EntityFrameworkCore",
|
|
||||||
description: "Group EntityFrameworkCore to exclude them from the dotnet monorepo preset",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
matchPackageNames: [
|
matchPackageNames: [
|
||||||
"AutoMapper.Extensions.Microsoft.DependencyInjection",
|
"AutoMapper.Extensions.Microsoft.DependencyInjection",
|
||||||
"AWSSDK.SimpleEmail",
|
"AWSSDK.SimpleEmail",
|
||||||
"AWSSDK.SQS",
|
"AWSSDK.SQS",
|
||||||
"Handlebars.Net",
|
"Handlebars.Net",
|
||||||
"LaunchDarkly.ServerSdk",
|
|
||||||
"MailKit",
|
"MailKit",
|
||||||
"Microsoft.AspNetCore.SignalR.Protocols.MessagePack",
|
|
||||||
"Microsoft.AspNetCore.SignalR.StackExchangeRedis",
|
|
||||||
"Microsoft.Azure.NotificationHubs",
|
"Microsoft.Azure.NotificationHubs",
|
||||||
"Microsoft.Extensions.Configuration.EnvironmentVariables",
|
|
||||||
"Microsoft.Extensions.Configuration.UserSecrets",
|
|
||||||
"Microsoft.Extensions.Configuration",
|
|
||||||
"Microsoft.Extensions.DependencyInjection.Abstractions",
|
|
||||||
"Microsoft.Extensions.DependencyInjection",
|
|
||||||
"SendGrid",
|
"SendGrid",
|
||||||
],
|
],
|
||||||
description: "Tools owned dependencies",
|
description: "Tools owned dependencies",
|
||||||
commitMessagePrefix: "[deps] Tools:",
|
commitMessagePrefix: "[deps] Tools:",
|
||||||
reviewers: ["team:team-tools-dev"],
|
reviewers: ["team:team-tools-dev"],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
matchPackagePatterns: ["^Microsoft.AspNetCore.SignalR"],
|
|
||||||
groupName: "SignalR",
|
|
||||||
description: "Group SignalR to exclude them from the dotnet monorepo preset",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matchPackagePatterns: ["^Microsoft.Extensions.Configuration"],
|
|
||||||
groupName: "Microsoft.Extensions.Configuration",
|
|
||||||
description: "Group Microsoft.Extensions.Configuration to exclude them from the dotnet monorepo preset",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
matchPackagePatterns: ["^Microsoft.Extensions.DependencyInjection"],
|
|
||||||
groupName: "Microsoft.Extensions.DependencyInjection",
|
|
||||||
description: "Group Microsoft.Extensions.DependencyInjection to exclude them from the dotnet monorepo preset",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
matchPackageNames: [
|
matchPackageNames: [
|
||||||
"AngleSharp",
|
"AngleSharp",
|
||||||
|
235
.github/workflows/build.yml
vendored
235
.github/workflows/build.yml
vendored
@ -12,13 +12,17 @@ on:
|
|||||||
workflow_call:
|
workflow_call:
|
||||||
inputs: {}
|
inputs: {}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
env:
|
env:
|
||||||
_AZ_REGISTRY: "bitwardenprod.azurecr.io"
|
_AZ_REGISTRY: "bitwardenprod.azurecr.io"
|
||||||
|
_GITHUB_PR_REPO_NAME: ${{ github.event.pull_request.head.repo.full_name }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
name: Lint
|
name: Lint
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
@ -32,115 +36,15 @@ jobs:
|
|||||||
run: dotnet format --verify-no-changes
|
run: dotnet format --verify-no-changes
|
||||||
|
|
||||||
build-artifacts:
|
build-artifacts:
|
||||||
name: Build artifacts
|
name: Build Docker images
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- lint
|
- lint
|
||||||
outputs:
|
outputs:
|
||||||
has_secrets: ${{ steps.check-secrets.outputs.has_secrets }}
|
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:
|
permissions:
|
||||||
security-events: write
|
security-events: write
|
||||||
id-token: write
|
id-token: write
|
||||||
needs:
|
|
||||||
- build-artifacts
|
|
||||||
if: ${{ needs.build-artifacts.outputs.has_secrets == 'true' }}
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -148,6 +52,7 @@ jobs:
|
|||||||
- project_name: Admin
|
- project_name: Admin
|
||||||
base_path: ./src
|
base_path: ./src
|
||||||
dotnet: true
|
dotnet: true
|
||||||
|
node: true
|
||||||
- project_name: Api
|
- project_name: Api
|
||||||
base_path: ./src
|
base_path: ./src
|
||||||
dotnet: true
|
dotnet: true
|
||||||
@ -181,9 +86,6 @@ jobs:
|
|||||||
- project_name: Scim
|
- project_name: Scim
|
||||||
base_path: ./bitwarden_license/src
|
base_path: ./bitwarden_license/src
|
||||||
dotnet: true
|
dotnet: true
|
||||||
- project_name: Server
|
|
||||||
base_path: ./util
|
|
||||||
dotnet: true
|
|
||||||
- project_name: Setup
|
- project_name: Setup
|
||||||
base_path: ./util
|
base_path: ./util
|
||||||
dotnet: true
|
dotnet: true
|
||||||
@ -191,6 +93,14 @@ jobs:
|
|||||||
base_path: ./bitwarden_license/src
|
base_path: ./bitwarden_license/src
|
||||||
dotnet: true
|
dotnet: true
|
||||||
steps:
|
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
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
@ -202,13 +112,67 @@ jobs:
|
|||||||
id: publish-branch-check
|
id: publish-branch-check
|
||||||
run: |
|
run: |
|
||||||
IFS="," read -a publish_branches <<< $PUBLISH_BRANCHES
|
IFS="," read -a publish_branches <<< $PUBLISH_BRANCHES
|
||||||
|
|
||||||
if [[ " ${publish_branches[*]} " =~ " ${GITHUB_REF:11} " ]]; then
|
if [[ " ${publish_branches[*]} " =~ " ${GITHUB_REF:11} " ]]; then
|
||||||
echo "is_publish_branch=true" >> $GITHUB_ENV
|
echo "is_publish_branch=true" >> $GITHUB_ENV
|
||||||
else
|
else
|
||||||
echo "is_publish_branch=false" >> $GITHUB_ENV
|
echo "is_publish_branch=false" >> $GITHUB_ENV
|
||||||
fi
|
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 ##########
|
########## ACRs ##########
|
||||||
- name: Log in to Azure - production subscription
|
- name: Log in to Azure - production subscription
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||||
@ -234,12 +198,18 @@ jobs:
|
|||||||
- name: Generate Docker image tag
|
- name: Generate Docker image tag
|
||||||
id: tag
|
id: tag
|
||||||
run: |
|
run: |
|
||||||
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
|
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" || "${GITHUB_EVENT_NAME}" == "pull_request_target" ]]; then
|
||||||
IMAGE_TAG=$(echo "${GITHUB_HEAD_REF}" | sed "s#/#-#g")
|
IMAGE_TAG=$(echo "${GITHUB_HEAD_REF}" | sed "s/[^a-zA-Z0-9]/-/g") # Sanitize branch name to alphanumeric only
|
||||||
else
|
else
|
||||||
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g")
|
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
||||||
|
SANITIZED_REPO_NAME=$(echo "$_GITHUB_PR_REPO_NAME" | sed "s/[^a-zA-Z0-9]/-/g") # Sanitize repo name to alphanumeric only
|
||||||
|
IMAGE_TAG=$SANITIZED_REPO_NAME-$IMAGE_TAG # Add repo name to the tag
|
||||||
|
IMAGE_TAG=${IMAGE_TAG:0:128} # Limit to 128 characters, as that's the max length for Docker image tags
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$IMAGE_TAG" == "main" ]]; then
|
if [[ "$IMAGE_TAG" == "main" ]]; then
|
||||||
IMAGE_TAG=dev
|
IMAGE_TAG=dev
|
||||||
fi
|
fi
|
||||||
@ -270,26 +240,16 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
echo "tags=$TAGS" >> $GITHUB_OUTPUT
|
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
|
- name: Build Docker image
|
||||||
id: build-docker
|
id: build-artifacts
|
||||||
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
|
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
|
||||||
with:
|
with:
|
||||||
context: ${{ matrix.base_path }}/${{ matrix.project_name }}
|
context: .
|
||||||
file: ${{ matrix.base_path }}/${{ matrix.project_name }}/Dockerfile
|
file: ${{ matrix.base_path }}/${{ matrix.project_name }}/Dockerfile
|
||||||
platforms: linux/amd64
|
platforms: |
|
||||||
|
linux/amd64,
|
||||||
|
linux/arm/v7,
|
||||||
|
linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.image-tags.outputs.tags }}
|
tags: ${{ steps.image-tags.outputs.tags }}
|
||||||
secrets: |
|
secrets: |
|
||||||
@ -302,7 +262,7 @@ jobs:
|
|||||||
- name: Sign image with Cosign
|
- name: Sign image with Cosign
|
||||||
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
||||||
env:
|
env:
|
||||||
DIGEST: ${{ steps.build-docker.outputs.digest }}
|
DIGEST: ${{ steps.build-artifacts.outputs.digest }}
|
||||||
TAGS: ${{ steps.image-tags.outputs.tags }}
|
TAGS: ${{ steps.image-tags.outputs.tags }}
|
||||||
run: |
|
run: |
|
||||||
IFS="," read -a tags <<< "${TAGS}"
|
IFS="," read -a tags <<< "${TAGS}"
|
||||||
@ -329,8 +289,8 @@ jobs:
|
|||||||
|
|
||||||
upload:
|
upload:
|
||||||
name: Upload
|
name: Upload
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
needs: build-docker
|
needs: build-artifacts
|
||||||
steps:
|
steps:
|
||||||
- name: Check out repo
|
- name: Check out repo
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
@ -370,9 +330,9 @@ jobs:
|
|||||||
|
|
||||||
# Run setup
|
# Run setup
|
||||||
docker run -i --rm --name setup -v $STUB_OUTPUT/US:/bitwarden $SETUP_IMAGE \
|
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 \
|
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
|
sudo chown -R $(whoami):$(whoami) $STUB_OUTPUT
|
||||||
|
|
||||||
@ -505,7 +465,7 @@ jobs:
|
|||||||
|
|
||||||
build-mssqlmigratorutility:
|
build-mssqlmigratorutility:
|
||||||
name: Build MSSQL migrator utility
|
name: Build MSSQL migrator utility
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- lint
|
- lint
|
||||||
defaults:
|
defaults:
|
||||||
@ -561,9 +521,9 @@ jobs:
|
|||||||
if: |
|
if: |
|
||||||
github.event_name != 'pull_request'
|
github.event_name != 'pull_request'
|
||||||
&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
|
&& (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:
|
needs:
|
||||||
- build-docker
|
- build-artifacts
|
||||||
steps:
|
steps:
|
||||||
- name: Log in to Azure - CI subscription
|
- name: Log in to Azure - CI subscription
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||||
@ -597,7 +557,7 @@ jobs:
|
|||||||
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs:
|
needs:
|
||||||
- build-docker
|
- build-artifacts
|
||||||
steps:
|
steps:
|
||||||
- name: Log in to Azure - CI subscription
|
- name: Log in to Azure - CI subscription
|
||||||
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
|
||||||
@ -629,7 +589,8 @@ jobs:
|
|||||||
|
|
||||||
setup-ephemeral-environment:
|
setup-ephemeral-environment:
|
||||||
name: Setup Ephemeral Environment
|
name: Setup Ephemeral Environment
|
||||||
needs: build-docker
|
needs:
|
||||||
|
- build-artifacts
|
||||||
if: |
|
if: |
|
||||||
needs.build-artifacts.outputs.has_secrets == 'true'
|
needs.build-artifacts.outputs.has_secrets == 'true'
|
||||||
&& github.event_name == 'pull_request'
|
&& github.event_name == 'pull_request'
|
||||||
@ -637,8 +598,9 @@ jobs:
|
|||||||
uses: bitwarden/gh-actions/.github/workflows/_ephemeral_environment_manager.yml@main
|
uses: bitwarden/gh-actions/.github/workflows/_ephemeral_environment_manager.yml@main
|
||||||
with:
|
with:
|
||||||
project: server
|
project: server
|
||||||
pull_request_number: ${{ github.event.number }}
|
pull_request_number: ${{ github.event.number || 0 }}
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
permissions: read-all
|
||||||
|
|
||||||
check-failures:
|
check-failures:
|
||||||
name: Check for failures
|
name: Check for failures
|
||||||
@ -647,7 +609,6 @@ jobs:
|
|||||||
needs:
|
needs:
|
||||||
- lint
|
- lint
|
||||||
- build-artifacts
|
- build-artifacts
|
||||||
- build-docker
|
|
||||||
- upload
|
- upload
|
||||||
- build-mssqlmigratorutility
|
- build-mssqlmigratorutility
|
||||||
- self-host-build
|
- self-host-build
|
||||||
|
4
.github/workflows/build_target.yml
vendored
4
.github/workflows/build_target.yml
vendored
@ -2,7 +2,9 @@ name: Build on PR Target
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [opened, synchronize]
|
types: [opened, synchronize, reopened]
|
||||||
|
branches:
|
||||||
|
- "main"
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
1
.github/workflows/code-references.yml
vendored
1
.github/workflows/code-references.yml
vendored
@ -44,6 +44,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
accessToken: ${{ secrets.LD_ACCESS_TOKEN }}
|
accessToken: ${{ secrets.LD_ACCESS_TOKEN }}
|
||||||
projKey: default
|
projKey: default
|
||||||
|
allowTags: true
|
||||||
|
|
||||||
- name: Add label
|
- name: Add label
|
||||||
if: steps.collect.outputs.any-changed == 'true'
|
if: steps.collect.outputs.any-changed == 'true'
|
||||||
|
8
.github/workflows/scan.yml
vendored
8
.github/workflows/scan.yml
vendored
@ -7,8 +7,14 @@ on:
|
|||||||
- "main"
|
- "main"
|
||||||
- "rc"
|
- "rc"
|
||||||
- "hotfix-rc"
|
- "hotfix-rc"
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
branches-ignore:
|
||||||
|
- main
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
types: [opened, synchronize]
|
types: [opened, synchronize, reopened]
|
||||||
|
branches:
|
||||||
|
- "main"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-run:
|
check-run:
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
|
||||||
<Version>2025.5.0</Version>
|
<Version>2025.6.0</Version>
|
||||||
|
|
||||||
<RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace>
|
<RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
@ -69,5 +69,4 @@
|
|||||||
</AssemblyAttribute>
|
</AssemblyAttribute>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
@ -5,9 +5,6 @@
|
|||||||
<a href="https://github.com/bitwarden/server/actions/workflows/build.yml?query=branch:main" target="_blank">
|
<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" />
|
<img src="https://github.com/bitwarden/server/actions/workflows/build.yml/badge.svg?branch=main" alt="Github Workflow build on main" />
|
||||||
</a>
|
</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">
|
<a href="https://gitter.im/bitwarden/Lobby" target="_blank">
|
||||||
<img src="https://badges.gitter.im/bitwarden/Lobby.svg" alt="gitter chat" />
|
<img src="https://badges.gitter.im/bitwarden/Lobby.svg" alt="gitter chat" />
|
||||||
</a>
|
</a>
|
||||||
@ -26,12 +23,12 @@ Please refer to the [Server Setup Guide](https://contributing.bitwarden.com/gett
|
|||||||
## Deploy
|
## Deploy
|
||||||
|
|
||||||
<p align="center">
|
<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" />
|
<img src="https://i.imgur.com/SZc8JnH.png" alt="docker" />
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</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/
|
Full documentation for deploying Bitwarden with Docker can be found in our help center at: https://help.bitwarden.com/article/install-on-premise/
|
||||||
|
|
||||||
|
@ -129,6 +129,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Events.IntegrationTest", "t
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.IntegrationTest", "test\Core.IntegrationTest\Core.IntegrationTest.csproj", "{3631BA42-6731-4118-A917-DAA43C5032B9}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.IntegrationTest", "test\Core.IntegrationTest\Core.IntegrationTest.csproj", "{3631BA42-6731-4118-A917-DAA43C5032B9}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Seeder", "util\Seeder\Seeder.csproj", "{9A612EBA-1C0E-42B8-982B-62F0EE81000A}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DbSeederUtility", "util\DbSeederUtility\DbSeederUtility.csproj", "{17A89266-260A-4A03-81AE-C0468C6EE06E}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -325,6 +329,14 @@ Global
|
|||||||
{3631BA42-6731-4118-A917-DAA43C5032B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{3631BA42-6731-4118-A917-DAA43C5032B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{3631BA42-6731-4118-A917-DAA43C5032B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{3631BA42-6731-4118-A917-DAA43C5032B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{3631BA42-6731-4118-A917-DAA43C5032B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
{3631BA42-6731-4118-A917-DAA43C5032B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9A612EBA-1C0E-42B8-982B-62F0EE81000A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9A612EBA-1C0E-42B8-982B-62F0EE81000A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9A612EBA-1C0E-42B8-982B-62F0EE81000A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9A612EBA-1C0E-42B8-982B-62F0EE81000A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{17A89266-260A-4A03-81AE-C0468C6EE06E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{17A89266-260A-4A03-81AE-C0468C6EE06E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{17A89266-260A-4A03-81AE-C0468C6EE06E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{17A89266-260A-4A03-81AE-C0468C6EE06E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -377,6 +389,8 @@ Global
|
|||||||
{4A725DB3-BE4F-4C23-9087-82D0610D67AF} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
{4A725DB3-BE4F-4C23-9087-82D0610D67AF} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
||||||
{4F4C63A9-AEE2-48C4-AB86-A5BCD665E401} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
{4F4C63A9-AEE2-48C4-AB86-A5BCD665E401} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
||||||
{3631BA42-6731-4118-A917-DAA43C5032B9} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
{3631BA42-6731-4118-A917-DAA43C5032B9} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
|
||||||
|
{9A612EBA-1C0E-42B8-982B-62F0EE81000A} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
|
||||||
|
{17A89266-260A-4A03-81AE-C0468C6EE06E} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {E01CBF68-2E20-425F-9EDB-E0A6510CA92F}
|
SolutionGuid = {E01CBF68-2E20-425F-9EDB-E0A6510CA92F}
|
||||||
|
@ -3,9 +3,9 @@ using Bit.Core.AdminConsole.Enums.Provider;
|
|||||||
using Bit.Core.AdminConsole.Providers.Interfaces;
|
using Bit.Core.AdminConsole.Providers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
using Bit.Core.Billing.Entities;
|
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Repositories;
|
using Bit.Core.Billing.Providers.Entities;
|
||||||
|
using Bit.Core.Billing.Providers.Repositories;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
@ -7,13 +7,12 @@ using Bit.Core.AdminConsole.Repositories;
|
|||||||
using Bit.Core.Billing.Constants;
|
using Bit.Core.Billing.Constants;
|
||||||
using Bit.Core.Billing.Extensions;
|
using Bit.Core.Billing.Extensions;
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
|
using Bit.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Services;
|
||||||
using Bit.Core.Billing.Services.Implementations.AutomaticTax;
|
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Stripe;
|
using Stripe;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.AdminConsole.Providers;
|
namespace Bit.Commercial.Core.AdminConsole.Providers;
|
||||||
@ -23,7 +22,6 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv
|
|||||||
private readonly IEventService _eventService;
|
private readonly IEventService _eventService;
|
||||||
private readonly IMailService _mailService;
|
private readonly IMailService _mailService;
|
||||||
private readonly IOrganizationRepository _organizationRepository;
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly IOrganizationService _organizationService;
|
|
||||||
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
|
private readonly IProviderOrganizationRepository _providerOrganizationRepository;
|
||||||
private readonly IStripeAdapter _stripeAdapter;
|
private readonly IStripeAdapter _stripeAdapter;
|
||||||
private readonly IFeatureService _featureService;
|
private readonly IFeatureService _featureService;
|
||||||
@ -31,26 +29,22 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv
|
|||||||
private readonly ISubscriberService _subscriberService;
|
private readonly ISubscriberService _subscriberService;
|
||||||
private readonly IHasConfirmedOwnersExceptQuery _hasConfirmedOwnersExceptQuery;
|
private readonly IHasConfirmedOwnersExceptQuery _hasConfirmedOwnersExceptQuery;
|
||||||
private readonly IPricingClient _pricingClient;
|
private readonly IPricingClient _pricingClient;
|
||||||
private readonly IAutomaticTaxStrategy _automaticTaxStrategy;
|
|
||||||
|
|
||||||
public RemoveOrganizationFromProviderCommand(
|
public RemoveOrganizationFromProviderCommand(
|
||||||
IEventService eventService,
|
IEventService eventService,
|
||||||
IMailService mailService,
|
IMailService mailService,
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
IOrganizationService organizationService,
|
|
||||||
IProviderOrganizationRepository providerOrganizationRepository,
|
IProviderOrganizationRepository providerOrganizationRepository,
|
||||||
IStripeAdapter stripeAdapter,
|
IStripeAdapter stripeAdapter,
|
||||||
IFeatureService featureService,
|
IFeatureService featureService,
|
||||||
IProviderBillingService providerBillingService,
|
IProviderBillingService providerBillingService,
|
||||||
ISubscriberService subscriberService,
|
ISubscriberService subscriberService,
|
||||||
IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery,
|
IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery,
|
||||||
IPricingClient pricingClient,
|
IPricingClient pricingClient)
|
||||||
[FromKeyedServices(AutomaticTaxFactory.BusinessUse)] IAutomaticTaxStrategy automaticTaxStrategy)
|
|
||||||
{
|
{
|
||||||
_eventService = eventService;
|
_eventService = eventService;
|
||||||
_mailService = mailService;
|
_mailService = mailService;
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_organizationService = organizationService;
|
|
||||||
_providerOrganizationRepository = providerOrganizationRepository;
|
_providerOrganizationRepository = providerOrganizationRepository;
|
||||||
_stripeAdapter = stripeAdapter;
|
_stripeAdapter = stripeAdapter;
|
||||||
_featureService = featureService;
|
_featureService = featureService;
|
||||||
@ -58,7 +52,6 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv
|
|||||||
_subscriberService = subscriberService;
|
_subscriberService = subscriberService;
|
||||||
_hasConfirmedOwnersExceptQuery = hasConfirmedOwnersExceptQuery;
|
_hasConfirmedOwnersExceptQuery = hasConfirmedOwnersExceptQuery;
|
||||||
_pricingClient = pricingClient;
|
_pricingClient = pricingClient;
|
||||||
_automaticTaxStrategy = automaticTaxStrategy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemoveOrganizationFromProvider(
|
public async Task RemoveOrganizationFromProvider(
|
||||||
@ -76,7 +69,7 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv
|
|||||||
|
|
||||||
if (!await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(
|
if (!await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(
|
||||||
providerOrganization.OrganizationId,
|
providerOrganization.OrganizationId,
|
||||||
Array.Empty<Guid>(),
|
[],
|
||||||
includeProvider: false))
|
includeProvider: false))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||||
@ -101,7 +94,7 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// When a client organization is unlinked from a provider, we have to check if they're Stripe-enabled
|
/// When a client organization is unlinked from a provider, we have to check if they're Stripe-enabled
|
||||||
/// and, if they are, we remove their MSP discount and set their Subscription to `send_invoice`. This is because
|
/// and, if they are, we remove their MSP discount and set their Subscription to `send_invoice`. This is because
|
||||||
/// the provider's payment method will be removed from their Stripe customer causing ensuing charges to fail. Lastly,
|
/// the provider's payment method will be removed from their Stripe customer, causing ensuing charges to fail. Lastly,
|
||||||
/// we email the organization owners letting them know they need to add a new payment method.
|
/// we email the organization owners letting them know they need to add a new payment method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async Task ResetOrganizationBillingAsync(
|
private async Task ResetOrganizationBillingAsync(
|
||||||
@ -141,15 +134,18 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv
|
|||||||
Items = [new SubscriptionItemOptions { Price = plan.PasswordManager.StripeSeatPlanId, Quantity = organization.Seats }]
|
Items = [new SubscriptionItemOptions { Price = plan.PasswordManager.StripeSeatPlanId, Quantity = organization.Seats }]
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.PM19147_AutomaticTaxImprovements))
|
var setNonUSBusinessUseToReverseCharge = _featureService.IsEnabled(FeatureFlagKeys.PM21092_SetNonUSBusinessUseToReverseCharge);
|
||||||
|
|
||||||
|
if (setNonUSBusinessUseToReverseCharge)
|
||||||
{
|
{
|
||||||
_automaticTaxStrategy.SetCreateOptions(subscriptionCreateOptions, customer);
|
subscriptionCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true };
|
||||||
}
|
}
|
||||||
else
|
else if (customer.HasRecognizedTaxLocation())
|
||||||
{
|
{
|
||||||
subscriptionCreateOptions.AutomaticTax ??= new SubscriptionAutomaticTaxOptions
|
subscriptionCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions
|
||||||
{
|
{
|
||||||
Enabled = true
|
Enabled = customer.Address.Country == "US" ||
|
||||||
|
customer.TaxIds.Any()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +182,7 @@ public class RemoveOrganizationFromProviderCommand : IRemoveOrganizationFromProv
|
|||||||
await _mailService.SendProviderUpdatePaymentMethod(
|
await _mailService.SendProviderUpdatePaymentMethod(
|
||||||
organization.Id,
|
organization.Id,
|
||||||
organization.Name,
|
organization.Name,
|
||||||
provider.Name,
|
provider.Name!,
|
||||||
organizationOwnerEmails);
|
organizationOwnerEmails);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,13 @@ using Bit.Core.AdminConsole.Entities.Provider;
|
|||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Models.Business.Provider;
|
using Bit.Core.AdminConsole.Models.Business.Provider;
|
||||||
using Bit.Core.AdminConsole.Models.Business.Tokenables;
|
using Bit.Core.AdminConsole.Models.Business.Tokenables;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Models;
|
using Bit.Core.Billing.Models;
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -53,6 +54,7 @@ public class ProviderService : IProviderService
|
|||||||
private readonly IApplicationCacheService _applicationCacheService;
|
private readonly IApplicationCacheService _applicationCacheService;
|
||||||
private readonly IProviderBillingService _providerBillingService;
|
private readonly IProviderBillingService _providerBillingService;
|
||||||
private readonly IPricingClient _pricingClient;
|
private readonly IPricingClient _pricingClient;
|
||||||
|
private readonly IProviderClientOrganizationSignUpCommand _providerClientOrganizationSignUpCommand;
|
||||||
|
|
||||||
public ProviderService(IProviderRepository providerRepository, IProviderUserRepository providerUserRepository,
|
public ProviderService(IProviderRepository providerRepository, IProviderUserRepository providerUserRepository,
|
||||||
IProviderOrganizationRepository providerOrganizationRepository, IUserRepository userRepository,
|
IProviderOrganizationRepository providerOrganizationRepository, IUserRepository userRepository,
|
||||||
@ -61,7 +63,8 @@ public class ProviderService : IProviderService
|
|||||||
IOrganizationRepository organizationRepository, GlobalSettings globalSettings,
|
IOrganizationRepository organizationRepository, GlobalSettings globalSettings,
|
||||||
ICurrentContext currentContext, IStripeAdapter stripeAdapter, IFeatureService featureService,
|
ICurrentContext currentContext, IStripeAdapter stripeAdapter, IFeatureService featureService,
|
||||||
IDataProtectorTokenFactory<ProviderDeleteTokenable> providerDeleteTokenDataFactory,
|
IDataProtectorTokenFactory<ProviderDeleteTokenable> providerDeleteTokenDataFactory,
|
||||||
IApplicationCacheService applicationCacheService, IProviderBillingService providerBillingService, IPricingClient pricingClient)
|
IApplicationCacheService applicationCacheService, IProviderBillingService providerBillingService, IPricingClient pricingClient,
|
||||||
|
IProviderClientOrganizationSignUpCommand providerClientOrganizationSignUpCommand)
|
||||||
{
|
{
|
||||||
_providerRepository = providerRepository;
|
_providerRepository = providerRepository;
|
||||||
_providerUserRepository = providerUserRepository;
|
_providerUserRepository = providerUserRepository;
|
||||||
@ -81,6 +84,7 @@ public class ProviderService : IProviderService
|
|||||||
_applicationCacheService = applicationCacheService;
|
_applicationCacheService = applicationCacheService;
|
||||||
_providerBillingService = providerBillingService;
|
_providerBillingService = providerBillingService;
|
||||||
_pricingClient = pricingClient;
|
_pricingClient = pricingClient;
|
||||||
|
_providerClientOrganizationSignUpCommand = providerClientOrganizationSignUpCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Provider> CompleteSetupAsync(Provider provider, Guid ownerUserId, string token, string key, TaxInfo taxInfo, TokenizedPaymentSource tokenizedPaymentSource = null)
|
public async Task<Provider> CompleteSetupAsync(Provider provider, Guid ownerUserId, string token, string key, TaxInfo taxInfo, TokenizedPaymentSource tokenizedPaymentSource = null)
|
||||||
@ -283,11 +287,10 @@ public class ProviderService : IProviderService
|
|||||||
|
|
||||||
foreach (var user in users)
|
foreach (var user in users)
|
||||||
{
|
{
|
||||||
if (!keyedFilteredUsers.ContainsKey(user.Id))
|
if (!keyedFilteredUsers.TryGetValue(user.Id, out var providerUser))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var providerUser = keyedFilteredUsers[user.Id];
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (providerUser.Status != ProviderUserStatusType.Accepted || providerUser.ProviderId != providerId)
|
if (providerUser.Status != ProviderUserStatusType.Accepted || providerUser.ProviderId != providerId)
|
||||||
@ -560,12 +563,12 @@ public class ProviderService : IProviderService
|
|||||||
|
|
||||||
ThrowOnInvalidPlanType(provider.Type, organizationSignup.Plan);
|
ThrowOnInvalidPlanType(provider.Type, organizationSignup.Plan);
|
||||||
|
|
||||||
var (organization, _, defaultCollection) = await _organizationService.SignupClientAsync(organizationSignup);
|
var signUpResponse = await _providerClientOrganizationSignUpCommand.SignUpClientOrganizationAsync(organizationSignup);
|
||||||
|
|
||||||
var providerOrganization = new ProviderOrganization
|
var providerOrganization = new ProviderOrganization
|
||||||
{
|
{
|
||||||
ProviderId = providerId,
|
ProviderId = providerId,
|
||||||
OrganizationId = organization.Id,
|
OrganizationId = signUpResponse.Organization.Id,
|
||||||
Key = organizationSignup.OwnerKey,
|
Key = organizationSignup.OwnerKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -574,12 +577,12 @@ public class ProviderService : IProviderService
|
|||||||
|
|
||||||
// Give the owner Can Manage access over the default collection
|
// Give the owner Can Manage access over the default collection
|
||||||
// The orgUser is not available when the org is created so we have to do it here as part of the invite
|
// The orgUser is not available when the org is created so we have to do it here as part of the invite
|
||||||
var defaultOwnerAccess = defaultCollection != null
|
var defaultOwnerAccess = signUpResponse.DefaultCollection != null
|
||||||
?
|
?
|
||||||
[
|
[
|
||||||
new CollectionAccessSelection
|
new CollectionAccessSelection
|
||||||
{
|
{
|
||||||
Id = defaultCollection.Id,
|
Id = signUpResponse.DefaultCollection.Id,
|
||||||
HidePasswords = false,
|
HidePasswords = false,
|
||||||
ReadOnly = false,
|
ReadOnly = false,
|
||||||
Manage = true
|
Manage = true
|
||||||
@ -587,7 +590,7 @@ public class ProviderService : IProviderService
|
|||||||
]
|
]
|
||||||
: Array.Empty<CollectionAccessSelection>();
|
: Array.Empty<CollectionAccessSelection>();
|
||||||
|
|
||||||
await _organizationService.InviteUsersAsync(organization.Id, user.Id, systemUser: null,
|
await _organizationService.InviteUsersAsync(signUpResponse.Organization.Id, user.Id, systemUser: null,
|
||||||
new (OrganizationUserInvite, string)[]
|
new (OrganizationUserInvite, string)[]
|
||||||
{
|
{
|
||||||
(
|
(
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Bit.Core.Billing.Entities;
|
using Bit.Core.Billing.Providers.Entities;
|
||||||
using CsvHelper.Configuration.Attributes;
|
using CsvHelper.Configuration.Attributes;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.Billing.Models;
|
namespace Bit.Commercial.Core.Billing.Providers.Models;
|
||||||
|
|
||||||
public class ProviderClientInvoiceReportRow
|
public class ProviderClientInvoiceReportRow
|
||||||
{
|
{
|
@ -1,17 +1,17 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Billing;
|
using Bit.Core.Billing;
|
||||||
using Bit.Core.Billing.Constants;
|
using Bit.Core.Billing.Constants;
|
||||||
using Bit.Core.Billing.Entities;
|
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Extensions;
|
using Bit.Core.Billing.Extensions;
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Billing.Repositories;
|
using Bit.Core.Billing.Providers.Entities;
|
||||||
|
using Bit.Core.Billing.Providers.Repositories;
|
||||||
|
using Bit.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Services;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -24,9 +24,8 @@ using Microsoft.Extensions.Logging;
|
|||||||
using OneOf;
|
using OneOf;
|
||||||
using Stripe;
|
using Stripe;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.Billing;
|
namespace Bit.Commercial.Core.Billing.Providers.Services;
|
||||||
|
|
||||||
[RequireFeature(FeatureFlagKeys.PM18770_EnableOrganizationBusinessUnitConversion)]
|
|
||||||
public class BusinessUnitConverter(
|
public class BusinessUnitConverter(
|
||||||
IDataProtectionProvider dataProtectionProvider,
|
IDataProtectionProvider dataProtectionProvider,
|
||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings,
|
||||||
@ -67,6 +66,7 @@ public class BusinessUnitConverter(
|
|||||||
organization.MaxStorageGb = updatedPlan.PasswordManager.BaseStorageGb;
|
organization.MaxStorageGb = updatedPlan.PasswordManager.BaseStorageGb;
|
||||||
organization.UsePolicies = updatedPlan.HasPolicies;
|
organization.UsePolicies = updatedPlan.HasPolicies;
|
||||||
organization.UseSso = updatedPlan.HasSso;
|
organization.UseSso = updatedPlan.HasSso;
|
||||||
|
organization.UseOrganizationDomains = updatedPlan.HasOrganizationDomains;
|
||||||
organization.UseGroups = updatedPlan.HasGroups;
|
organization.UseGroups = updatedPlan.HasGroups;
|
||||||
organization.UseEvents = updatedPlan.HasEvents;
|
organization.UseEvents = updatedPlan.HasEvents;
|
||||||
organization.UseDirectory = updatedPlan.HasDirectory;
|
organization.UseDirectory = updatedPlan.HasDirectory;
|
@ -1,5 +1,5 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Bit.Commercial.Core.Billing.Models;
|
using Bit.Commercial.Core.Billing.Providers.Models;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
@ -8,15 +8,17 @@ using Bit.Core.AdminConsole.Repositories;
|
|||||||
using Bit.Core.Billing;
|
using Bit.Core.Billing;
|
||||||
using Bit.Core.Billing.Caches;
|
using Bit.Core.Billing.Caches;
|
||||||
using Bit.Core.Billing.Constants;
|
using Bit.Core.Billing.Constants;
|
||||||
using Bit.Core.Billing.Entities;
|
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Extensions;
|
using Bit.Core.Billing.Extensions;
|
||||||
using Bit.Core.Billing.Models;
|
using Bit.Core.Billing.Models;
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Billing.Repositories;
|
using Bit.Core.Billing.Providers.Entities;
|
||||||
|
using Bit.Core.Billing.Providers.Models;
|
||||||
|
using Bit.Core.Billing.Providers.Repositories;
|
||||||
|
using Bit.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Services;
|
||||||
using Bit.Core.Billing.Services.Contracts;
|
using Bit.Core.Billing.Tax.Models;
|
||||||
using Bit.Core.Billing.Services.Implementations.AutomaticTax;
|
using Bit.Core.Billing.Tax.Services;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
@ -25,15 +27,13 @@ using Bit.Core.Services;
|
|||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
using Braintree;
|
using Braintree;
|
||||||
using CsvHelper;
|
using CsvHelper;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Stripe;
|
using Stripe;
|
||||||
|
|
||||||
using static Bit.Core.Billing.Utilities;
|
using static Bit.Core.Billing.Utilities;
|
||||||
using Customer = Stripe.Customer;
|
using Customer = Stripe.Customer;
|
||||||
using Subscription = Stripe.Subscription;
|
using Subscription = Stripe.Subscription;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.Billing;
|
namespace Bit.Commercial.Core.Billing.Providers.Services;
|
||||||
|
|
||||||
public class ProviderBillingService(
|
public class ProviderBillingService(
|
||||||
IBraintreeGateway braintreeGateway,
|
IBraintreeGateway braintreeGateway,
|
||||||
@ -50,8 +50,7 @@ public class ProviderBillingService(
|
|||||||
ISetupIntentCache setupIntentCache,
|
ISetupIntentCache setupIntentCache,
|
||||||
IStripeAdapter stripeAdapter,
|
IStripeAdapter stripeAdapter,
|
||||||
ISubscriberService subscriberService,
|
ISubscriberService subscriberService,
|
||||||
ITaxService taxService,
|
ITaxService taxService)
|
||||||
[FromKeyedServices(AutomaticTaxFactory.BusinessUse)] IAutomaticTaxStrategy automaticTaxStrategy)
|
|
||||||
: IProviderBillingService
|
: IProviderBillingService
|
||||||
{
|
{
|
||||||
public async Task AddExistingOrganization(
|
public async Task AddExistingOrganization(
|
||||||
@ -97,6 +96,7 @@ public class ProviderBillingService(
|
|||||||
organization.MaxStorageGb = plan.PasswordManager.BaseStorageGb;
|
organization.MaxStorageGb = plan.PasswordManager.BaseStorageGb;
|
||||||
organization.UsePolicies = plan.HasPolicies;
|
organization.UsePolicies = plan.HasPolicies;
|
||||||
organization.UseSso = plan.HasSso;
|
organization.UseSso = plan.HasSso;
|
||||||
|
organization.UseOrganizationDomains = plan.HasOrganizationDomains;
|
||||||
organization.UseGroups = plan.HasGroups;
|
organization.UseGroups = plan.HasGroups;
|
||||||
organization.UseEvents = plan.HasEvents;
|
organization.UseEvents = plan.HasEvents;
|
||||||
organization.UseDirectory = plan.HasDirectory;
|
organization.UseDirectory = plan.HasDirectory;
|
||||||
@ -125,7 +125,7 @@ public class ProviderBillingService(
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* We have to scale the provider's seats before the ProviderOrganization
|
* We have to scale the provider's seats before the ProviderOrganization
|
||||||
* row is inserted so the added organization's seats don't get double counted.
|
* row is inserted so the added organization's seats don't get double-counted.
|
||||||
*/
|
*/
|
||||||
await ScaleSeats(provider, organization.PlanType, organization.Seats!.Value);
|
await ScaleSeats(provider, organization.PlanType, organization.Seats!.Value);
|
||||||
|
|
||||||
@ -233,7 +233,7 @@ public class ProviderBillingService(
|
|||||||
|
|
||||||
var providerCustomer = await subscriberService.GetCustomerOrThrow(provider, new CustomerGetOptions
|
var providerCustomer = await subscriberService.GetCustomerOrThrow(provider, new CustomerGetOptions
|
||||||
{
|
{
|
||||||
Expand = ["tax_ids"]
|
Expand = ["tax", "tax_ids"]
|
||||||
});
|
});
|
||||||
|
|
||||||
var providerTaxId = providerCustomer.TaxIds.FirstOrDefault();
|
var providerTaxId = providerCustomer.TaxIds.FirstOrDefault();
|
||||||
@ -281,6 +281,13 @@ public class ProviderBillingService(
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var setNonUSBusinessUseToReverseCharge = featureService.IsEnabled(FeatureFlagKeys.PM21092_SetNonUSBusinessUseToReverseCharge);
|
||||||
|
|
||||||
|
if (setNonUSBusinessUseToReverseCharge && providerCustomer.Address is not { Country: "US" })
|
||||||
|
{
|
||||||
|
customerCreateOptions.TaxExempt = StripeConstants.TaxExempt.Reverse;
|
||||||
|
}
|
||||||
|
|
||||||
var customer = await stripeAdapter.CustomerCreateAsync(customerCreateOptions);
|
var customer = await stripeAdapter.CustomerCreateAsync(customerCreateOptions);
|
||||||
|
|
||||||
organization.GatewayCustomerId = customer.Id;
|
organization.GatewayCustomerId = customer.Id;
|
||||||
@ -517,6 +524,13 @@ public class ProviderBillingService(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var setNonUSBusinessUseToReverseCharge = featureService.IsEnabled(FeatureFlagKeys.PM21092_SetNonUSBusinessUseToReverseCharge);
|
||||||
|
|
||||||
|
if (setNonUSBusinessUseToReverseCharge && taxInfo.BillingAddressCountry != "US")
|
||||||
|
{
|
||||||
|
options.TaxExempt = StripeConstants.TaxExempt.Reverse;
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(taxInfo.TaxIdNumber))
|
if (!string.IsNullOrEmpty(taxInfo.TaxIdNumber))
|
||||||
{
|
{
|
||||||
var taxIdType = taxService.GetStripeTaxCode(
|
var taxIdType = taxService.GetStripeTaxCode(
|
||||||
@ -528,6 +542,7 @@ public class ProviderBillingService(
|
|||||||
logger.LogWarning("Could not infer tax ID type in country '{Country}' with tax ID '{TaxID}'.",
|
logger.LogWarning("Could not infer tax ID type in country '{Country}' with tax ID '{TaxID}'.",
|
||||||
taxInfo.BillingAddressCountry,
|
taxInfo.BillingAddressCountry,
|
||||||
taxInfo.TaxIdNumber);
|
taxInfo.TaxIdNumber);
|
||||||
|
|
||||||
throw new BadRequestException("billingTaxIdTypeInferenceError");
|
throw new BadRequestException("billingTaxIdTypeInferenceError");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -535,6 +550,15 @@ public class ProviderBillingService(
|
|||||||
[
|
[
|
||||||
new CustomerTaxIdDataOptions { Type = taxIdType, Value = taxInfo.TaxIdNumber }
|
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))
|
if (!string.IsNullOrEmpty(provider.DiscountId))
|
||||||
@ -692,6 +716,13 @@ public class ProviderBillingService(
|
|||||||
customer.Metadata.ContainsKey(BraintreeCustomerIdKey) ||
|
customer.Metadata.ContainsKey(BraintreeCustomerIdKey) ||
|
||||||
setupIntent.IsUnverifiedBankAccount());
|
setupIntent.IsUnverifiedBankAccount());
|
||||||
|
|
||||||
|
int? trialPeriodDays = provider.Type switch
|
||||||
|
{
|
||||||
|
ProviderType.Msp when usePaymentMethod => 14,
|
||||||
|
ProviderType.BusinessUnit when usePaymentMethod => 4,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
var subscriptionCreateOptions = new SubscriptionCreateOptions
|
var subscriptionCreateOptions = new SubscriptionCreateOptions
|
||||||
{
|
{
|
||||||
CollectionMethod = usePaymentMethod ?
|
CollectionMethod = usePaymentMethod ?
|
||||||
@ -705,17 +736,24 @@ public class ProviderBillingService(
|
|||||||
},
|
},
|
||||||
OffSession = true,
|
OffSession = true,
|
||||||
ProrationBehavior = StripeConstants.ProrationBehavior.CreateProrations,
|
ProrationBehavior = StripeConstants.ProrationBehavior.CreateProrations,
|
||||||
TrialPeriodDays = usePaymentMethod ? 14 : null
|
TrialPeriodDays = trialPeriodDays
|
||||||
};
|
};
|
||||||
|
|
||||||
if (featureService.IsEnabled(FeatureFlagKeys.PM19147_AutomaticTaxImprovements))
|
var setNonUSBusinessUseToReverseCharge =
|
||||||
{
|
featureService.IsEnabled(FeatureFlagKeys.PM21092_SetNonUSBusinessUseToReverseCharge);
|
||||||
automaticTaxStrategy.SetCreateOptions(subscriptionCreateOptions, customer);
|
|
||||||
}
|
if (setNonUSBusinessUseToReverseCharge)
|
||||||
else
|
|
||||||
{
|
{
|
||||||
subscriptionCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true };
|
subscriptionCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions { Enabled = true };
|
||||||
}
|
}
|
||||||
|
else if (customer.HasRecognizedTaxLocation())
|
||||||
|
{
|
||||||
|
subscriptionCreateOptions.AutomaticTax = new SubscriptionAutomaticTaxOptions
|
||||||
|
{
|
||||||
|
Enabled = customer.Address.Country == "US" ||
|
||||||
|
customer.TaxIds.Any()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
@ -6,7 +6,7 @@ using Bit.Core.Billing;
|
|||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Stripe;
|
using Stripe;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.Billing;
|
namespace Bit.Commercial.Core.Billing.Providers.Services;
|
||||||
|
|
||||||
public static class ProviderPriceAdapter
|
public static class ProviderPriceAdapter
|
||||||
{
|
{
|
@ -1,13 +1,9 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Enums;
|
|
||||||
using Bit.Core.Billing.Licenses;
|
|
||||||
using Bit.Core.Billing.Licenses.Extensions;
|
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.SecretsManager.Queries.Projects.Interfaces;
|
using Bit.Core.SecretsManager.Queries.Projects.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Bit.Core.Services;
|
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.SecretsManager.Queries.Projects;
|
namespace Bit.Commercial.Core.SecretsManager.Queries.Projects;
|
||||||
@ -17,72 +13,42 @@ public class MaxProjectsQuery : IMaxProjectsQuery
|
|||||||
private readonly IOrganizationRepository _organizationRepository;
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly IProjectRepository _projectRepository;
|
private readonly IProjectRepository _projectRepository;
|
||||||
private readonly IGlobalSettings _globalSettings;
|
private readonly IGlobalSettings _globalSettings;
|
||||||
private readonly ILicensingService _licensingService;
|
|
||||||
private readonly IPricingClient _pricingClient;
|
private readonly IPricingClient _pricingClient;
|
||||||
|
|
||||||
public MaxProjectsQuery(
|
public MaxProjectsQuery(
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
IProjectRepository projectRepository,
|
IProjectRepository projectRepository,
|
||||||
IGlobalSettings globalSettings,
|
IGlobalSettings globalSettings,
|
||||||
ILicensingService licensingService,
|
|
||||||
IPricingClient pricingClient)
|
IPricingClient pricingClient)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_projectRepository = projectRepository;
|
_projectRepository = projectRepository;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
_licensingService = licensingService;
|
|
||||||
_pricingClient = pricingClient;
|
_pricingClient = pricingClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(short? max, bool? overMax)> GetByOrgIdAsync(Guid organizationId, int projectsToAdd)
|
public async Task<(short? max, bool? overMax)> GetByOrgIdAsync(Guid organizationId, int projectsToAdd)
|
||||||
{
|
{
|
||||||
|
// "MaxProjects" only applies to free 2-person organizations, which can't be self-hosted.
|
||||||
|
if (_globalSettings.SelfHosted)
|
||||||
|
{
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
var org = await _organizationRepository.GetByIdAsync(organizationId);
|
var org = await _organizationRepository.GetByIdAsync(organizationId);
|
||||||
if (org == null)
|
if (org == null)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var (planType, maxProjects) = await GetPlanTypeAndMaxProjectsAsync(org);
|
var plan = await _pricingClient.GetPlan(org.PlanType);
|
||||||
|
|
||||||
if (planType != PlanType.Free)
|
if (plan is not { SecretsManager: not null, Type: PlanType.Free })
|
||||||
{
|
{
|
||||||
return (null, null);
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
var projects = await _projectRepository.GetProjectCountByOrganizationIdAsync(organizationId);
|
var projects = await _projectRepository.GetProjectCountByOrganizationIdAsync(organizationId);
|
||||||
return ((short? max, bool? overMax))(projects + projectsToAdd > maxProjects ? (maxProjects, true) : (maxProjects, false));
|
return ((short? max, bool? overMax))(projects + projectsToAdd > plan.SecretsManager.MaxProjects ? (plan.SecretsManager.MaxProjects, true) : (plan.SecretsManager.MaxProjects, false));
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<(PlanType planType, int maxProjects)> GetPlanTypeAndMaxProjectsAsync(Organization organization)
|
|
||||||
{
|
|
||||||
if (_globalSettings.SelfHosted)
|
|
||||||
{
|
|
||||||
var license = await _licensingService.ReadOrganizationLicenseAsync(organization);
|
|
||||||
|
|
||||||
if (license == null)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("License not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var claimsPrincipal = _licensingService.GetClaimsPrincipalFromLicense(license);
|
|
||||||
var maxProjects = claimsPrincipal.GetValue<int?>(OrganizationLicenseConstants.SmMaxProjects);
|
|
||||||
|
|
||||||
if (!maxProjects.HasValue)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("License does not contain a value for max Secrets Manager projects");
|
|
||||||
}
|
|
||||||
|
|
||||||
var planType = claimsPrincipal.GetValue<PlanType>(OrganizationLicenseConstants.PlanType);
|
|
||||||
return (planType, maxProjects.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
var plan = await _pricingClient.GetPlan(organization.PlanType);
|
|
||||||
|
|
||||||
if (plan is { SupportsSecretsManager: true })
|
|
||||||
{
|
|
||||||
return (plan.Type, plan.SecretsManager.MaxProjects);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new BadRequestException("Existing plan not found.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
using Bit.Commercial.Core.AdminConsole.Providers;
|
using Bit.Commercial.Core.AdminConsole.Providers;
|
||||||
using Bit.Commercial.Core.AdminConsole.Services;
|
using Bit.Commercial.Core.AdminConsole.Services;
|
||||||
using Bit.Commercial.Core.Billing;
|
using Bit.Commercial.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.AdminConsole.Providers.Interfaces;
|
using Bit.Core.AdminConsole.Providers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Providers.Services;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.Utilities;
|
namespace Bit.Commercial.Core.Utilities;
|
||||||
|
@ -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
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||||
|
|
||||||
|
ARG TARGETPLATFORM
|
||||||
LABEL com.bitwarden.product="bitwarden"
|
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 \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends \
|
&& apt-get install -y --no-install-recommends \
|
||||||
@ -9,11 +53,10 @@ RUN apt-get update \
|
|||||||
krb5-user \
|
krb5-user \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV ASPNETCORE_URLS http://+:5000
|
# Copy app from the build stage
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 5000
|
COPY --from=build /source/bitwarden_license/src/Scim/out /app
|
||||||
COPY obj/build-output/publish .
|
COPY ./bitwarden_license/src/Scim/entrypoint.sh /entrypoint.sh
|
||||||
COPY entrypoint.sh /
|
|
||||||
RUN chmod +x /entrypoint.sh
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|
||||||
HEALTHCHECK CMD curl -f http://localhost:5000/alive || exit 1
|
HEALTHCHECK CMD curl -f http://localhost:5000/alive || exit 1
|
||||||
|
@ -16,8 +16,8 @@ public class Program
|
|||||||
{
|
{
|
||||||
var context = e.Properties["SourceContext"].ToString();
|
var context = e.Properties["SourceContext"].ToString();
|
||||||
|
|
||||||
if (e.Properties.ContainsKey("RequestPath") &&
|
if (e.Properties.TryGetValue("RequestPath", out var requestPath) &&
|
||||||
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
|
!string.IsNullOrWhiteSpace(requestPath?.ToString()) &&
|
||||||
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -6,10 +6,10 @@ using Bit.Core.AdminConsole.Models.Business;
|
|||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Errors;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Errors;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
|
||||||
|
using Bit.Core.AdminConsole.Utilities.Commands;
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Commands;
|
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
@ -76,9 +76,8 @@ public class PostUserCommand(
|
|||||||
var invitedOrganizationUserId = result switch
|
var invitedOrganizationUserId = result switch
|
||||||
{
|
{
|
||||||
Success<ScimInviteOrganizationUsersResponse> success => success.Value.InvitedUser.Id,
|
Success<ScimInviteOrganizationUsersResponse> success => success.Value.InvitedUser.Id,
|
||||||
Failure<ScimInviteOrganizationUsersResponse> failure when failure.Errors
|
Failure<ScimInviteOrganizationUsersResponse> { Error.Message: NoUsersToInviteError.Code } => (Guid?)null,
|
||||||
.Any(x => x.Message == NoUsersToInviteError.Code) => (Guid?)null,
|
Failure<ScimInviteOrganizationUsersResponse> failure => throw MapToBitException(failure.Error),
|
||||||
Failure<ScimInviteOrganizationUsersResponse> failure when failure.Errors.Length != 0 => throw MapToBitException(failure.Errors),
|
|
||||||
_ => throw new InvalidOperationException()
|
_ => throw new InvalidOperationException()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
|
|
||||||
@ -19,31 +19,42 @@ then
|
|||||||
LGID=65534
|
LGID=65534
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create user and group
|
if [ "$(id -u)" = "0" ]
|
||||||
|
then
|
||||||
|
# Create user and group
|
||||||
|
|
||||||
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
||||||
groupmod -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 ||
|
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
|
usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1
|
||||||
mkhomedir_helper $USERNAME
|
mkhomedir_helper $USERNAME
|
||||||
|
|
||||||
# The rest...
|
# The rest...
|
||||||
|
|
||||||
chown -R $USERNAME:$GROUPNAME /app
|
chown -R $USERNAME:$GROUPNAME /app
|
||||||
mkdir -p /etc/bitwarden/core
|
mkdir -p /etc/bitwarden/core
|
||||||
mkdir -p /etc/bitwarden/logs
|
mkdir -p /etc/bitwarden/logs
|
||||||
mkdir -p /etc/bitwarden/ca-certificates
|
mkdir -p /etc/bitwarden/ca-certificates
|
||||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||||
|
|
||||||
if [[ $globalSettings__selfHosted == "true" ]]; then
|
if [[ -f "/etc/bitwarden/kerberos/bitwarden.keytab" && -f "/etc/bitwarden/kerberos/krb5.conf" ]]; then
|
||||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden/kerberos
|
||||||
&& update-ca-certificates
|
fi
|
||||||
|
|
||||||
|
gosu_cmd="gosu $USERNAME:$GROUPNAME"
|
||||||
|
else
|
||||||
|
gosu_cmd=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -f "/etc/bitwarden/kerberos/bitwarden.keytab" && -f "/etc/bitwarden/kerberos/krb5.conf" ]]; then
|
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
|
cp -f /etc/bitwarden/kerberos/krb5.conf /etc/krb5.conf
|
||||||
gosu $USERNAME:$GROUPNAME kinit $globalSettings__kerberosUser -k -t /etc/bitwarden/kerberos/bitwarden.keytab
|
$gosu_cmd kinit $globalSettings__kerberosUser -k -t /etc/bitwarden/kerberos/bitwarden.keytab
|
||||||
fi
|
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.
|
// for the user identifier.
|
||||||
static bool nameIdIsNotTransient(Claim c) => c.Type == ClaimTypes.NameIdentifier
|
static bool nameIdIsNotTransient(Claim c) => c.Type == ClaimTypes.NameIdentifier
|
||||||
&& (c.Properties == null
|
&& (c.Properties == null
|
||||||
|| !c.Properties.ContainsKey(SamlPropertyKeys.ClaimFormat)
|
|| !c.Properties.TryGetValue(SamlPropertyKeys.ClaimFormat, out var claimFormat)
|
||||||
|| c.Properties[SamlPropertyKeys.ClaimFormat] != SamlNameIdFormats.Transient);
|
|| claimFormat != SamlNameIdFormats.Transient);
|
||||||
|
|
||||||
// Try to determine the unique id of the external user (issued by the provider)
|
// 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
|
// the most common claim type for that are the sub claim and the NameIdentifier
|
||||||
|
@ -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
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||||
|
|
||||||
|
ARG TARGETPLATFORM
|
||||||
LABEL com.bitwarden.product="bitwarden"
|
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 \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends \
|
&& apt-get install -y --no-install-recommends \
|
||||||
@ -9,11 +53,10 @@ RUN apt-get update \
|
|||||||
krb5-user \
|
krb5-user \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV ASPNETCORE_URLS http://+:5000
|
# Copy app from the build stage
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 5000
|
COPY --from=build /source/bitwarden_license/src/Sso/out /app
|
||||||
COPY obj/build-output/publish .
|
COPY ./bitwarden_license/src/Sso/entrypoint.sh /entrypoint.sh
|
||||||
COPY entrypoint.sh /
|
|
||||||
RUN chmod +x /entrypoint.sh
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|
||||||
HEALTHCHECK CMD curl -f http://localhost:5000/alive || exit 1
|
HEALTHCHECK CMD curl -f http://localhost:5000/alive || exit 1
|
||||||
|
@ -17,8 +17,8 @@ public class Program
|
|||||||
logging.AddSerilog(hostingContext, (e, globalSettings) =>
|
logging.AddSerilog(hostingContext, (e, globalSettings) =>
|
||||||
{
|
{
|
||||||
var context = e.Properties["SourceContext"].ToString();
|
var context = e.Properties["SourceContext"].ToString();
|
||||||
if (e.Properties.ContainsKey("RequestPath") &&
|
if (e.Properties.TryGetValue("RequestPath", out var requestPath) &&
|
||||||
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
|
!string.IsNullOrWhiteSpace(requestPath?.ToString()) &&
|
||||||
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -46,9 +46,9 @@ public static class OpenIdConnectOptionsExtensions
|
|||||||
|
|
||||||
// Handle State if we've gotten that back
|
// Handle State if we've gotten that back
|
||||||
var decodedState = options.StateDataFormat.Unprotect(state);
|
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
|
catch
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
|
|
||||||
@ -19,37 +19,42 @@ then
|
|||||||
LGID=65534
|
LGID=65534
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create user and group
|
if [ "$(id -u)" = "0" ]
|
||||||
|
then
|
||||||
|
# Create user and group
|
||||||
|
|
||||||
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
||||||
groupmod -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 ||
|
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
|
usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1
|
||||||
mkhomedir_helper $USERNAME
|
mkhomedir_helper $USERNAME
|
||||||
|
|
||||||
# The rest...
|
# The rest...
|
||||||
|
|
||||||
mkdir -p /etc/bitwarden/identity
|
chown -R $USERNAME:$GROUPNAME /app
|
||||||
mkdir -p /etc/bitwarden/core
|
mkdir -p /etc/bitwarden/core
|
||||||
mkdir -p /etc/bitwarden/logs
|
mkdir -p /etc/bitwarden/logs
|
||||||
mkdir -p /etc/bitwarden/ca-certificates
|
mkdir -p /etc/bitwarden/ca-certificates
|
||||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||||
|
|
||||||
if [[ $globalSettings__selfHosted == "true" ]]; then
|
if [[ -f "/etc/bitwarden/kerberos/bitwarden.keytab" && -f "/etc/bitwarden/kerberos/krb5.conf" ]]; then
|
||||||
cp /etc/bitwarden/identity/identity.pfx /app/identity.pfx
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden/kerberos
|
||||||
fi
|
fi
|
||||||
|
|
||||||
chown -R $USERNAME:$GROUPNAME /app
|
gosu_cmd="gosu $USERNAME:$GROUPNAME"
|
||||||
|
else
|
||||||
if [[ $globalSettings__selfHosted == "true" ]]; then
|
gosu_cmd=""
|
||||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
|
||||||
&& update-ca-certificates
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -f "/etc/bitwarden/kerberos/bitwarden.keytab" && -f "/etc/bitwarden/kerberos/krb5.conf" ]]; then
|
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
|
cp -f /etc/bitwarden/kerberos/krb5.conf /etc/krb5.conf
|
||||||
gosu $USERNAME:$GROUPNAME kinit $globalSettings__kerberosUser -k -t /etc/bitwarden/kerberos/bitwarden.keytab
|
$gosu_cmd kinit $globalSettings__kerberosUser -k -t /etc/bitwarden/kerberos/bitwarden.keytab
|
||||||
fi
|
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
|
||||||
|
207
bitwarden_license/src/Sso/package-lock.json
generated
207
bitwarden_license/src/Sso/package-lock.json
generated
@ -9,17 +9,17 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "-",
|
"license": "-",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bootstrap": "5.3.3",
|
"bootstrap": "5.3.6",
|
||||||
"font-awesome": "4.7.0",
|
"font-awesome": "4.7.0",
|
||||||
"jquery": "3.7.1"
|
"jquery": "3.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"css-loader": "7.1.2",
|
"css-loader": "7.1.2",
|
||||||
"expose-loader": "5.0.0",
|
"expose-loader": "5.0.1",
|
||||||
"mini-css-extract-plugin": "2.9.2",
|
"mini-css-extract-plugin": "2.9.2",
|
||||||
"sass": "1.85.0",
|
"sass": "1.88.0",
|
||||||
"sass-loader": "16.0.4",
|
"sass-loader": "16.0.5",
|
||||||
"webpack": "5.97.1",
|
"webpack": "5.99.8",
|
||||||
"webpack-cli": "5.1.4"
|
"webpack-cli": "5.1.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -455,13 +455,13 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.13.14",
|
"version": "22.15.21",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
|
||||||
"integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==",
|
"integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.20.0"
|
"undici-types": "~6.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/ast": {
|
"node_modules/@webassemblyjs/ast": {
|
||||||
@ -748,9 +748,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bootstrap": {
|
"node_modules/bootstrap": {
|
||||||
"version": "5.3.3",
|
"version": "5.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.6.tgz",
|
||||||
"integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
|
"integrity": "sha512-jX0GAcRzvdwISuvArXn3m7KZscWWFAf1MKBcnzaN02qWMb3jpMoUX4/qgeiGzqyIb4ojulRzs89UCUmGcFSzTA==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@ -781,9 +781,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.24.4",
|
"version": "4.24.5",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz",
|
||||||
"integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
|
"integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -801,10 +801,10 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"caniuse-lite": "^1.0.30001688",
|
"caniuse-lite": "^1.0.30001716",
|
||||||
"electron-to-chromium": "^1.5.73",
|
"electron-to-chromium": "^1.5.149",
|
||||||
"node-releases": "^2.0.19",
|
"node-releases": "^2.0.19",
|
||||||
"update-browserslist-db": "^1.1.1"
|
"update-browserslist-db": "^1.1.3"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"browserslist": "cli.js"
|
"browserslist": "cli.js"
|
||||||
@ -821,9 +821,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001707",
|
"version": "1.0.30001718",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
|
||||||
"integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==",
|
"integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -975,9 +975,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.128",
|
"version": "1.5.155",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.128.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz",
|
||||||
"integrity": "sha512-bo1A4HH/NS522Ws0QNFIzyPcyUUNV/yyy70Ho1xqfGYzPUme2F/xr4tlEOuM6/A538U1vDA7a4XfCd1CKRegKQ==",
|
"integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
@ -1009,9 +1009,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/es-module-lexer": {
|
"node_modules/es-module-lexer": {
|
||||||
"version": "1.6.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
|
||||||
"integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==",
|
"integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@ -1083,9 +1083,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/expose-loader": {
|
"node_modules/expose-loader": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-5.0.1.tgz",
|
||||||
"integrity": "sha512-BtUqYRmvx1bEY5HN6eK2I9URUZgNmN0x5UANuocaNjXSgfoDlkXt+wyEMe7i5DzDNh2BKJHPc5F4rBwEdSQX6w==",
|
"integrity": "sha512-5YPZuszN/eWND/B+xuq5nIpb/l5TV1HYmdO6SubYtHv+HenVw9/6bn33Mm5reY8DNid7AVtbARvyUD34edfCtg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1106,13 +1106,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/fast-uri": {
|
||||||
"version": "3.0.6",
|
"version": "3.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
|
||||||
@ -1248,9 +1241,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/immutable": {
|
"node_modules/immutable": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.2.tgz",
|
||||||
"integrity": "sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==",
|
"integrity": "sha512-qHKXW1q6liAk1Oys6umoaZbDRqjcjgSrbnrifHsfsttza7zcvRAsL7mMV6xWcyhwQy7Xj5v4hhbr6b+iDYwlmQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@ -1754,16 +1747,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/randombytes": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||||
@ -1877,9 +1860,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/sass": {
|
"node_modules/sass": {
|
||||||
"version": "1.85.0",
|
"version": "1.88.0",
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.85.0.tgz",
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.88.0.tgz",
|
||||||
"integrity": "sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww==",
|
"integrity": "sha512-sF6TWQqjFvr4JILXzG4ucGOLELkESHL+I5QJhh7CNaE+Yge0SI+ehCatsXhJ7ymU1hAFcIS3/PBpjdIbXoyVbg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1898,9 +1881,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sass-loader": {
|
"node_modules/sass-loader": {
|
||||||
"version": "16.0.4",
|
"version": "16.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.5.tgz",
|
||||||
"integrity": "sha512-LavLbgbBGUt3wCiYzhuLLu65+fWXaXLmq7YxivLhEqmiupCFZ5sKUAipK3do6V80YSU0jvSxNhEdT13IXNr3rg==",
|
"integrity": "sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1939,9 +1922,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/schema-utils": {
|
"node_modules/schema-utils": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
|
||||||
"integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
|
"integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1959,9 +1942,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.7.1",
|
"version": "7.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
@ -2078,9 +2061,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tapable": {
|
"node_modules/tapable": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz",
|
||||||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
|
"integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -2088,14 +2071,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/terser": {
|
"node_modules/terser": {
|
||||||
"version": "5.39.0",
|
"version": "5.39.2",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.39.2.tgz",
|
||||||
"integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==",
|
"integrity": "sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
"acorn": "^8.8.2",
|
"acorn": "^8.14.0",
|
||||||
"commander": "^2.20.0",
|
"commander": "^2.20.0",
|
||||||
"source-map-support": "~0.5.20"
|
"source-map-support": "~0.5.20"
|
||||||
},
|
},
|
||||||
@ -2156,9 +2139,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "6.20.0",
|
"version": "6.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@ -2193,16 +2176,6 @@
|
|||||||
"browserslist": ">= 4.21.0"
|
"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": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
@ -2211,9 +2184,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/watchpack": {
|
"node_modules/watchpack": {
|
||||||
"version": "2.4.2",
|
"version": "2.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz",
|
||||||
"integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
|
"integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -2225,14 +2198,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/webpack": {
|
"node_modules/webpack": {
|
||||||
"version": "5.97.1",
|
"version": "5.99.8",
|
||||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz",
|
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.8.tgz",
|
||||||
"integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==",
|
"integrity": "sha512-lQ3CPiSTpfOnrEGeXDwoq5hIGzSjmwD72GdfVzF7CQAI7t47rJG9eDWvcEkEn3CUQymAElVvDg3YNTlCYj+qUQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/eslint-scope": "^3.7.7",
|
"@types/eslint-scope": "^3.7.7",
|
||||||
"@types/estree": "^1.0.6",
|
"@types/estree": "^1.0.6",
|
||||||
|
"@types/json-schema": "^7.0.15",
|
||||||
"@webassemblyjs/ast": "^1.14.1",
|
"@webassemblyjs/ast": "^1.14.1",
|
||||||
"@webassemblyjs/wasm-edit": "^1.14.1",
|
"@webassemblyjs/wasm-edit": "^1.14.1",
|
||||||
"@webassemblyjs/wasm-parser": "^1.14.1",
|
"@webassemblyjs/wasm-parser": "^1.14.1",
|
||||||
@ -2249,9 +2223,9 @@
|
|||||||
"loader-runner": "^4.2.0",
|
"loader-runner": "^4.2.0",
|
||||||
"mime-types": "^2.1.27",
|
"mime-types": "^2.1.27",
|
||||||
"neo-async": "^2.6.2",
|
"neo-async": "^2.6.2",
|
||||||
"schema-utils": "^3.2.0",
|
"schema-utils": "^4.3.2",
|
||||||
"tapable": "^2.1.1",
|
"tapable": "^2.1.1",
|
||||||
"terser-webpack-plugin": "^5.3.10",
|
"terser-webpack-plugin": "^5.3.11",
|
||||||
"watchpack": "^2.4.1",
|
"watchpack": "^2.4.1",
|
||||||
"webpack-sources": "^3.2.3"
|
"webpack-sources": "^3.2.3"
|
||||||
},
|
},
|
||||||
@ -2352,59 +2326,6 @@
|
|||||||
"node": ">=10.13.0"
|
"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": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
@ -8,17 +8,17 @@
|
|||||||
"build": "webpack"
|
"build": "webpack"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bootstrap": "5.3.3",
|
"bootstrap": "5.3.6",
|
||||||
"font-awesome": "4.7.0",
|
"font-awesome": "4.7.0",
|
||||||
"jquery": "3.7.1"
|
"jquery": "3.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"css-loader": "7.1.2",
|
"css-loader": "7.1.2",
|
||||||
"expose-loader": "5.0.0",
|
"expose-loader": "5.0.1",
|
||||||
"mini-css-extract-plugin": "2.9.2",
|
"mini-css-extract-plugin": "2.9.2",
|
||||||
"sass": "1.85.0",
|
"sass": "1.88.0",
|
||||||
"sass-loader": "16.0.4",
|
"sass-loader": "16.0.5",
|
||||||
"webpack": "5.97.1",
|
"webpack": "5.99.8",
|
||||||
"webpack-cli": "5.1.4"
|
"webpack-cli": "5.1.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Bit.Commercial.Core.AdminConsole.Providers;
|
using Bit.Commercial.Core.AdminConsole.Providers;
|
||||||
|
using Bit.Core;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
@ -7,6 +8,7 @@ using Bit.Core.AdminConsole.Repositories;
|
|||||||
using Bit.Core.Billing.Constants;
|
using Bit.Core.Billing.Constants;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
|
using Bit.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Services;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
@ -223,31 +225,115 @@ public class RemoveOrganizationFromProviderCommandTests
|
|||||||
|
|
||||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
|
|
||||||
|
stripeAdapter.CustomerUpdateAsync(organization.GatewayCustomerId, Arg.Is<CustomerUpdateOptions>(options =>
|
||||||
|
options.Description == string.Empty &&
|
||||||
|
options.Email == organization.BillingEmail &&
|
||||||
|
options.Expand[0] == "tax" &&
|
||||||
|
options.Expand[1] == "tax_ids")).Returns(new Customer
|
||||||
|
{
|
||||||
|
Id = "customer_id",
|
||||||
|
Address = new Address
|
||||||
|
{
|
||||||
|
Country = "US"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
stripeAdapter.SubscriptionCreateAsync(Arg.Any<SubscriptionCreateOptions>()).Returns(new Subscription
|
stripeAdapter.SubscriptionCreateAsync(Arg.Any<SubscriptionCreateOptions>()).Returns(new Subscription
|
||||||
{
|
{
|
||||||
Id = "subscription_id"
|
Id = "subscription_id"
|
||||||
});
|
});
|
||||||
|
|
||||||
sutProvider.GetDependency<IAutomaticTaxStrategy>()
|
await sutProvider.Sut.RemoveOrganizationFromProvider(provider, providerOrganization, organization);
|
||||||
.When(x => x.SetCreateOptions(
|
|
||||||
Arg.Is<SubscriptionCreateOptions>(options =>
|
await stripeAdapter.Received(1).SubscriptionCreateAsync(Arg.Is<SubscriptionCreateOptions>(options =>
|
||||||
options.Customer == organization.GatewayCustomerId &&
|
options.Customer == organization.GatewayCustomerId &&
|
||||||
options.CollectionMethod == StripeConstants.CollectionMethod.SendInvoice &&
|
options.CollectionMethod == StripeConstants.CollectionMethod.SendInvoice &&
|
||||||
options.DaysUntilDue == 30 &&
|
options.DaysUntilDue == 30 &&
|
||||||
|
options.AutomaticTax.Enabled == true &&
|
||||||
options.Metadata["organizationId"] == organization.Id.ToString() &&
|
options.Metadata["organizationId"] == organization.Id.ToString() &&
|
||||||
options.OffSession == true &&
|
options.OffSession == true &&
|
||||||
options.ProrationBehavior == StripeConstants.ProrationBehavior.CreateProrations &&
|
options.ProrationBehavior == StripeConstants.ProrationBehavior.CreateProrations &&
|
||||||
options.Items.First().Price == teamsMonthlyPlan.PasswordManager.StripeSeatPlanId &&
|
options.Items.First().Price == teamsMonthlyPlan.PasswordManager.StripeSeatPlanId &&
|
||||||
options.Items.First().Quantity == organization.Seats)
|
options.Items.First().Quantity == organization.Seats));
|
||||||
, Arg.Any<Customer>()))
|
|
||||||
.Do(x =>
|
await sutProvider.GetDependency<IProviderBillingService>().Received(1)
|
||||||
|
.ScaleSeats(provider, organization.PlanType, -organization.Seats ?? 0);
|
||||||
|
|
||||||
|
await organizationRepository.Received(1).ReplaceAsync(Arg.Is<Organization>(
|
||||||
|
org =>
|
||||||
|
org.BillingEmail == "a@example.com" &&
|
||||||
|
org.GatewaySubscriptionId == "subscription_id" &&
|
||||||
|
org.Status == OrganizationStatusType.Created));
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IProviderOrganizationRepository>().Received(1)
|
||||||
|
.DeleteAsync(providerOrganization);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IEventService>().Received(1)
|
||||||
|
.LogProviderOrganizationEventAsync(providerOrganization, EventType.ProviderOrganization_Removed);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||||
|
.SendProviderUpdatePaymentMethod(
|
||||||
|
organization.Id,
|
||||||
|
organization.Name,
|
||||||
|
provider.Name,
|
||||||
|
Arg.Is<IEnumerable<string>>(emails => emails.FirstOrDefault() == "a@example.com"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task RemoveOrganizationFromProvider_OrganizationStripeEnabled_ConsolidatedBilling_ReverseCharge_MakesCorrectInvocations(
|
||||||
|
Provider provider,
|
||||||
|
ProviderOrganization providerOrganization,
|
||||||
|
Organization organization,
|
||||||
|
SutProvider<RemoveOrganizationFromProviderCommand> sutProvider)
|
||||||
{
|
{
|
||||||
x.Arg<SubscriptionCreateOptions>().AutomaticTax = new SubscriptionAutomaticTaxOptions
|
provider.Status = ProviderStatusType.Billable;
|
||||||
|
|
||||||
|
providerOrganization.ProviderId = provider.Id;
|
||||||
|
|
||||||
|
organization.Status = OrganizationStatusType.Managed;
|
||||||
|
|
||||||
|
organization.PlanType = PlanType.TeamsMonthly;
|
||||||
|
|
||||||
|
var teamsMonthlyPlan = StaticStore.GetPlan(PlanType.TeamsMonthly);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(PlanType.TeamsMonthly).Returns(teamsMonthlyPlan);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IHasConfirmedOwnersExceptQuery>().HasConfirmedOwnersExceptAsync(
|
||||||
|
providerOrganization.OrganizationId,
|
||||||
|
[],
|
||||||
|
includeProvider: false)
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||||
|
|
||||||
|
organizationRepository.GetOwnerEmailAddressesById(organization.Id).Returns([
|
||||||
|
"a@example.com",
|
||||||
|
"b@example.com"
|
||||||
|
]);
|
||||||
|
|
||||||
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
|
|
||||||
|
stripeAdapter.CustomerUpdateAsync(organization.GatewayCustomerId, Arg.Is<CustomerUpdateOptions>(options =>
|
||||||
|
options.Description == string.Empty &&
|
||||||
|
options.Email == organization.BillingEmail &&
|
||||||
|
options.Expand[0] == "tax" &&
|
||||||
|
options.Expand[1] == "tax_ids")).Returns(new Customer
|
||||||
{
|
{
|
||||||
Enabled = true
|
Id = "customer_id",
|
||||||
};
|
Address = new Address
|
||||||
|
{
|
||||||
|
Country = "US"
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
stripeAdapter.SubscriptionCreateAsync(Arg.Any<SubscriptionCreateOptions>()).Returns(new Subscription
|
||||||
|
{
|
||||||
|
Id = "subscription_id"
|
||||||
|
});
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
|
.IsEnabled(FeatureFlagKeys.PM21092_SetNonUSBusinessUseToReverseCharge).Returns(true);
|
||||||
|
|
||||||
await sutProvider.Sut.RemoveOrganizationFromProvider(provider, providerOrganization, organization);
|
await sutProvider.Sut.RemoveOrganizationFromProvider(provider, providerOrganization, organization);
|
||||||
|
|
||||||
await stripeAdapter.Received(1).SubscriptionCreateAsync(Arg.Is<SubscriptionCreateOptions>(options =>
|
await stripeAdapter.Received(1).SubscriptionCreateAsync(Arg.Is<SubscriptionCreateOptions>(options =>
|
||||||
|
@ -6,11 +6,12 @@ using Bit.Core.AdminConsole.Entities.Provider;
|
|||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Models.Business.Provider;
|
using Bit.Core.AdminConsole.Models.Business.Provider;
|
||||||
using Bit.Core.AdminConsole.Models.Business.Tokenables;
|
using Bit.Core.AdminConsole.Models.Business.Tokenables;
|
||||||
|
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Models;
|
using Bit.Core.Billing.Models;
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -717,8 +718,8 @@ public class ProviderServiceTests
|
|||||||
|
|
||||||
sutProvider.GetDependency<IProviderRepository>().GetByIdAsync(provider.Id).Returns(provider);
|
sutProvider.GetDependency<IProviderRepository>().GetByIdAsync(provider.Id).Returns(provider);
|
||||||
var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>();
|
var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>();
|
||||||
sutProvider.GetDependency<IOrganizationService>().SignupClientAsync(organizationSignup)
|
sutProvider.GetDependency<IProviderClientOrganizationSignUpCommand>().SignUpClientOrganizationAsync(organizationSignup)
|
||||||
.Returns((organization, null as OrganizationUser, new Collection()));
|
.Returns(new ProviderClientOrganizationSignUpResponse(organization, new Collection()));
|
||||||
|
|
||||||
var providerOrganization =
|
var providerOrganization =
|
||||||
await sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user);
|
await sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user);
|
||||||
@ -755,8 +756,8 @@ public class ProviderServiceTests
|
|||||||
|
|
||||||
var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>();
|
var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>();
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationService>().SignupClientAsync(organizationSignup)
|
sutProvider.GetDependency<IProviderClientOrganizationSignUpCommand>().SignUpClientOrganizationAsync(organizationSignup)
|
||||||
.Returns((organization, null as OrganizationUser, new Collection()));
|
.Returns(new ProviderClientOrganizationSignUpResponse(organization, new Collection()));
|
||||||
|
|
||||||
await Assert.ThrowsAsync<BadRequestException>(() =>
|
await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||||
sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user));
|
sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user));
|
||||||
@ -782,8 +783,8 @@ public class ProviderServiceTests
|
|||||||
|
|
||||||
var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>();
|
var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>();
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationService>().SignupClientAsync(organizationSignup)
|
sutProvider.GetDependency<IProviderClientOrganizationSignUpCommand>().SignUpClientOrganizationAsync(organizationSignup)
|
||||||
.Returns((organization, null as OrganizationUser, new Collection()));
|
.Returns(new ProviderClientOrganizationSignUpResponse(organization, new Collection()));
|
||||||
|
|
||||||
var providerOrganization = await sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user);
|
var providerOrganization = await sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user);
|
||||||
|
|
||||||
@ -821,8 +822,8 @@ public class ProviderServiceTests
|
|||||||
|
|
||||||
sutProvider.GetDependency<IProviderRepository>().GetByIdAsync(provider.Id).Returns(provider);
|
sutProvider.GetDependency<IProviderRepository>().GetByIdAsync(provider.Id).Returns(provider);
|
||||||
var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>();
|
var providerOrganizationRepository = sutProvider.GetDependency<IProviderOrganizationRepository>();
|
||||||
sutProvider.GetDependency<IOrganizationService>().SignupClientAsync(organizationSignup)
|
sutProvider.GetDependency<IProviderClientOrganizationSignUpCommand>().SignUpClientOrganizationAsync(organizationSignup)
|
||||||
.Returns((organization, null as OrganizationUser, defaultCollection));
|
.Returns(new ProviderClientOrganizationSignUpResponse(organization, defaultCollection));
|
||||||
|
|
||||||
var providerOrganization =
|
var providerOrganization =
|
||||||
await sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user);
|
await sutProvider.Sut.CreateOrganizationAsync(provider.Id, organizationSignup, clientOwnerEmail, user);
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Bit.Commercial.Core.Billing;
|
using Bit.Commercial.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Billing;
|
using Bit.Core.Billing;
|
||||||
using Bit.Core.Billing.Constants;
|
using Bit.Core.Billing.Constants;
|
||||||
using Bit.Core.Billing.Entities;
|
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Billing.Repositories;
|
using Bit.Core.Billing.Providers.Entities;
|
||||||
|
using Bit.Core.Billing.Providers.Repositories;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Services;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -25,7 +25,7 @@ using NSubstitute;
|
|||||||
using Stripe;
|
using Stripe;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.Test.Billing;
|
namespace Bit.Commercial.Core.Test.Billing.Providers;
|
||||||
|
|
||||||
public class BusinessUnitConverterTests
|
public class BusinessUnitConverterTests
|
||||||
{
|
{
|
@ -1,7 +1,7 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Bit.Commercial.Core.Billing;
|
using Bit.Commercial.Core.Billing.Providers.Models;
|
||||||
using Bit.Commercial.Core.Billing.Models;
|
using Bit.Commercial.Core.Billing.Providers.Services;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
@ -10,13 +10,14 @@ using Bit.Core.AdminConsole.Models.Data.Provider;
|
|||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Billing.Caches;
|
using Bit.Core.Billing.Caches;
|
||||||
using Bit.Core.Billing.Constants;
|
using Bit.Core.Billing.Constants;
|
||||||
using Bit.Core.Billing.Entities;
|
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Models;
|
using Bit.Core.Billing.Models;
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Billing.Repositories;
|
using Bit.Core.Billing.Providers.Entities;
|
||||||
|
using Bit.Core.Billing.Providers.Models;
|
||||||
|
using Bit.Core.Billing.Providers.Repositories;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Services;
|
||||||
using Bit.Core.Billing.Services.Contracts;
|
using Bit.Core.Billing.Tax.Services;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
@ -39,7 +40,7 @@ using Customer = Stripe.Customer;
|
|||||||
using PaymentMethod = Stripe.PaymentMethod;
|
using PaymentMethod = Stripe.PaymentMethod;
|
||||||
using Subscription = Stripe.Subscription;
|
using Subscription = Stripe.Subscription;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.Test.Billing;
|
namespace Bit.Commercial.Core.Test.Billing.Providers;
|
||||||
|
|
||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
public class ProviderBillingServiceTests
|
public class ProviderBillingServiceTests
|
||||||
@ -261,7 +262,7 @@ public class ProviderBillingServiceTests
|
|||||||
};
|
};
|
||||||
|
|
||||||
sutProvider.GetDependency<ISubscriberService>().GetCustomerOrThrow(provider, Arg.Is<CustomerGetOptions>(
|
sutProvider.GetDependency<ISubscriberService>().GetCustomerOrThrow(provider, Arg.Is<CustomerGetOptions>(
|
||||||
options => options.Expand.FirstOrDefault() == "tax_ids"))
|
options => options.Expand.Contains("tax") && options.Expand.Contains("tax_ids")))
|
||||||
.Returns(providerCustomer);
|
.Returns(providerCustomer);
|
||||||
|
|
||||||
sutProvider.GetDependency<IGlobalSettings>().BaseServiceUri
|
sutProvider.GetDependency<IGlobalSettings>().BaseServiceUri
|
||||||
@ -311,6 +312,91 @@ public class ProviderBillingServiceTests
|
|||||||
org => org.GatewayCustomerId == "customer_id"));
|
org => org.GatewayCustomerId == "customer_id"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task CreateCustomer_ForClientOrg_ReverseCharge_Succeeds(
|
||||||
|
Provider provider,
|
||||||
|
Organization organization,
|
||||||
|
SutProvider<ProviderBillingService> sutProvider)
|
||||||
|
{
|
||||||
|
organization.GatewayCustomerId = null;
|
||||||
|
organization.Name = "Name";
|
||||||
|
organization.BusinessName = "BusinessName";
|
||||||
|
|
||||||
|
var providerCustomer = new Customer
|
||||||
|
{
|
||||||
|
Address = new Address
|
||||||
|
{
|
||||||
|
Country = "CA",
|
||||||
|
PostalCode = "12345",
|
||||||
|
Line1 = "123 Main St.",
|
||||||
|
Line2 = "Unit 4",
|
||||||
|
City = "Fake Town",
|
||||||
|
State = "Fake State"
|
||||||
|
},
|
||||||
|
TaxIds = new StripeList<TaxId>
|
||||||
|
{
|
||||||
|
Data =
|
||||||
|
[
|
||||||
|
new TaxId { Type = "TYPE", Value = "VALUE" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
sutProvider.GetDependency<ISubscriberService>().GetCustomerOrThrow(provider, Arg.Is<CustomerGetOptions>(
|
||||||
|
options => options.Expand.Contains("tax") && options.Expand.Contains("tax_ids")))
|
||||||
|
.Returns(providerCustomer);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().BaseServiceUri
|
||||||
|
.Returns(new Bit.Core.Settings.GlobalSettings.BaseServiceUriSettings(new Bit.Core.Settings.GlobalSettings())
|
||||||
|
{
|
||||||
|
CloudRegion = "US"
|
||||||
|
});
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
|
.IsEnabled(FeatureFlagKeys.PM21092_SetNonUSBusinessUseToReverseCharge).Returns(true);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IStripeAdapter>().CustomerCreateAsync(Arg.Is<CustomerCreateOptions>(
|
||||||
|
options =>
|
||||||
|
options.Address.Country == providerCustomer.Address.Country &&
|
||||||
|
options.Address.PostalCode == providerCustomer.Address.PostalCode &&
|
||||||
|
options.Address.Line1 == providerCustomer.Address.Line1 &&
|
||||||
|
options.Address.Line2 == providerCustomer.Address.Line2 &&
|
||||||
|
options.Address.City == providerCustomer.Address.City &&
|
||||||
|
options.Address.State == providerCustomer.Address.State &&
|
||||||
|
options.Name == organization.DisplayName() &&
|
||||||
|
options.Description == $"{provider.Name} Client Organization" &&
|
||||||
|
options.Email == provider.BillingEmail &&
|
||||||
|
options.InvoiceSettings.CustomFields.FirstOrDefault().Name == "Organization" &&
|
||||||
|
options.InvoiceSettings.CustomFields.FirstOrDefault().Value == "Name" &&
|
||||||
|
options.Metadata["region"] == "US" &&
|
||||||
|
options.TaxIdData.FirstOrDefault().Type == providerCustomer.TaxIds.FirstOrDefault().Type &&
|
||||||
|
options.TaxIdData.FirstOrDefault().Value == providerCustomer.TaxIds.FirstOrDefault().Value &&
|
||||||
|
options.TaxExempt == StripeConstants.TaxExempt.Reverse))
|
||||||
|
.Returns(new Customer { Id = "customer_id" });
|
||||||
|
|
||||||
|
await sutProvider.Sut.CreateCustomerForClientOrganization(provider, organization);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IStripeAdapter>().Received(1).CustomerCreateAsync(Arg.Is<CustomerCreateOptions>(
|
||||||
|
options =>
|
||||||
|
options.Address.Country == providerCustomer.Address.Country &&
|
||||||
|
options.Address.PostalCode == providerCustomer.Address.PostalCode &&
|
||||||
|
options.Address.Line1 == providerCustomer.Address.Line1 &&
|
||||||
|
options.Address.Line2 == providerCustomer.Address.Line2 &&
|
||||||
|
options.Address.City == providerCustomer.Address.City &&
|
||||||
|
options.Address.State == providerCustomer.Address.State &&
|
||||||
|
options.Name == organization.DisplayName() &&
|
||||||
|
options.Description == $"{provider.Name} Client Organization" &&
|
||||||
|
options.Email == provider.BillingEmail &&
|
||||||
|
options.InvoiceSettings.CustomFields.FirstOrDefault().Name == "Organization" &&
|
||||||
|
options.InvoiceSettings.CustomFields.FirstOrDefault().Value == "Name" &&
|
||||||
|
options.Metadata["region"] == "US" &&
|
||||||
|
options.TaxIdData.FirstOrDefault().Type == providerCustomer.TaxIds.FirstOrDefault().Type &&
|
||||||
|
options.TaxIdData.FirstOrDefault().Value == providerCustomer.TaxIds.FirstOrDefault().Value));
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IOrganizationRepository>().Received(1).ReplaceAsync(Arg.Is<Organization>(
|
||||||
|
org => org.GatewayCustomerId == "customer_id"));
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region GenerateClientInvoiceReport
|
#region GenerateClientInvoiceReport
|
||||||
@ -1181,6 +1267,62 @@ public class ProviderBillingServiceTests
|
|||||||
Assert.Equivalent(expected, actual);
|
Assert.Equivalent(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task SetupCustomer_WithCard_ReverseCharge_Success(
|
||||||
|
SutProvider<ProviderBillingService> sutProvider,
|
||||||
|
Provider provider,
|
||||||
|
TaxInfo taxInfo)
|
||||||
|
{
|
||||||
|
provider.Name = "MSP";
|
||||||
|
|
||||||
|
sutProvider.GetDependency<ITaxService>()
|
||||||
|
.GetStripeTaxCode(Arg.Is<string>(
|
||||||
|
p => p == taxInfo.BillingAddressCountry),
|
||||||
|
Arg.Is<string>(p => p == taxInfo.TaxIdNumber))
|
||||||
|
.Returns(taxInfo.TaxIdType);
|
||||||
|
|
||||||
|
taxInfo.BillingAddressCountry = "AD";
|
||||||
|
|
||||||
|
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||||
|
|
||||||
|
var expected = new Customer
|
||||||
|
{
|
||||||
|
Id = "customer_id",
|
||||||
|
Tax = new CustomerTax { AutomaticTax = StripeConstants.AutomaticTaxStatus.Supported }
|
||||||
|
};
|
||||||
|
|
||||||
|
var tokenizedPaymentSource = new TokenizedPaymentSource(PaymentMethodType.Card, "token");
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
|
.IsEnabled(FeatureFlagKeys.PM19956_RequireProviderPaymentMethodDuringSetup).Returns(true);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
|
.IsEnabled(FeatureFlagKeys.PM21092_SetNonUSBusinessUseToReverseCharge).Returns(true);
|
||||||
|
|
||||||
|
stripeAdapter.CustomerCreateAsync(Arg.Is<CustomerCreateOptions>(o =>
|
||||||
|
o.Address.Country == taxInfo.BillingAddressCountry &&
|
||||||
|
o.Address.PostalCode == taxInfo.BillingAddressPostalCode &&
|
||||||
|
o.Address.Line1 == taxInfo.BillingAddressLine1 &&
|
||||||
|
o.Address.Line2 == taxInfo.BillingAddressLine2 &&
|
||||||
|
o.Address.City == taxInfo.BillingAddressCity &&
|
||||||
|
o.Address.State == taxInfo.BillingAddressState &&
|
||||||
|
o.Description == WebUtility.HtmlDecode(provider.BusinessName) &&
|
||||||
|
o.Email == provider.BillingEmail &&
|
||||||
|
o.PaymentMethod == tokenizedPaymentSource.Token &&
|
||||||
|
o.InvoiceSettings.DefaultPaymentMethod == tokenizedPaymentSource.Token &&
|
||||||
|
o.InvoiceSettings.CustomFields.FirstOrDefault().Name == "Provider" &&
|
||||||
|
o.InvoiceSettings.CustomFields.FirstOrDefault().Value == "MSP" &&
|
||||||
|
o.Metadata["region"] == "" &&
|
||||||
|
o.TaxIdData.FirstOrDefault().Type == taxInfo.TaxIdType &&
|
||||||
|
o.TaxIdData.FirstOrDefault().Value == taxInfo.TaxIdNumber &&
|
||||||
|
o.TaxExempt == StripeConstants.TaxExempt.Reverse))
|
||||||
|
.Returns(expected);
|
||||||
|
|
||||||
|
var actual = await sutProvider.Sut.SetupCustomer(provider, taxInfo, tokenizedPaymentSource);
|
||||||
|
|
||||||
|
Assert.Equivalent(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory, BitAutoData]
|
[Theory, BitAutoData]
|
||||||
public async Task SetupCustomer_Throws_BadRequestException_WhenTaxIdIsInvalid(
|
public async Task SetupCustomer_Throws_BadRequestException_WhenTaxIdIsInvalid(
|
||||||
SutProvider<ProviderBillingService> sutProvider,
|
SutProvider<ProviderBillingService> sutProvider,
|
||||||
@ -1306,7 +1448,7 @@ public class ProviderBillingServiceTests
|
|||||||
.Returns(new Customer
|
.Returns(new Customer
|
||||||
{
|
{
|
||||||
Id = "customer_id",
|
Id = "customer_id",
|
||||||
Tax = new CustomerTax { AutomaticTax = StripeConstants.AutomaticTaxStatus.Supported }
|
Address = new Address { Country = "US" }
|
||||||
});
|
});
|
||||||
|
|
||||||
var providerPlans = new List<ProviderPlan>
|
var providerPlans = new List<ProviderPlan>
|
||||||
@ -1358,7 +1500,7 @@ public class ProviderBillingServiceTests
|
|||||||
var customer = new Customer
|
var customer = new Customer
|
||||||
{
|
{
|
||||||
Id = "customer_id",
|
Id = "customer_id",
|
||||||
Tax = new CustomerTax { AutomaticTax = StripeConstants.AutomaticTaxStatus.Supported }
|
Address = new Address { Country = "US" }
|
||||||
};
|
};
|
||||||
sutProvider.GetDependency<ISubscriberService>()
|
sutProvider.GetDependency<ISubscriberService>()
|
||||||
.GetCustomerOrThrow(
|
.GetCustomerOrThrow(
|
||||||
@ -1398,19 +1540,6 @@ public class ProviderBillingServiceTests
|
|||||||
|
|
||||||
var expected = new Subscription { Id = "subscription_id", Status = StripeConstants.SubscriptionStatus.Active };
|
var expected = new Subscription { Id = "subscription_id", Status = StripeConstants.SubscriptionStatus.Active };
|
||||||
|
|
||||||
sutProvider.GetDependency<IAutomaticTaxStrategy>()
|
|
||||||
.When(x => x.SetCreateOptions(
|
|
||||||
Arg.Is<SubscriptionCreateOptions>(options =>
|
|
||||||
options.Customer == "customer_id")
|
|
||||||
, Arg.Is<Customer>(p => p == customer)))
|
|
||||||
.Do(x =>
|
|
||||||
{
|
|
||||||
x.Arg<SubscriptionCreateOptions>().AutomaticTax = new SubscriptionAutomaticTaxOptions
|
|
||||||
{
|
|
||||||
Enabled = true
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IStripeAdapter>().SubscriptionCreateAsync(Arg.Is<SubscriptionCreateOptions>(
|
sutProvider.GetDependency<IStripeAdapter>().SubscriptionCreateAsync(Arg.Is<SubscriptionCreateOptions>(
|
||||||
sub =>
|
sub =>
|
||||||
sub.AutomaticTax.Enabled == true &&
|
sub.AutomaticTax.Enabled == true &&
|
||||||
@ -1442,11 +1571,11 @@ public class ProviderBillingServiceTests
|
|||||||
var customer = new Customer
|
var customer = new Customer
|
||||||
{
|
{
|
||||||
Id = "customer_id",
|
Id = "customer_id",
|
||||||
|
Address = new Address { Country = "US" },
|
||||||
InvoiceSettings = new CustomerInvoiceSettings
|
InvoiceSettings = new CustomerInvoiceSettings
|
||||||
{
|
{
|
||||||
DefaultPaymentMethodId = "pm_123"
|
DefaultPaymentMethodId = "pm_123"
|
||||||
},
|
}
|
||||||
Tax = new CustomerTax { AutomaticTax = StripeConstants.AutomaticTaxStatus.Supported }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
sutProvider.GetDependency<ISubscriberService>()
|
sutProvider.GetDependency<ISubscriberService>()
|
||||||
@ -1487,19 +1616,6 @@ public class ProviderBillingServiceTests
|
|||||||
|
|
||||||
var expected = new Subscription { Id = "subscription_id", Status = StripeConstants.SubscriptionStatus.Active };
|
var expected = new Subscription { Id = "subscription_id", Status = StripeConstants.SubscriptionStatus.Active };
|
||||||
|
|
||||||
sutProvider.GetDependency<IAutomaticTaxStrategy>()
|
|
||||||
.When(x => x.SetCreateOptions(
|
|
||||||
Arg.Is<SubscriptionCreateOptions>(options =>
|
|
||||||
options.Customer == "customer_id")
|
|
||||||
, Arg.Is<Customer>(p => p == customer)))
|
|
||||||
.Do(x =>
|
|
||||||
{
|
|
||||||
x.Arg<SubscriptionCreateOptions>().AutomaticTax = new SubscriptionAutomaticTaxOptions
|
|
||||||
{
|
|
||||||
Enabled = true
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
.IsEnabled(FeatureFlagKeys.PM19956_RequireProviderPaymentMethodDuringSetup).Returns(true);
|
.IsEnabled(FeatureFlagKeys.PM19956_RequireProviderPaymentMethodDuringSetup).Returns(true);
|
||||||
|
|
||||||
@ -1535,9 +1651,9 @@ public class ProviderBillingServiceTests
|
|||||||
var customer = new Customer
|
var customer = new Customer
|
||||||
{
|
{
|
||||||
Id = "customer_id",
|
Id = "customer_id",
|
||||||
|
Address = new Address { Country = "US" },
|
||||||
InvoiceSettings = new CustomerInvoiceSettings(),
|
InvoiceSettings = new CustomerInvoiceSettings(),
|
||||||
Metadata = new Dictionary<string, string>(),
|
Metadata = new Dictionary<string, string>()
|
||||||
Tax = new CustomerTax { AutomaticTax = StripeConstants.AutomaticTaxStatus.Supported }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
sutProvider.GetDependency<ISubscriberService>()
|
sutProvider.GetDependency<ISubscriberService>()
|
||||||
@ -1578,19 +1694,6 @@ public class ProviderBillingServiceTests
|
|||||||
|
|
||||||
var expected = new Subscription { Id = "subscription_id", Status = StripeConstants.SubscriptionStatus.Active };
|
var expected = new Subscription { Id = "subscription_id", Status = StripeConstants.SubscriptionStatus.Active };
|
||||||
|
|
||||||
sutProvider.GetDependency<IAutomaticTaxStrategy>()
|
|
||||||
.When(x => x.SetCreateOptions(
|
|
||||||
Arg.Is<SubscriptionCreateOptions>(options =>
|
|
||||||
options.Customer == "customer_id")
|
|
||||||
, Arg.Is<Customer>(p => p == customer)))
|
|
||||||
.Do(x =>
|
|
||||||
{
|
|
||||||
x.Arg<SubscriptionCreateOptions>().AutomaticTax = new SubscriptionAutomaticTaxOptions
|
|
||||||
{
|
|
||||||
Enabled = true
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
.IsEnabled(FeatureFlagKeys.PM19956_RequireProviderPaymentMethodDuringSetup).Returns(true);
|
.IsEnabled(FeatureFlagKeys.PM19956_RequireProviderPaymentMethodDuringSetup).Returns(true);
|
||||||
|
|
||||||
@ -1645,12 +1748,15 @@ public class ProviderBillingServiceTests
|
|||||||
var customer = new Customer
|
var customer = new Customer
|
||||||
{
|
{
|
||||||
Id = "customer_id",
|
Id = "customer_id",
|
||||||
|
Address = new Address
|
||||||
|
{
|
||||||
|
Country = "US"
|
||||||
|
},
|
||||||
InvoiceSettings = new CustomerInvoiceSettings(),
|
InvoiceSettings = new CustomerInvoiceSettings(),
|
||||||
Metadata = new Dictionary<string, string>
|
Metadata = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
["btCustomerId"] = "braintree_customer_id"
|
["btCustomerId"] = "braintree_customer_id"
|
||||||
},
|
}
|
||||||
Tax = new CustomerTax { AutomaticTax = StripeConstants.AutomaticTaxStatus.Supported }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
sutProvider.GetDependency<ISubscriberService>()
|
sutProvider.GetDependency<ISubscriberService>()
|
||||||
@ -1691,22 +1797,92 @@ public class ProviderBillingServiceTests
|
|||||||
|
|
||||||
var expected = new Subscription { Id = "subscription_id", Status = StripeConstants.SubscriptionStatus.Active };
|
var expected = new Subscription { Id = "subscription_id", Status = StripeConstants.SubscriptionStatus.Active };
|
||||||
|
|
||||||
sutProvider.GetDependency<IAutomaticTaxStrategy>()
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
.When(x => x.SetCreateOptions(
|
.IsEnabled(FeatureFlagKeys.PM19956_RequireProviderPaymentMethodDuringSetup).Returns(true);
|
||||||
Arg.Is<SubscriptionCreateOptions>(options =>
|
|
||||||
options.Customer == "customer_id")
|
sutProvider.GetDependency<IStripeAdapter>().SubscriptionCreateAsync(Arg.Is<SubscriptionCreateOptions>(
|
||||||
, Arg.Is<Customer>(p => p == customer)))
|
sub =>
|
||||||
.Do(x =>
|
sub.AutomaticTax.Enabled == true &&
|
||||||
|
sub.CollectionMethod == StripeConstants.CollectionMethod.ChargeAutomatically &&
|
||||||
|
sub.Customer == "customer_id" &&
|
||||||
|
sub.DaysUntilDue == null &&
|
||||||
|
sub.Items.Count == 2 &&
|
||||||
|
sub.Items.ElementAt(0).Price == ProviderPriceAdapter.MSP.Active.Teams &&
|
||||||
|
sub.Items.ElementAt(0).Quantity == 100 &&
|
||||||
|
sub.Items.ElementAt(1).Price == ProviderPriceAdapter.MSP.Active.Enterprise &&
|
||||||
|
sub.Items.ElementAt(1).Quantity == 100 &&
|
||||||
|
sub.Metadata["providerId"] == provider.Id.ToString() &&
|
||||||
|
sub.OffSession == true &&
|
||||||
|
sub.ProrationBehavior == StripeConstants.ProrationBehavior.CreateProrations &&
|
||||||
|
sub.TrialPeriodDays == 14)).Returns(expected);
|
||||||
|
|
||||||
|
var actual = await sutProvider.Sut.SetupSubscription(provider);
|
||||||
|
|
||||||
|
Assert.Equivalent(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, BitAutoData]
|
||||||
|
public async Task SetupSubscription_ReverseCharge_Succeeds(
|
||||||
|
SutProvider<ProviderBillingService> sutProvider,
|
||||||
|
Provider provider)
|
||||||
{
|
{
|
||||||
x.Arg<SubscriptionCreateOptions>().AutomaticTax = new SubscriptionAutomaticTaxOptions
|
provider.Type = ProviderType.Msp;
|
||||||
|
provider.GatewaySubscriptionId = null;
|
||||||
|
|
||||||
|
var customer = new Customer
|
||||||
{
|
{
|
||||||
Enabled = true
|
Id = "customer_id",
|
||||||
|
Address = new Address { Country = "CA" },
|
||||||
|
InvoiceSettings = new CustomerInvoiceSettings
|
||||||
|
{
|
||||||
|
DefaultPaymentMethodId = "pm_123"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
|
||||||
|
sutProvider.GetDependency<ISubscriberService>()
|
||||||
|
.GetCustomerOrThrow(
|
||||||
|
provider,
|
||||||
|
Arg.Is<CustomerGetOptions>(p => p.Expand.Contains("tax") || p.Expand.Contains("tax_ids"))).Returns(customer);
|
||||||
|
|
||||||
|
var providerPlans = new List<ProviderPlan>
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
ProviderId = provider.Id,
|
||||||
|
PlanType = PlanType.TeamsMonthly,
|
||||||
|
SeatMinimum = 100,
|
||||||
|
PurchasedSeats = 0,
|
||||||
|
AllocatedSeats = 0
|
||||||
|
},
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
ProviderId = provider.Id,
|
||||||
|
PlanType = PlanType.EnterpriseMonthly,
|
||||||
|
SeatMinimum = 100,
|
||||||
|
PurchasedSeats = 0,
|
||||||
|
AllocatedSeats = 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var plan in providerPlans)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IPricingClient>().GetPlanOrThrow(plan.PlanType)
|
||||||
|
.Returns(StaticStore.GetPlan(plan.PlanType));
|
||||||
|
}
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IProviderPlanRepository>().GetByProviderId(provider.Id)
|
||||||
|
.Returns(providerPlans);
|
||||||
|
|
||||||
|
var expected = new Subscription { Id = "subscription_id", Status = StripeConstants.SubscriptionStatus.Active };
|
||||||
|
|
||||||
sutProvider.GetDependency<IFeatureService>()
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
.IsEnabled(FeatureFlagKeys.PM19956_RequireProviderPaymentMethodDuringSetup).Returns(true);
|
.IsEnabled(FeatureFlagKeys.PM19956_RequireProviderPaymentMethodDuringSetup).Returns(true);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IFeatureService>()
|
||||||
|
.IsEnabled(FeatureFlagKeys.PM21092_SetNonUSBusinessUseToReverseCharge).Returns(true);
|
||||||
|
|
||||||
sutProvider.GetDependency<IStripeAdapter>().SubscriptionCreateAsync(Arg.Is<SubscriptionCreateOptions>(
|
sutProvider.GetDependency<IStripeAdapter>().SubscriptionCreateAsync(Arg.Is<SubscriptionCreateOptions>(
|
||||||
sub =>
|
sub =>
|
||||||
sub.AutomaticTax.Enabled == true &&
|
sub.AutomaticTax.Enabled == true &&
|
@ -1,11 +1,11 @@
|
|||||||
using Bit.Commercial.Core.Billing;
|
using Bit.Commercial.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Stripe;
|
using Stripe;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.Test.Billing;
|
namespace Bit.Commercial.Core.Test.Billing.Providers;
|
||||||
|
|
||||||
public class ProviderPriceAdapterTests
|
public class ProviderPriceAdapterTests
|
||||||
{
|
{
|
@ -1,9 +1,9 @@
|
|||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Tax.Services.Implementations;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.Test.Billing;
|
namespace Bit.Commercial.Core.Test.Billing.Tax;
|
||||||
|
|
||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
public class TaxServiceTests
|
public class TaxServiceTests
|
@ -1,14 +1,10 @@
|
|||||||
using System.Security.Claims;
|
using Bit.Commercial.Core.SecretsManager.Queries.Projects;
|
||||||
using Bit.Commercial.Core.SecretsManager.Queries.Projects;
|
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Licenses;
|
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Business;
|
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Bit.Core.Services;
|
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
@ -22,11 +18,26 @@ namespace Bit.Commercial.Core.Test.SecretsManager.Queries.Projects;
|
|||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
public class MaxProjectsQueryTests
|
public class MaxProjectsQueryTests
|
||||||
{
|
{
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task GetByOrgIdAsync_SelfHosted_ReturnsNulls(SutProvider<MaxProjectsQuery> sutProvider,
|
||||||
|
Guid organizationId)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true);
|
||||||
|
|
||||||
|
var (max, overMax) = await sutProvider.Sut.GetByOrgIdAsync(organizationId, 1);
|
||||||
|
|
||||||
|
Assert.Null(max);
|
||||||
|
Assert.Null(overMax);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task GetByOrgIdAsync_OrganizationIsNull_ThrowsNotFound(SutProvider<MaxProjectsQuery> sutProvider,
|
public async Task GetByOrgIdAsync_OrganizationIsNull_ThrowsNotFound(SutProvider<MaxProjectsQuery> sutProvider,
|
||||||
Guid organizationId)
|
Guid organizationId)
|
||||||
{
|
{
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(false);
|
||||||
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(default).ReturnsNull();
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(default).ReturnsNull();
|
||||||
|
|
||||||
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.GetByOrgIdAsync(organizationId, 1));
|
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.GetByOrgIdAsync(organizationId, 1));
|
||||||
@ -35,54 +46,6 @@ public class MaxProjectsQueryTests
|
|||||||
.GetProjectCountByOrganizationIdAsync(organizationId);
|
.GetProjectCountByOrganizationIdAsync(organizationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(PlanType.FamiliesAnnually2019)]
|
|
||||||
[BitAutoData(PlanType.Custom)]
|
|
||||||
[BitAutoData(PlanType.FamiliesAnnually)]
|
|
||||||
public async Task GetByOrgIdAsync_Cloud_SmPlanIsNull_ThrowsBadRequest(PlanType planType,
|
|
||||||
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
|
||||||
{
|
|
||||||
organization.PlanType = planType;
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>()
|
|
||||||
.GetByIdAsync(organization.Id)
|
|
||||||
.Returns(organization);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(false);
|
|
||||||
var plan = StaticStore.GetPlan(planType);
|
|
||||||
sutProvider.GetDependency<IPricingClient>().GetPlan(organization.PlanType).Returns(plan);
|
|
||||||
|
|
||||||
await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
async () => await sutProvider.Sut.GetByOrgIdAsync(organization.Id, 1));
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IProjectRepository>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.GetProjectCountByOrganizationIdAsync(organization.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData]
|
|
||||||
public async Task GetByOrgIdAsync_SelfHosted_NoMaxProjectsClaim_ThrowsBadRequest(
|
|
||||||
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>()
|
|
||||||
.GetByIdAsync(organization.Id)
|
|
||||||
.Returns(organization);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true);
|
|
||||||
|
|
||||||
var license = new OrganizationLicense();
|
|
||||||
var claimsPrincipal = new ClaimsPrincipal();
|
|
||||||
sutProvider.GetDependency<ILicensingService>().ReadOrganizationLicenseAsync(organization).Returns(license);
|
|
||||||
sutProvider.GetDependency<ILicensingService>().GetClaimsPrincipalFromLicense(license).Returns(claimsPrincipal);
|
|
||||||
|
|
||||||
await Assert.ThrowsAsync<BadRequestException>(
|
|
||||||
async () => await sutProvider.Sut.GetByOrgIdAsync(organization.Id, 1));
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IProjectRepository>()
|
|
||||||
.DidNotReceiveWithAnyArgs()
|
|
||||||
.GetProjectCountByOrganizationIdAsync(organization.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData(PlanType.TeamsMonthly2019)]
|
[BitAutoData(PlanType.TeamsMonthly2019)]
|
||||||
[BitAutoData(PlanType.TeamsMonthly2020)]
|
[BitAutoData(PlanType.TeamsMonthly2020)]
|
||||||
@ -97,57 +60,16 @@ public class MaxProjectsQueryTests
|
|||||||
[BitAutoData(PlanType.EnterpriseAnnually2019)]
|
[BitAutoData(PlanType.EnterpriseAnnually2019)]
|
||||||
[BitAutoData(PlanType.EnterpriseAnnually2020)]
|
[BitAutoData(PlanType.EnterpriseAnnually2020)]
|
||||||
[BitAutoData(PlanType.EnterpriseAnnually)]
|
[BitAutoData(PlanType.EnterpriseAnnually)]
|
||||||
public async Task GetByOrgIdAsync_Cloud_SmNoneFreePlans_ReturnsNull(PlanType planType,
|
public async Task GetByOrgIdAsync_SmNoneFreePlans_ReturnsNull(PlanType planType,
|
||||||
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
||||||
{
|
{
|
||||||
organization.PlanType = planType;
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
|
|
||||||
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(false);
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(false);
|
||||||
var plan = StaticStore.GetPlan(planType);
|
|
||||||
sutProvider.GetDependency<IPricingClient>().GetPlan(organization.PlanType).Returns(plan);
|
|
||||||
|
|
||||||
var (limit, overLimit) = await sutProvider.Sut.GetByOrgIdAsync(organization.Id, 1);
|
|
||||||
|
|
||||||
Assert.Null(limit);
|
|
||||||
Assert.Null(overLimit);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IProjectRepository>().DidNotReceiveWithAnyArgs()
|
|
||||||
.GetProjectCountByOrganizationIdAsync(organization.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(PlanType.TeamsMonthly2019)]
|
|
||||||
[BitAutoData(PlanType.TeamsMonthly2020)]
|
|
||||||
[BitAutoData(PlanType.TeamsMonthly)]
|
|
||||||
[BitAutoData(PlanType.TeamsAnnually2019)]
|
|
||||||
[BitAutoData(PlanType.TeamsAnnually2020)]
|
|
||||||
[BitAutoData(PlanType.TeamsAnnually)]
|
|
||||||
[BitAutoData(PlanType.TeamsStarter)]
|
|
||||||
[BitAutoData(PlanType.EnterpriseMonthly2019)]
|
|
||||||
[BitAutoData(PlanType.EnterpriseMonthly2020)]
|
|
||||||
[BitAutoData(PlanType.EnterpriseMonthly)]
|
|
||||||
[BitAutoData(PlanType.EnterpriseAnnually2019)]
|
|
||||||
[BitAutoData(PlanType.EnterpriseAnnually2020)]
|
|
||||||
[BitAutoData(PlanType.EnterpriseAnnually)]
|
|
||||||
public async Task GetByOrgIdAsync_SelfHosted_SmNoneFreePlans_ReturnsNull(PlanType planType,
|
|
||||||
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
|
||||||
{
|
|
||||||
organization.PlanType = planType;
|
organization.PlanType = planType;
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true);
|
|
||||||
|
|
||||||
var license = new OrganizationLicense();
|
sutProvider.GetDependency<IPricingClient>().GetPlan(organization.PlanType)
|
||||||
var plan = StaticStore.GetPlan(planType);
|
.Returns(StaticStore.GetPlan(organization.PlanType));
|
||||||
var claims = new List<Claim>
|
|
||||||
{
|
|
||||||
new (nameof(OrganizationLicenseConstants.PlanType), organization.PlanType.ToString()),
|
|
||||||
new (nameof(OrganizationLicenseConstants.SmMaxProjects), plan.SecretsManager.MaxProjects.ToString())
|
|
||||||
};
|
|
||||||
var identity = new ClaimsIdentity(claims, "TestAuthenticationType");
|
|
||||||
var claimsPrincipal = new ClaimsPrincipal(identity);
|
|
||||||
sutProvider.GetDependency<ILicensingService>().ReadOrganizationLicenseAsync(organization).Returns(license);
|
|
||||||
sutProvider.GetDependency<ILicensingService>().GetClaimsPrincipalFromLicense(license).Returns(claimsPrincipal);
|
|
||||||
|
|
||||||
var (limit, overLimit) = await sutProvider.Sut.GetByOrgIdAsync(organization.Id, 1);
|
var (limit, overLimit) = await sutProvider.Sut.GetByOrgIdAsync(organization.Id, 1);
|
||||||
|
|
||||||
@ -183,7 +105,7 @@ public class MaxProjectsQueryTests
|
|||||||
[BitAutoData(PlanType.Free, 3, 4, true)]
|
[BitAutoData(PlanType.Free, 3, 4, true)]
|
||||||
[BitAutoData(PlanType.Free, 4, 4, true)]
|
[BitAutoData(PlanType.Free, 4, 4, true)]
|
||||||
[BitAutoData(PlanType.Free, 40, 4, true)]
|
[BitAutoData(PlanType.Free, 40, 4, true)]
|
||||||
public async Task GetByOrgIdAsync_Cloud_SmFreePlan__Success(PlanType planType, int projects, int projectsToAdd, bool expectedOverMax,
|
public async Task GetByOrgIdAsync_SmFreePlan__Success(PlanType planType, int projects, int projectsToAdd, bool expectedOverMax,
|
||||||
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
||||||
{
|
{
|
||||||
organization.PlanType = planType;
|
organization.PlanType = planType;
|
||||||
@ -191,66 +113,8 @@ public class MaxProjectsQueryTests
|
|||||||
sutProvider.GetDependency<IProjectRepository>().GetProjectCountByOrganizationIdAsync(organization.Id)
|
sutProvider.GetDependency<IProjectRepository>().GetProjectCountByOrganizationIdAsync(organization.Id)
|
||||||
.Returns(projects);
|
.Returns(projects);
|
||||||
|
|
||||||
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(false);
|
sutProvider.GetDependency<IPricingClient>().GetPlan(organization.PlanType)
|
||||||
var plan = StaticStore.GetPlan(planType);
|
.Returns(StaticStore.GetPlan(organization.PlanType));
|
||||||
sutProvider.GetDependency<IPricingClient>().GetPlan(organization.PlanType).Returns(plan);
|
|
||||||
|
|
||||||
var (max, overMax) = await sutProvider.Sut.GetByOrgIdAsync(organization.Id, projectsToAdd);
|
|
||||||
|
|
||||||
Assert.NotNull(max);
|
|
||||||
Assert.NotNull(overMax);
|
|
||||||
Assert.Equal(3, max.Value);
|
|
||||||
Assert.Equal(expectedOverMax, overMax);
|
|
||||||
|
|
||||||
await sutProvider.GetDependency<IProjectRepository>().Received(1)
|
|
||||||
.GetProjectCountByOrganizationIdAsync(organization.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData(PlanType.Free, 0, 1, false)]
|
|
||||||
[BitAutoData(PlanType.Free, 1, 1, false)]
|
|
||||||
[BitAutoData(PlanType.Free, 2, 1, false)]
|
|
||||||
[BitAutoData(PlanType.Free, 3, 1, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 4, 1, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 40, 1, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 0, 2, false)]
|
|
||||||
[BitAutoData(PlanType.Free, 1, 2, false)]
|
|
||||||
[BitAutoData(PlanType.Free, 2, 2, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 3, 2, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 4, 2, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 40, 2, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 0, 3, false)]
|
|
||||||
[BitAutoData(PlanType.Free, 1, 3, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 2, 3, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 3, 3, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 4, 3, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 40, 3, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 0, 4, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 1, 4, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 2, 4, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 3, 4, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 4, 4, true)]
|
|
||||||
[BitAutoData(PlanType.Free, 40, 4, true)]
|
|
||||||
public async Task GetByOrgIdAsync_SelfHosted_SmFreePlan__Success(PlanType planType, int projects, int projectsToAdd, bool expectedOverMax,
|
|
||||||
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
|
||||||
{
|
|
||||||
organization.PlanType = planType;
|
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
|
||||||
sutProvider.GetDependency<IProjectRepository>().GetProjectCountByOrganizationIdAsync(organization.Id)
|
|
||||||
.Returns(projects);
|
|
||||||
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true);
|
|
||||||
|
|
||||||
var license = new OrganizationLicense();
|
|
||||||
var plan = StaticStore.GetPlan(planType);
|
|
||||||
var claims = new List<Claim>
|
|
||||||
{
|
|
||||||
new (nameof(OrganizationLicenseConstants.PlanType), organization.PlanType.ToString()),
|
|
||||||
new (nameof(OrganizationLicenseConstants.SmMaxProjects), plan.SecretsManager.MaxProjects.ToString())
|
|
||||||
};
|
|
||||||
var identity = new ClaimsIdentity(claims, "TestAuthenticationType");
|
|
||||||
var claimsPrincipal = new ClaimsPrincipal(identity);
|
|
||||||
sutProvider.GetDependency<ILicensingService>().ReadOrganizationLicenseAsync(organization).Returns(license);
|
|
||||||
sutProvider.GetDependency<ILicensingService>().GetClaimsPrincipalFromLicense(license).Returns(claimsPrincipal);
|
|
||||||
|
|
||||||
var (max, overMax) = await sutProvider.Sut.GetByOrgIdAsync(organization.Id, projectsToAdd);
|
var (max, overMax) = await sutProvider.Sut.GetByOrgIdAsync(organization.Id, projectsToAdd);
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ MAILCATCHER_PORT=1080
|
|||||||
# Alternative databases
|
# Alternative databases
|
||||||
POSTGRES_PASSWORD=SET_A_PASSWORD_HERE_123
|
POSTGRES_PASSWORD=SET_A_PASSWORD_HERE_123
|
||||||
MYSQL_ROOT_PASSWORD=SET_A_PASSWORD_HERE_123
|
MYSQL_ROOT_PASSWORD=SET_A_PASSWORD_HERE_123
|
||||||
|
MARIADB_ROOT_PASSWORD=SET_A_PASSWORD_HERE_123
|
||||||
|
|
||||||
# IdP configuration
|
# IdP configuration
|
||||||
# Complete using the values from the Manage SSO page in the web vault
|
# Complete using the values from the Manage SSO page in the web vault
|
||||||
|
@ -70,6 +70,20 @@ services:
|
|||||||
profiles:
|
profiles:
|
||||||
- mysql
|
- mysql
|
||||||
|
|
||||||
|
mariadb:
|
||||||
|
image: mariadb:10
|
||||||
|
ports:
|
||||||
|
- 4306:3306
|
||||||
|
environment:
|
||||||
|
MARIADB_USER: maria
|
||||||
|
MARIADB_PASSWORD: ${MARIADB_ROOT_PASSWORD}
|
||||||
|
MARIADB_DATABASE: vault_dev
|
||||||
|
MARIADB_RANDOM_ROOT_PASSWORD: "true"
|
||||||
|
volumes:
|
||||||
|
- mariadb_dev_data:/var/lib/mysql
|
||||||
|
profiles:
|
||||||
|
- mariadb
|
||||||
|
|
||||||
idp:
|
idp:
|
||||||
image: kenchan0130/simplesamlphp:1.19.8
|
image: kenchan0130/simplesamlphp:1.19.8
|
||||||
container_name: idp
|
container_name: idp
|
||||||
@ -85,7 +99,7 @@ services:
|
|||||||
- idp
|
- idp
|
||||||
|
|
||||||
rabbitmq:
|
rabbitmq:
|
||||||
image: rabbitmq:management
|
image: rabbitmq:4.1.0-management
|
||||||
container_name: rabbitmq
|
container_name: rabbitmq
|
||||||
ports:
|
ports:
|
||||||
- "5672:5672"
|
- "5672:5672"
|
||||||
@ -94,7 +108,7 @@ services:
|
|||||||
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER}
|
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER}
|
||||||
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}
|
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}
|
||||||
volumes:
|
volumes:
|
||||||
- rabbitmq_data:/var/lib/rabbitmq_data
|
- rabbitmq_data:/var/lib/rabbitmq
|
||||||
profiles:
|
profiles:
|
||||||
- rabbitmq
|
- rabbitmq
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ param(
|
|||||||
[switch]$all,
|
[switch]$all,
|
||||||
[switch]$postgres,
|
[switch]$postgres,
|
||||||
[switch]$mysql,
|
[switch]$mysql,
|
||||||
|
[switch]$mariadb,
|
||||||
[switch]$mssql,
|
[switch]$mssql,
|
||||||
[switch]$sqlite,
|
[switch]$sqlite,
|
||||||
[switch]$selfhost,
|
[switch]$selfhost,
|
||||||
@ -15,11 +16,15 @@ param(
|
|||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
$currentDir = Get-Location
|
$currentDir = Get-Location
|
||||||
|
|
||||||
if (!$all -and !$postgres -and !$mysql -and !$sqlite) {
|
function Get-IsEFDatabase {
|
||||||
|
return $postgres -or $mysql -or $mariadb -or $sqlite;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$all -and !$(Get-IsEFDatabase)) {
|
||||||
$mssql = $true;
|
$mssql = $true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($all -or $postgres -or $mysql -or $sqlite) {
|
if ($all -or $(Get-IsEFDatabase)) {
|
||||||
dotnet ef *> $null
|
dotnet ef *> $null
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
Write-Host "Entity Framework Core tools were not found in the dotnet global tools. Attempting to install"
|
Write-Host "Entity Framework Core tools were not found in the dotnet global tools. Attempting to install"
|
||||||
@ -60,9 +65,12 @@ if ($all -or $mssql) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Foreach ($item in @(
|
Foreach ($item in @(
|
||||||
@($mysql, "MySQL", "MySqlMigrations", "mySql", 2),
|
|
||||||
@($postgres, "PostgreSQL", "PostgresMigrations", "postgreSql", 0),
|
@($postgres, "PostgreSQL", "PostgresMigrations", "postgreSql", 0),
|
||||||
@($sqlite, "SQLite", "SqliteMigrations", "sqlite", 1)
|
@($sqlite, "SQLite", "SqliteMigrations", "sqlite", 1),
|
||||||
|
@($mysql, "MySQL", "MySqlMigrations", "mySql", 2),
|
||||||
|
# MariaDB shares the MySQL connection string in the server config so they are mutually exclusive in that context.
|
||||||
|
# However they can still be run independently for integration tests.
|
||||||
|
@($mariadb, "MariaDB", "MySqlMigrations", "mySql", 3)
|
||||||
)) {
|
)) {
|
||||||
if (!$item[0] -and !$all) {
|
if (!$item[0] -and !$all) {
|
||||||
continue
|
continue
|
||||||
|
@ -33,6 +33,39 @@
|
|||||||
"Name": "events-webhook-subscription"
|
"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");
|
AllowedMethods = @("Get", "PUT");
|
||||||
});
|
});
|
||||||
$containers = "attachments", "sendfiles", "misc";
|
$containers = "attachments", "sendfiles", "misc";
|
||||||
$queues = "event", "notifications", "reference-events", "mail";
|
$queues = "event", "notifications", "mail";
|
||||||
$tables = "event", "metadata", "installationdevice";
|
$tables = "event", "metadata", "installationdevice";
|
||||||
# End configuration
|
# End configuration
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
"rollForward": "latestFeature"
|
"rollForward": "latestFeature"
|
||||||
},
|
},
|
||||||
"msbuild-sdks": {
|
"msbuild-sdks": {
|
||||||
"Microsoft.Build.Traversal": "4.1.0"
|
"Microsoft.Build.Traversal": "4.1.0",
|
||||||
|
"Microsoft.Build.Sql": "1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,6 @@ export function authenticate(
|
|||||||
payload["deviceName"] = "chrome";
|
payload["deviceName"] = "chrome";
|
||||||
payload["username"] = username;
|
payload["username"] = username;
|
||||||
payload["password"] = password;
|
payload["password"] = password;
|
||||||
|
|
||||||
params.headers["Auth-Email"] = encoding.b64encode(username);
|
|
||||||
} else {
|
} else {
|
||||||
payload["scope"] = "api.organization";
|
payload["scope"] = "api.organization";
|
||||||
payload["grant_type"] = "client_credentials";
|
payload["grant_type"] = "client_credentials";
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
*
|
|
||||||
!obj/build-output/publish/*
|
|
||||||
!obj/Docker/empty/
|
|
||||||
!entrypoint.sh
|
|
@ -11,8 +11,7 @@ using Bit.Core.AdminConsole.Repositories;
|
|||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Extensions;
|
using Bit.Core.Billing.Extensions;
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.Context;
|
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.OrganizationConnectionConfigs;
|
using Bit.Core.Models.OrganizationConnectionConfigs;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
|
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
|
||||||
@ -20,9 +19,6 @@ using Bit.Core.Repositories;
|
|||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
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.Utilities;
|
||||||
using Bit.Core.Vault.Repositories;
|
using Bit.Core.Vault.Repositories;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
@ -45,12 +41,9 @@ public class OrganizationsController : Controller
|
|||||||
private readonly IPaymentService _paymentService;
|
private readonly IPaymentService _paymentService;
|
||||||
private readonly IApplicationCacheService _applicationCacheService;
|
private readonly IApplicationCacheService _applicationCacheService;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
private readonly IReferenceEventService _referenceEventService;
|
|
||||||
private readonly IUserService _userService;
|
|
||||||
private readonly IProviderRepository _providerRepository;
|
private readonly IProviderRepository _providerRepository;
|
||||||
private readonly ILogger<OrganizationsController> _logger;
|
private readonly ILogger<OrganizationsController> _logger;
|
||||||
private readonly IAccessControlService _accessControlService;
|
private readonly IAccessControlService _accessControlService;
|
||||||
private readonly ICurrentContext _currentContext;
|
|
||||||
private readonly ISecretRepository _secretRepository;
|
private readonly ISecretRepository _secretRepository;
|
||||||
private readonly IProjectRepository _projectRepository;
|
private readonly IProjectRepository _projectRepository;
|
||||||
private readonly IServiceAccountRepository _serviceAccountRepository;
|
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||||
@ -73,12 +66,9 @@ public class OrganizationsController : Controller
|
|||||||
IPaymentService paymentService,
|
IPaymentService paymentService,
|
||||||
IApplicationCacheService applicationCacheService,
|
IApplicationCacheService applicationCacheService,
|
||||||
GlobalSettings globalSettings,
|
GlobalSettings globalSettings,
|
||||||
IReferenceEventService referenceEventService,
|
|
||||||
IUserService userService,
|
|
||||||
IProviderRepository providerRepository,
|
IProviderRepository providerRepository,
|
||||||
ILogger<OrganizationsController> logger,
|
ILogger<OrganizationsController> logger,
|
||||||
IAccessControlService accessControlService,
|
IAccessControlService accessControlService,
|
||||||
ICurrentContext currentContext,
|
|
||||||
ISecretRepository secretRepository,
|
ISecretRepository secretRepository,
|
||||||
IProjectRepository projectRepository,
|
IProjectRepository projectRepository,
|
||||||
IServiceAccountRepository serviceAccountRepository,
|
IServiceAccountRepository serviceAccountRepository,
|
||||||
@ -100,12 +90,9 @@ public class OrganizationsController : Controller
|
|||||||
_paymentService = paymentService;
|
_paymentService = paymentService;
|
||||||
_applicationCacheService = applicationCacheService;
|
_applicationCacheService = applicationCacheService;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
_referenceEventService = referenceEventService;
|
|
||||||
_userService = userService;
|
|
||||||
_providerRepository = providerRepository;
|
_providerRepository = providerRepository;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_accessControlService = accessControlService;
|
_accessControlService = accessControlService;
|
||||||
_currentContext = currentContext;
|
|
||||||
_secretRepository = secretRepository;
|
_secretRepository = secretRepository;
|
||||||
_projectRepository = projectRepository;
|
_projectRepository = projectRepository;
|
||||||
_serviceAccountRepository = serviceAccountRepository;
|
_serviceAccountRepository = serviceAccountRepository;
|
||||||
@ -272,11 +259,6 @@ public class OrganizationsController : Controller
|
|||||||
await _organizationRepository.ReplaceAsync(organization);
|
await _organizationRepository.ReplaceAsync(organization);
|
||||||
|
|
||||||
await _applicationCacheService.UpsertOrganizationAbilityAsync(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 });
|
return RedirectToAction("Edit", new { id });
|
||||||
}
|
}
|
||||||
@ -462,6 +444,7 @@ public class OrganizationsController : Controller
|
|||||||
organization.UsersGetPremium = model.UsersGetPremium;
|
organization.UsersGetPremium = model.UsersGetPremium;
|
||||||
organization.UseSecretsManager = model.UseSecretsManager;
|
organization.UseSecretsManager = model.UseSecretsManager;
|
||||||
organization.UseRiskInsights = model.UseRiskInsights;
|
organization.UseRiskInsights = model.UseRiskInsights;
|
||||||
|
organization.UseOrganizationDomains = model.UseOrganizationDomains;
|
||||||
organization.UseAdminSponsoredFamilies = model.UseAdminSponsoredFamilies;
|
organization.UseAdminSponsoredFamilies = model.UseAdminSponsoredFamilies;
|
||||||
|
|
||||||
//secrets
|
//secrets
|
||||||
|
@ -10,13 +10,13 @@ using Bit.Core.AdminConsole.Providers.Interfaces;
|
|||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
using Bit.Core.Billing.Constants;
|
using Bit.Core.Billing.Constants;
|
||||||
using Bit.Core.Billing.Entities;
|
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Extensions;
|
using Bit.Core.Billing.Extensions;
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Billing.Repositories;
|
using Bit.Core.Billing.Providers.Entities;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Providers.Models;
|
||||||
using Bit.Core.Billing.Services.Contracts;
|
using Bit.Core.Billing.Providers.Repositories;
|
||||||
|
using Bit.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
@ -102,7 +102,7 @@ public class OrganizationEditModel : OrganizationViewModel
|
|||||||
MaxAutoscaleSmSeats = org.MaxAutoscaleSmSeats;
|
MaxAutoscaleSmSeats = org.MaxAutoscaleSmSeats;
|
||||||
SmServiceAccounts = org.SmServiceAccounts;
|
SmServiceAccounts = org.SmServiceAccounts;
|
||||||
MaxAutoscaleSmServiceAccounts = org.MaxAutoscaleSmServiceAccounts;
|
MaxAutoscaleSmServiceAccounts = org.MaxAutoscaleSmServiceAccounts;
|
||||||
|
UseOrganizationDomains = org.UseOrganizationDomains;
|
||||||
_plans = plans;
|
_plans = plans;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,6 +186,8 @@ public class OrganizationEditModel : OrganizationViewModel
|
|||||||
public int? SmServiceAccounts { get; set; }
|
public int? SmServiceAccounts { get; set; }
|
||||||
[Display(Name = "Max Autoscale Machine Accounts")]
|
[Display(Name = "Max Autoscale Machine Accounts")]
|
||||||
public int? MaxAutoscaleSmServiceAccounts { get; set; }
|
public int? MaxAutoscaleSmServiceAccounts { get; set; }
|
||||||
|
[Display(Name = "Use Organization Domains")]
|
||||||
|
public bool UseOrganizationDomains { get; set; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Plan[] object for use in Javascript
|
* Creates a Plan[] object for use in Javascript
|
||||||
@ -215,6 +217,7 @@ public class OrganizationEditModel : OrganizationViewModel
|
|||||||
Has2fa = p.Has2fa,
|
Has2fa = p.Has2fa,
|
||||||
HasApi = p.HasApi,
|
HasApi = p.HasApi,
|
||||||
HasSso = p.HasSso,
|
HasSso = p.HasSso,
|
||||||
|
HasOrganizationDomains = p.HasOrganizationDomains,
|
||||||
HasKeyConnector = p.HasKeyConnector,
|
HasKeyConnector = p.HasKeyConnector,
|
||||||
HasScim = p.HasScim,
|
HasScim = p.HasScim,
|
||||||
HasResetPassword = p.HasResetPassword,
|
HasResetPassword = p.HasResetPassword,
|
||||||
@ -315,6 +318,7 @@ public class OrganizationEditModel : OrganizationViewModel
|
|||||||
existingOrganization.MaxAutoscaleSmSeats = MaxAutoscaleSmSeats;
|
existingOrganization.MaxAutoscaleSmSeats = MaxAutoscaleSmSeats;
|
||||||
existingOrganization.SmServiceAccounts = SmServiceAccounts;
|
existingOrganization.SmServiceAccounts = SmServiceAccounts;
|
||||||
existingOrganization.MaxAutoscaleSmServiceAccounts = MaxAutoscaleSmServiceAccounts;
|
existingOrganization.MaxAutoscaleSmServiceAccounts = MaxAutoscaleSmServiceAccounts;
|
||||||
|
existingOrganization.UseOrganizationDomains = UseOrganizationDomains;
|
||||||
return existingOrganization;
|
return existingOrganization;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,8 @@ public class OrganizationViewModel
|
|||||||
orgUsers
|
orgUsers
|
||||||
.Where(u => u.Type == OrganizationUserType.Admin && u.Status == organizationUserStatus)
|
.Where(u => u.Type == OrganizationUserType.Admin && u.Status == organizationUserStatus)
|
||||||
.Select(u => u.Email));
|
.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;
|
SecretsCount = secretsCount;
|
||||||
ProjectsCount = projectCount;
|
ProjectsCount = projectCount;
|
||||||
ServiceAccountsCount = serviceAccountsCount;
|
ServiceAccountsCount = serviceAccountsCount;
|
||||||
@ -70,4 +72,6 @@ public class OrganizationViewModel
|
|||||||
public int OccupiedSmSeatsCount { get; set; }
|
public int OccupiedSmSeatsCount { get; set; }
|
||||||
public bool UseSecretsManager => Organization.UseSecretsManager;
|
public bool UseSecretsManager => Organization.UseSecretsManager;
|
||||||
public bool UseRiskInsights => Organization.UseRiskInsights;
|
public bool UseRiskInsights => Organization.UseRiskInsights;
|
||||||
|
public IEnumerable<OrganizationUserUserDetails> OwnersDetails { get; set; }
|
||||||
|
public IEnumerable<OrganizationUserUserDetails> AdminsDetails { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Models.Data.Provider;
|
using Bit.Core.AdminConsole.Models.Data.Provider;
|
||||||
using Bit.Core.Billing.Entities;
|
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
|
using Bit.Core.Billing.Providers.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.SharedWeb.Utilities;
|
using Bit.SharedWeb.Utilities;
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Models.Data.Provider;
|
using Bit.Core.AdminConsole.Models.Data.Provider;
|
||||||
using Bit.Core.Billing.Entities;
|
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
|
using Bit.Core.Billing.Providers.Entities;
|
||||||
|
|
||||||
namespace Bit.Admin.AdminConsole.Models;
|
namespace Bit.Admin.AdminConsole.Models;
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ public class ProviderViewModel
|
|||||||
{
|
{
|
||||||
Provider = provider;
|
Provider = provider;
|
||||||
UserCount = providerUsers.Count();
|
UserCount = providerUsers.Count();
|
||||||
ProviderAdmins = providerUsers.Where(u => u.Type == ProviderUserType.ProviderAdmin);
|
ProviderUsers = providerUsers;
|
||||||
ProviderOrganizations = organizations.Where(o => o.ProviderId == provider.Id);
|
ProviderOrganizations = organizations.Where(o => o.ProviderId == provider.Id);
|
||||||
|
|
||||||
if (Provider.Type == ProviderType.Msp)
|
if (Provider.Type == ProviderType.Msp)
|
||||||
@ -61,7 +61,7 @@ public class ProviderViewModel
|
|||||||
|
|
||||||
public int UserCount { get; set; }
|
public int UserCount { get; set; }
|
||||||
public Provider Provider { 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 IEnumerable<ProviderOrganizationOrganizationDetails> ProviderOrganizations { get; set; }
|
||||||
public List<ProviderPlanViewModel> ProviderPlanViewModels { get; set; } = [];
|
public List<ProviderPlanViewModel> ProviderPlanViewModels { get; set; } = [];
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
@using Bit.Admin.Enums;
|
@using Bit.Admin.Enums;
|
||||||
@using Bit.Admin.Models
|
@using Bit.Admin.Models
|
||||||
@using Bit.Core
|
|
||||||
@using Bit.Core.AdminConsole.Enums.Provider
|
@using Bit.Core.AdminConsole.Enums.Provider
|
||||||
@using Bit.Core.Billing.Enums
|
@using Bit.Core.Billing.Enums
|
||||||
@using Bit.Core.Billing.Extensions
|
@using Bit.Core.Billing.Extensions
|
||||||
@using Bit.Core.Services
|
|
||||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
|
||||||
@inject Bit.Admin.Services.IAccessControlService AccessControlService
|
@inject Bit.Admin.Services.IAccessControlService AccessControlService
|
||||||
@inject IFeatureService FeatureService
|
|
||||||
@model OrganizationEditModel
|
@model OrganizationEditModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = (Model.Provider != null ? "Client " : string.Empty) + "Organization: " + Model.Name;
|
ViewData["Title"] = (Model.Provider != null ? "Client " : string.Empty) + "Organization: " + Model.Name;
|
||||||
@ -19,9 +15,7 @@
|
|||||||
var canDelete = AccessControlService.UserHasPermission(Permission.Org_Delete);
|
var canDelete = AccessControlService.UserHasPermission(Permission.Org_Delete);
|
||||||
var canUnlinkFromProvider = AccessControlService.UserHasPermission(Permission.Provider_Edit);
|
var canUnlinkFromProvider = AccessControlService.UserHasPermission(Permission.Provider_Edit);
|
||||||
|
|
||||||
var canConvertToBusinessUnit =
|
var canConvertToBusinessUnit = AccessControlService.UserHasPermission(Permission.Org_Billing_ConvertToBusinessUnit) &&
|
||||||
FeatureService.IsEnabled(FeatureFlagKeys.PM18770_EnableOrganizationBusinessUnitConversion) &&
|
|
||||||
AccessControlService.UserHasPermission(Permission.Org_Billing_ConvertToBusinessUnit) &&
|
|
||||||
Model.Organization.PlanType.GetProductTier() == ProductTierType.Enterprise &&
|
Model.Organization.PlanType.GetProductTier() == ProductTierType.Enterprise &&
|
||||||
!string.IsNullOrEmpty(Model.Organization.GatewaySubscriptionId) &&
|
!string.IsNullOrEmpty(Model.Organization.GatewaySubscriptionId) &&
|
||||||
Model.Provider is null or { Type: ProviderType.BusinessUnit, Status: ProviderStatusType.Pending };
|
Model.Provider is null or { Type: ProviderType.BusinessUnit, Status: ProviderStatusType.Pending };
|
||||||
|
@ -19,12 +19,6 @@
|
|||||||
<span id="org-confirmed-users" title="Confirmed">@Model.UserConfirmedCount</span>)
|
<span id="org-confirmed-users" title="Confirmed">@Model.UserConfirmedCount</span>)
|
||||||
</dd>
|
</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>
|
<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>
|
<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>
|
<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>
|
<dd id="sm-seat-count" class="col-sm-8 col-lg-9">@(Model.UseSecretsManager ? Model.OccupiedSmSeatsCount: "N/A" )</dd>
|
||||||
</dl>
|
</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);
|
var canResendEmailInvite = AccessControlService.UserHasPermission(Permission.Provider_ResendEmailInvite);
|
||||||
}
|
}
|
||||||
|
|
||||||
<h2>Provider Admins</h2>
|
<h2>Administrators</h2>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
@ -15,12 +15,13 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width: 190px;">Email</th>
|
<th style="width: 190px;">Email</th>
|
||||||
|
<th style="width: 160px;">Role</th>
|
||||||
<th style="width: 40px;">Status</th>
|
<th style="width: 40px;">Status</th>
|
||||||
<th style="width: 30px;"></th>
|
<th style="width: 30px;"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@if(!Model.ProviderAdmins.Any())
|
@if(!Model.ProviderUsers.Any())
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="6">No results to list.</td>
|
<td colspan="6">No results to list.</td>
|
||||||
@ -28,29 +29,39 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@foreach(var admin in Model.ProviderAdmins)
|
@foreach(var user in Model.ProviderUsers)
|
||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
@admin.Email
|
@user.Email
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle">
|
<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>
|
||||||
<td>
|
<td>
|
||||||
@if(admin.Status.Equals(ProviderUserStatusType.Confirmed)
|
@if(user.Status.Equals(ProviderUserStatusType.Confirmed)
|
||||||
&& @Model.Provider.Status.Equals(ProviderStatusType.Pending)
|
&& @Model.Provider.Status.Equals(ProviderStatusType.Pending)
|
||||||
&& canResendEmailInvite)
|
&& 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>
|
<button class="btn btn-outline-success btn-sm disabled" disabled>Invite Resent!</button>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<a class="btn btn-outline-secondary btn-sm"
|
<a class="btn btn-outline-secondary btn-sm"
|
||||||
data-id="@admin.Id" asp-controller="Providers"
|
data-id="@user.Id" asp-controller="Providers"
|
||||||
asp-action="ResendInvite" asp-route-ownerId="@admin.UserId"
|
asp-action="ResendInvite" asp-route-ownerId="@user.UserId"
|
||||||
asp-route-providerId="@Model.Provider.Id">
|
asp-route-providerId="@Model.Provider.Id">
|
||||||
Resend Setup Invite
|
Resend Setup Invite
|
||||||
</a>
|
</a>
|
||||||
|
@ -124,6 +124,10 @@
|
|||||||
<input type="checkbox" class="form-check-input" asp-for="UseSso" disabled='@(canEditPlan ? null : "disabled")'>
|
<input type="checkbox" class="form-check-input" asp-for="UseSso" disabled='@(canEditPlan ? null : "disabled")'>
|
||||||
<label class="form-check-label" asp-for="UseSso"></label>
|
<label class="form-check-label" asp-for="UseSso"></label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" asp-for="UseOrganizationDomains" disabled='@(canEditPlan ? null : "disabled")'>
|
||||||
|
<label class="form-check-label" asp-for="UseOrganizationDomains"></label>
|
||||||
|
</div>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input type="checkbox" class="form-check-input" asp-for="UseKeyConnector" disabled='@(canEditPlan ? null : "disabled")'>
|
<input type="checkbox" class="form-check-input" asp-for="UseKeyConnector" disabled='@(canEditPlan ? null : "disabled")'>
|
||||||
<label class="form-check-label" asp-for="UseKeyConnector"></label>
|
<label class="form-check-label" asp-for="UseKeyConnector"></label>
|
||||||
|
@ -69,6 +69,7 @@
|
|||||||
document.getElementById('@(nameof(Model.UseGroups))').checked = plan.hasGroups;
|
document.getElementById('@(nameof(Model.UseGroups))').checked = plan.hasGroups;
|
||||||
document.getElementById('@(nameof(Model.UsePolicies))').checked = plan.hasPolicies;
|
document.getElementById('@(nameof(Model.UsePolicies))').checked = plan.hasPolicies;
|
||||||
document.getElementById('@(nameof(Model.UseSso))').checked = plan.hasSso;
|
document.getElementById('@(nameof(Model.UseSso))').checked = plan.hasSso;
|
||||||
|
document.getElementById('@(nameof(Model.UseOrganizationDomains))').checked = plan.hasOrganizationDomains;
|
||||||
document.getElementById('@(nameof(Model.UseScim))').checked = plan.hasScim;
|
document.getElementById('@(nameof(Model.UseScim))').checked = plan.hasScim;
|
||||||
document.getElementById('@(nameof(Model.UseDirectory))').checked = plan.hasDirectory;
|
document.getElementById('@(nameof(Model.UseDirectory))').checked = plan.hasDirectory;
|
||||||
document.getElementById('@(nameof(Model.UseEvents))').checked = plan.hasEvents;
|
document.getElementById('@(nameof(Model.UseEvents))').checked = plan.hasEvents;
|
||||||
|
@ -2,12 +2,11 @@
|
|||||||
using Bit.Admin.Billing.Models;
|
using Bit.Admin.Billing.Models;
|
||||||
using Bit.Admin.Enums;
|
using Bit.Admin.Enums;
|
||||||
using Bit.Admin.Utilities;
|
using Bit.Admin.Utilities;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
@ -18,7 +17,6 @@ namespace Bit.Admin.Billing.Controllers;
|
|||||||
|
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[Route("organizations/billing/{organizationId:guid}/business-unit")]
|
[Route("organizations/billing/{organizationId:guid}/business-unit")]
|
||||||
[RequireFeature(FeatureFlagKeys.PM18770_EnableOrganizationBusinessUnitConversion)]
|
|
||||||
public class BusinessUnitConversionController(
|
public class BusinessUnitConversionController(
|
||||||
IBusinessUnitConverter businessUnitConverter,
|
IBusinessUnitConverter businessUnitConverter,
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
using Bit.Admin.Billing.Models;
|
using Bit.Admin.Billing.Models;
|
||||||
using Bit.Admin.Enums;
|
using Bit.Admin.Enums;
|
||||||
using Bit.Admin.Utilities;
|
using Bit.Admin.Utilities;
|
||||||
using Bit.Core.Billing.Migration.Models;
|
using Bit.Core.Billing.Providers.Migration.Models;
|
||||||
using Bit.Core.Billing.Migration.Services;
|
using Bit.Core.Billing.Providers.Migration.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Bit.Core.Billing.Entities;
|
using Bit.Core.Billing.Providers.Entities;
|
||||||
|
|
||||||
namespace Bit.Admin.Billing.Models;
|
namespace Bit.Admin.Billing.Models;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
@using System.Text.Json
|
@using System.Text.Json
|
||||||
@model Bit.Core.Billing.Migration.Models.ProviderMigrationResult
|
@model Bit.Core.Billing.Providers.Migration.Models.ProviderMigrationResult
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Results";
|
ViewData["Title"] = "Results";
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@model Bit.Core.Billing.Migration.Models.ProviderMigrationResult[]
|
@model Bit.Core.Billing.Providers.Migration.Models.ProviderMigrationResult[]
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Results";
|
ViewData["Title"] = "Results";
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ using Bit.Admin.Enums;
|
|||||||
using Bit.Admin.Models;
|
using Bit.Admin.Models;
|
||||||
using Bit.Admin.Services;
|
using Bit.Admin.Services;
|
||||||
using Bit.Admin.Utilities;
|
using Bit.Admin.Utilities;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
@ -89,7 +88,7 @@ public class UsersController : Controller
|
|||||||
var ciphers = await _cipherRepository.GetManyByUserIdAsync(id);
|
var ciphers = await _cipherRepository.GetManyByUserIdAsync(id);
|
||||||
|
|
||||||
var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
||||||
var verifiedDomain = await AccountDeprovisioningEnabled(user.Id);
|
var verifiedDomain = await _userService.IsClaimedByAnyOrganizationAsync(user.Id);
|
||||||
return View(UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers, verifiedDomain));
|
return View(UserViewModel.MapViewModel(user, isTwoFactorEnabled, ciphers, verifiedDomain));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +105,7 @@ public class UsersController : Controller
|
|||||||
var billingInfo = await _paymentService.GetBillingAsync(user);
|
var billingInfo = await _paymentService.GetBillingAsync(user);
|
||||||
var billingHistoryInfo = await _paymentService.GetBillingHistoryAsync(user);
|
var billingHistoryInfo = await _paymentService.GetBillingHistoryAsync(user);
|
||||||
var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
var isTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
||||||
var verifiedDomain = await AccountDeprovisioningEnabled(user.Id);
|
var verifiedDomain = await _userService.IsClaimedByAnyOrganizationAsync(user.Id);
|
||||||
var deviceVerificationRequired = await _userService.ActiveNewDeviceVerificationException(user.Id);
|
var deviceVerificationRequired = await _userService.ActiveNewDeviceVerificationException(user.Id);
|
||||||
|
|
||||||
return View(new UserEditModel(user, isTwoFactorEnabled, ciphers, billingInfo, billingHistoryInfo, _globalSettings, verifiedDomain, deviceVerificationRequired));
|
return View(new UserEditModel(user, isTwoFactorEnabled, ciphers, billingInfo, billingHistoryInfo, _globalSettings, verifiedDomain, deviceVerificationRequired));
|
||||||
@ -167,7 +166,6 @@ public class UsersController : Controller
|
|||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
[RequirePermission(Permission.User_NewDeviceException_Edit)]
|
[RequirePermission(Permission.User_NewDeviceException_Edit)]
|
||||||
[RequireFeature(FeatureFlagKeys.NewDeviceVerification)]
|
|
||||||
public async Task<IActionResult> ToggleNewDeviceVerification(Guid id)
|
public async Task<IActionResult> ToggleNewDeviceVerification(Guid id)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(id);
|
var user = await _userRepository.GetByIdAsync(id);
|
||||||
@ -179,12 +177,4 @@ public class UsersController : Controller
|
|||||||
await _userService.ToggleNewDeviceVerificationException(user.Id);
|
await _userService.ToggleNewDeviceVerificationException(user.Id);
|
||||||
return RedirectToAction("Edit", new { id });
|
return RedirectToAction("Edit", new { id });
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Feature flag to be removed in PM-14207
|
|
||||||
private async Task<bool?> AccountDeprovisioningEnabled(Guid userId)
|
|
||||||
{
|
|
||||||
return _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
|
||||||
? await _userService.IsClaimedByAnyOrganizationAsync(userId)
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||||
|
|
||||||
|
ARG TARGETPLATFORM
|
||||||
LABEL com.bitwarden.product="bitwarden"
|
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 \
|
RUN apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends \
|
&& apt-get install -y --no-install-recommends \
|
||||||
gosu \
|
gosu \
|
||||||
curl \
|
curl \
|
||||||
krb5-user \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV ASPNETCORE_URLS http://+:5000
|
# Copy app from the build stage
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
EXPOSE 5000
|
COPY --from=build /source/src/Admin/out /app
|
||||||
COPY obj/build-output/publish .
|
COPY ./src/Admin/entrypoint.sh /entrypoint.sh
|
||||||
COPY entrypoint.sh /
|
|
||||||
RUN chmod +x /entrypoint.sh
|
RUN chmod +x /entrypoint.sh
|
||||||
|
HEALTHCHECK CMD curl -f http://localhost:5000/alive || exit 1
|
||||||
HEALTHCHECK CMD curl -f http://localhost:5000 || exit 1
|
|
||||||
|
|
||||||
ENTRYPOINT ["/entrypoint.sh"]
|
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)
|
if (userStamp == null)
|
||||||
{
|
{
|
||||||
return Task.FromResult<IdentityUser>(null);
|
return Task.FromResult<IdentityUser>(null);
|
||||||
|
@ -17,7 +17,7 @@ public class ChargeBraintreeModel : IValidatableObject
|
|||||||
{
|
{
|
||||||
if (Id != null)
|
if (Id != null)
|
||||||
{
|
{
|
||||||
if (Id.Length != 36 || (Id[0] != 'o' && Id[0] != 'u') ||
|
if (Id.Length != 36 || (Id[0] != 'o' && Id[0] != 'u' && Id[0] != 'p') ||
|
||||||
!Guid.TryParse(Id.Substring(1, 32), out var guid))
|
!Guid.TryParse(Id.Substring(1, 32), out var guid))
|
||||||
{
|
{
|
||||||
yield return new ValidationResult("Customer Id is not a valid format.");
|
yield return new ValidationResult("Customer Id is not a valid format.");
|
||||||
|
@ -20,8 +20,8 @@ public class Program
|
|||||||
logging.AddSerilog(hostingContext, (e, globalSettings) =>
|
logging.AddSerilog(hostingContext, (e, globalSettings) =>
|
||||||
{
|
{
|
||||||
var context = e.Properties["SourceContext"].ToString();
|
var context = e.Properties["SourceContext"].ToString();
|
||||||
if (e.Properties.ContainsKey("RequestPath") &&
|
if (e.Properties.TryGetValue("RequestPath", out var requestPath) &&
|
||||||
!string.IsNullOrWhiteSpace(e.Properties["RequestPath"]?.ToString()) &&
|
!string.IsNullOrWhiteSpace(requestPath?.ToString()) &&
|
||||||
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
(context.Contains(".Server.Kestrel") || context.Contains(".Core.IISHttpServer")))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -29,12 +29,12 @@ public class AccessControlService : IAccessControlService
|
|||||||
}
|
}
|
||||||
|
|
||||||
var userRole = GetUserRoleFromClaim();
|
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 false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return RolePermissionMapping.RolePermissions[userRole].Contains(permission);
|
return rolePermissions.Contains(permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetUserRole(string userEmail)
|
public string GetUserRole(string userEmail)
|
||||||
|
@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Mvc.Razor;
|
|||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
using Bit.Admin.Services;
|
using Bit.Admin.Services;
|
||||||
using Bit.Core.Billing.Extensions;
|
using Bit.Core.Billing.Extensions;
|
||||||
using Bit.Core.Billing.Migration;
|
using Bit.Core.Billing.Providers.Migration;
|
||||||
|
|
||||||
#if !OSS
|
#if !OSS
|
||||||
using Bit.Commercial.Core.Utilities;
|
using Bit.Commercial.Core.Utilities;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.Jobs;
|
using Bit.Core.Jobs;
|
||||||
using Bit.Core.Tools.Repositories;
|
using Bit.Core.Tools.Repositories;
|
||||||
using Bit.Core.Tools.Services;
|
using Bit.Core.Tools.SendFeatures.Commands.Interfaces;
|
||||||
using Quartz;
|
using Quartz;
|
||||||
|
|
||||||
namespace Bit.Admin.Tools.Jobs;
|
namespace Bit.Admin.Tools.Jobs;
|
||||||
@ -32,10 +32,10 @@ public class DeleteSendsJob : BaseJob
|
|||||||
}
|
}
|
||||||
using (var scope = _serviceProvider.CreateScope())
|
using (var scope = _serviceProvider.CreateScope())
|
||||||
{
|
{
|
||||||
var sendService = scope.ServiceProvider.GetRequiredService<ISendService>();
|
var nonAnonymousSendCommand = scope.ServiceProvider.GetRequiredService<INonAnonymousSendCommand>();
|
||||||
foreach (var send in sends)
|
foreach (var send in sends)
|
||||||
{
|
{
|
||||||
await sendService.DeleteSendAsync(send);
|
await nonAnonymousSendCommand.DeleteSendAsync(send);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
|
|
||||||
@ -19,31 +19,36 @@ then
|
|||||||
LGID=65534
|
LGID=65534
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create user and group
|
if [ "$(id -u)" = "0" ]
|
||||||
|
then
|
||||||
|
# Create user and group
|
||||||
|
|
||||||
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
|
||||||
groupmod -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 ||
|
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
|
usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1
|
||||||
mkhomedir_helper $USERNAME
|
mkhomedir_helper $USERNAME
|
||||||
|
|
||||||
# The rest...
|
# The rest...
|
||||||
|
|
||||||
chown -R $USERNAME:$GROUPNAME /app
|
chown -R $USERNAME:$GROUPNAME /app
|
||||||
mkdir -p /etc/bitwarden/core
|
mkdir -p /etc/bitwarden/core
|
||||||
mkdir -p /etc/bitwarden/logs
|
mkdir -p /etc/bitwarden/logs
|
||||||
mkdir -p /etc/bitwarden/ca-certificates
|
mkdir -p /etc/bitwarden/ca-certificates
|
||||||
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden
|
||||||
|
|
||||||
if [[ $globalSettings__selfHosted == "true" ]]; then
|
if [[ -f "/etc/bitwarden/kerberos/bitwarden.keytab" && -f "/etc/bitwarden/kerberos/krb5.conf" ]]; then
|
||||||
cp /etc/bitwarden/ca-certificates/*.crt /usr/local/share/ca-certificates/ >/dev/null 2>&1 \
|
chown -R $USERNAME:$GROUPNAME /etc/bitwarden/kerberos
|
||||||
&& update-ca-certificates
|
fi
|
||||||
|
|
||||||
|
gosu_cmd="gosu $USERNAME:$GROUPNAME"
|
||||||
|
else
|
||||||
|
gosu_cmd=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -f "/etc/bitwarden/kerberos/bitwarden.keytab" && -f "/etc/bitwarden/kerberos/krb5.conf" ]]; then
|
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
|
cp -f /etc/bitwarden/kerberos/krb5.conf /etc/krb5.conf
|
||||||
gosu $USERNAME:$GROUPNAME kinit $globalSettings__kerberosUser -k -t /etc/bitwarden/kerberos/bitwarden.keytab
|
$gosu_cmd kinit $globalSettings__kerberosUser -k -t /etc/bitwarden/kerberos/bitwarden.keytab
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exec gosu $USERNAME:$GROUPNAME dotnet /app/Admin.dll
|
exec $gosu_cmd /app/Admin
|
||||||
|
207
src/Admin/package-lock.json
generated
207
src/Admin/package-lock.json
generated
@ -9,18 +9,18 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bootstrap": "5.3.3",
|
"bootstrap": "5.3.6",
|
||||||
"font-awesome": "4.7.0",
|
"font-awesome": "4.7.0",
|
||||||
"jquery": "3.7.1",
|
"jquery": "3.7.1",
|
||||||
"toastr": "2.1.4"
|
"toastr": "2.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"css-loader": "7.1.2",
|
"css-loader": "7.1.2",
|
||||||
"expose-loader": "5.0.0",
|
"expose-loader": "5.0.1",
|
||||||
"mini-css-extract-plugin": "2.9.2",
|
"mini-css-extract-plugin": "2.9.2",
|
||||||
"sass": "1.85.0",
|
"sass": "1.88.0",
|
||||||
"sass-loader": "16.0.4",
|
"sass-loader": "16.0.5",
|
||||||
"webpack": "5.97.1",
|
"webpack": "5.99.8",
|
||||||
"webpack-cli": "5.1.4"
|
"webpack-cli": "5.1.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -456,13 +456,13 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.13.14",
|
"version": "22.15.21",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
|
||||||
"integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==",
|
"integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.20.0"
|
"undici-types": "~6.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/ast": {
|
"node_modules/@webassemblyjs/ast": {
|
||||||
@ -749,9 +749,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bootstrap": {
|
"node_modules/bootstrap": {
|
||||||
"version": "5.3.3",
|
"version": "5.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.6.tgz",
|
||||||
"integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
|
"integrity": "sha512-jX0GAcRzvdwISuvArXn3m7KZscWWFAf1MKBcnzaN02qWMb3jpMoUX4/qgeiGzqyIb4ojulRzs89UCUmGcFSzTA==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@ -782,9 +782,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.24.4",
|
"version": "4.24.5",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz",
|
||||||
"integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
|
"integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -802,10 +802,10 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"caniuse-lite": "^1.0.30001688",
|
"caniuse-lite": "^1.0.30001716",
|
||||||
"electron-to-chromium": "^1.5.73",
|
"electron-to-chromium": "^1.5.149",
|
||||||
"node-releases": "^2.0.19",
|
"node-releases": "^2.0.19",
|
||||||
"update-browserslist-db": "^1.1.1"
|
"update-browserslist-db": "^1.1.3"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"browserslist": "cli.js"
|
"browserslist": "cli.js"
|
||||||
@ -822,9 +822,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001707",
|
"version": "1.0.30001718",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
|
||||||
"integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==",
|
"integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -976,9 +976,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.128",
|
"version": "1.5.155",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.128.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz",
|
||||||
"integrity": "sha512-bo1A4HH/NS522Ws0QNFIzyPcyUUNV/yyy70Ho1xqfGYzPUme2F/xr4tlEOuM6/A538U1vDA7a4XfCd1CKRegKQ==",
|
"integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
@ -1010,9 +1010,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/es-module-lexer": {
|
"node_modules/es-module-lexer": {
|
||||||
"version": "1.6.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
|
||||||
"integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==",
|
"integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@ -1084,9 +1084,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/expose-loader": {
|
"node_modules/expose-loader": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-5.0.1.tgz",
|
||||||
"integrity": "sha512-BtUqYRmvx1bEY5HN6eK2I9URUZgNmN0x5UANuocaNjXSgfoDlkXt+wyEMe7i5DzDNh2BKJHPc5F4rBwEdSQX6w==",
|
"integrity": "sha512-5YPZuszN/eWND/B+xuq5nIpb/l5TV1HYmdO6SubYtHv+HenVw9/6bn33Mm5reY8DNid7AVtbARvyUD34edfCtg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -1107,13 +1107,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/fast-uri": {
|
||||||
"version": "3.0.6",
|
"version": "3.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
|
||||||
@ -1249,9 +1242,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/immutable": {
|
"node_modules/immutable": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.2.tgz",
|
||||||
"integrity": "sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg==",
|
"integrity": "sha512-qHKXW1q6liAk1Oys6umoaZbDRqjcjgSrbnrifHsfsttza7zcvRAsL7mMV6xWcyhwQy7Xj5v4hhbr6b+iDYwlmQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@ -1755,16 +1748,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/randombytes": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||||
@ -1878,9 +1861,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/sass": {
|
"node_modules/sass": {
|
||||||
"version": "1.85.0",
|
"version": "1.88.0",
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.85.0.tgz",
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.88.0.tgz",
|
||||||
"integrity": "sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww==",
|
"integrity": "sha512-sF6TWQqjFvr4JILXzG4ucGOLELkESHL+I5QJhh7CNaE+Yge0SI+ehCatsXhJ7ymU1hAFcIS3/PBpjdIbXoyVbg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1899,9 +1882,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sass-loader": {
|
"node_modules/sass-loader": {
|
||||||
"version": "16.0.4",
|
"version": "16.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.5.tgz",
|
||||||
"integrity": "sha512-LavLbgbBGUt3wCiYzhuLLu65+fWXaXLmq7YxivLhEqmiupCFZ5sKUAipK3do6V80YSU0jvSxNhEdT13IXNr3rg==",
|
"integrity": "sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1940,9 +1923,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/schema-utils": {
|
"node_modules/schema-utils": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
|
||||||
"integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
|
"integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -1960,9 +1943,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.7.1",
|
"version": "7.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
@ -2079,9 +2062,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tapable": {
|
"node_modules/tapable": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz",
|
||||||
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
|
"integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
@ -2089,14 +2072,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/terser": {
|
"node_modules/terser": {
|
||||||
"version": "5.39.0",
|
"version": "5.39.2",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.39.2.tgz",
|
||||||
"integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==",
|
"integrity": "sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
"acorn": "^8.8.2",
|
"acorn": "^8.14.0",
|
||||||
"commander": "^2.20.0",
|
"commander": "^2.20.0",
|
||||||
"source-map-support": "~0.5.20"
|
"source-map-support": "~0.5.20"
|
||||||
},
|
},
|
||||||
@ -2165,9 +2148,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "6.20.0",
|
"version": "6.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@ -2202,16 +2185,6 @@
|
|||||||
"browserslist": ">= 4.21.0"
|
"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": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
@ -2220,9 +2193,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/watchpack": {
|
"node_modules/watchpack": {
|
||||||
"version": "2.4.2",
|
"version": "2.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz",
|
||||||
"integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
|
"integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -2234,14 +2207,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/webpack": {
|
"node_modules/webpack": {
|
||||||
"version": "5.97.1",
|
"version": "5.99.8",
|
||||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz",
|
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.8.tgz",
|
||||||
"integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==",
|
"integrity": "sha512-lQ3CPiSTpfOnrEGeXDwoq5hIGzSjmwD72GdfVzF7CQAI7t47rJG9eDWvcEkEn3CUQymAElVvDg3YNTlCYj+qUQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/eslint-scope": "^3.7.7",
|
"@types/eslint-scope": "^3.7.7",
|
||||||
"@types/estree": "^1.0.6",
|
"@types/estree": "^1.0.6",
|
||||||
|
"@types/json-schema": "^7.0.15",
|
||||||
"@webassemblyjs/ast": "^1.14.1",
|
"@webassemblyjs/ast": "^1.14.1",
|
||||||
"@webassemblyjs/wasm-edit": "^1.14.1",
|
"@webassemblyjs/wasm-edit": "^1.14.1",
|
||||||
"@webassemblyjs/wasm-parser": "^1.14.1",
|
"@webassemblyjs/wasm-parser": "^1.14.1",
|
||||||
@ -2258,9 +2232,9 @@
|
|||||||
"loader-runner": "^4.2.0",
|
"loader-runner": "^4.2.0",
|
||||||
"mime-types": "^2.1.27",
|
"mime-types": "^2.1.27",
|
||||||
"neo-async": "^2.6.2",
|
"neo-async": "^2.6.2",
|
||||||
"schema-utils": "^3.2.0",
|
"schema-utils": "^4.3.2",
|
||||||
"tapable": "^2.1.1",
|
"tapable": "^2.1.1",
|
||||||
"terser-webpack-plugin": "^5.3.10",
|
"terser-webpack-plugin": "^5.3.11",
|
||||||
"watchpack": "^2.4.1",
|
"watchpack": "^2.4.1",
|
||||||
"webpack-sources": "^3.2.3"
|
"webpack-sources": "^3.2.3"
|
||||||
},
|
},
|
||||||
@ -2361,59 +2335,6 @@
|
|||||||
"node": ">=10.13.0"
|
"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": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
@ -8,18 +8,18 @@
|
|||||||
"build": "webpack"
|
"build": "webpack"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bootstrap": "5.3.3",
|
"bootstrap": "5.3.6",
|
||||||
"font-awesome": "4.7.0",
|
"font-awesome": "4.7.0",
|
||||||
"jquery": "3.7.1",
|
"jquery": "3.7.1",
|
||||||
"toastr": "2.1.4"
|
"toastr": "2.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"css-loader": "7.1.2",
|
"css-loader": "7.1.2",
|
||||||
"expose-loader": "5.0.0",
|
"expose-loader": "5.0.1",
|
||||||
"mini-css-extract-plugin": "2.9.2",
|
"mini-css-extract-plugin": "2.9.2",
|
||||||
"sass": "1.85.0",
|
"sass": "1.88.0",
|
||||||
"sass-loader": "16.0.4",
|
"sass-loader": "16.0.5",
|
||||||
"webpack": "5.97.1",
|
"webpack": "5.99.8",
|
||||||
"webpack-cli": "5.1.4"
|
"webpack-cli": "5.1.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
*
|
|
||||||
!obj/build-output/publish/*
|
|
||||||
!obj/Docker/empty/
|
|
||||||
!entrypoint.sh
|
|
@ -2,13 +2,11 @@
|
|||||||
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
||||||
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
||||||
using Bit.Api.Models.Response;
|
using Bit.Api.Models.Response;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
@ -137,7 +135,6 @@ public class OrganizationDomainController : Controller
|
|||||||
|
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpPost("domain/sso/verified")]
|
[HttpPost("domain/sso/verified")]
|
||||||
[RequireFeature(FeatureFlagKeys.VerifiedSsoDomainEndpoint)]
|
|
||||||
public async Task<VerifiedOrganizationDomainSsoDetailsResponseModel> GetVerifiedOrgDomainSsoDetailsAsync(
|
public async Task<VerifiedOrganizationDomainSsoDetailsResponseModel> GetVerifiedOrgDomainSsoDetailsAsync(
|
||||||
[FromBody] OrganizationDomainSsoDetailsRequestModel model)
|
[FromBody] OrganizationDomainSsoDetailsRequestModel model)
|
||||||
{
|
{
|
||||||
|
@ -616,7 +616,6 @@ public class OrganizationUsersController : Controller
|
|||||||
new OrganizationUserBulkResponseModel(r.OrganizationUserId, r.ErrorMessage)));
|
new OrganizationUserBulkResponseModel(r.OrganizationUserId, r.ErrorMessage)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[RequireFeature(FeatureFlagKeys.AccountDeprovisioning)]
|
|
||||||
[HttpDelete("{id}/delete-account")]
|
[HttpDelete("{id}/delete-account")]
|
||||||
[HttpPost("{id}/delete-account")]
|
[HttpPost("{id}/delete-account")]
|
||||||
public async Task DeleteAccount(Guid orgId, Guid id)
|
public async Task DeleteAccount(Guid orgId, Guid id)
|
||||||
@ -635,7 +634,6 @@ public class OrganizationUsersController : Controller
|
|||||||
await _deleteClaimedOrganizationUserAccountCommand.DeleteUserAsync(orgId, id, currentUser.Id);
|
await _deleteClaimedOrganizationUserAccountCommand.DeleteUserAsync(orgId, id, currentUser.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[RequireFeature(FeatureFlagKeys.AccountDeprovisioning)]
|
|
||||||
[HttpDelete("delete-account")]
|
[HttpDelete("delete-account")]
|
||||||
[HttpPost("delete-account")]
|
[HttpPost("delete-account")]
|
||||||
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkDeleteAccount(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkDeleteAccount(Guid orgId, [FromBody] OrganizationUserBulkRequestModel model)
|
||||||
@ -760,11 +758,6 @@ public class OrganizationUsersController : Controller
|
|||||||
|
|
||||||
private async Task<IDictionary<Guid, bool>> GetClaimedByOrganizationStatusAsync(Guid orgId, IEnumerable<Guid> userIds)
|
private async Task<IDictionary<Guid, bool>> GetClaimedByOrganizationStatusAsync(Guid orgId, IEnumerable<Guid> userIds)
|
||||||
{
|
{
|
||||||
if (!_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
|
|
||||||
{
|
|
||||||
return userIds.ToDictionary(kvp => kvp, kvp => false);
|
|
||||||
}
|
|
||||||
|
|
||||||
var usersOrganizationClaimedStatus = await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(orgId, userIds);
|
var usersOrganizationClaimedStatus = await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(orgId, userIds);
|
||||||
return usersOrganizationClaimedStatus;
|
return usersOrganizationClaimedStatus;
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ using Bit.Core.Auth.Services;
|
|||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
using Bit.Core.Billing.Extensions;
|
using Bit.Core.Billing.Extensions;
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
@ -279,8 +279,7 @@ public class OrganizationsController : Controller
|
|||||||
throw new BadRequestException("Your organization's Single Sign-On settings prevent you from leaving.");
|
throw new BadRequestException("Your organization's Single Sign-On settings prevent you from leaving.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
if ((await _userService.GetOrganizationsClaimingUserAsync(user.Id)).Any(x => x.Id == id))
|
||||||
&& (await _userService.GetOrganizationsClaimingUserAsync(user.Id)).Any(x => x.Id == id))
|
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Claimed user account cannot leave claiming organization. Contact your organization administrator for additional details.");
|
throw new BadRequestException("Claimed user account cannot leave claiming organization. Contact your organization administrator for additional details.");
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
using Bit.Api.AdminConsole.Models.Response.Helpers;
|
using Bit.Api.AdminConsole.Models.Response.Helpers;
|
||||||
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
||||||
using Bit.Api.Models.Response;
|
using Bit.Api.Models.Response;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Enums;
|
using Bit.Core.AdminConsole.Enums;
|
||||||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
|
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces;
|
||||||
@ -79,7 +78,7 @@ public class PoliciesController : Controller
|
|||||||
return new PolicyDetailResponseModel(new Policy { Type = (PolicyType)type });
|
return new PolicyDetailResponseModel(new Policy { Type = (PolicyType)type });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) && policy.Type is PolicyType.SingleOrg)
|
if (policy.Type is PolicyType.SingleOrg)
|
||||||
{
|
{
|
||||||
return await policy.GetSingleOrgPolicyDetailResponseAsync(_organizationHasVerifiedDomainsQuery);
|
return await policy.GetSingleOrgPolicyDetailResponseAsync(_organizationHasVerifiedDomainsQuery);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
using Bit.Api.Billing.Models.Requests;
|
using Bit.Api.Billing.Models.Requests;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
using Bit.Api.AdminConsole.Models.Response.Organizations;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Core.AdminConsole.Models.Data.Integrations;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Data.Integrations;
|
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
|
@ -75,6 +75,8 @@ public class OrganizationCreateRequestModel : IValidatableObject
|
|||||||
|
|
||||||
public string InitiationPath { get; set; }
|
public string InitiationPath { get; set; }
|
||||||
|
|
||||||
|
public bool SkipTrial { get; set; }
|
||||||
|
|
||||||
public virtual OrganizationSignup ToOrganizationSignup(User user)
|
public virtual OrganizationSignup ToOrganizationSignup(User user)
|
||||||
{
|
{
|
||||||
var orgSignup = new OrganizationSignup
|
var orgSignup = new OrganizationSignup
|
||||||
@ -107,6 +109,7 @@ public class OrganizationCreateRequestModel : IValidatableObject
|
|||||||
BillingAddressCountry = BillingAddressCountry,
|
BillingAddressCountry = BillingAddressCountry,
|
||||||
},
|
},
|
||||||
InitiationPath = InitiationPath,
|
InitiationPath = InitiationPath,
|
||||||
|
SkipTrial = SkipTrial
|
||||||
};
|
};
|
||||||
|
|
||||||
Keys?.ToOrganizationSignup(orgSignup);
|
Keys?.ToOrganizationSignup(orgSignup);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Core.AdminConsole.Models.Data.Integrations;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Data.Integrations;
|
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ public class OrganizationResponseModel : ResponseModel
|
|||||||
LimitItemDeletion = organization.LimitItemDeletion;
|
LimitItemDeletion = organization.LimitItemDeletion;
|
||||||
AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
|
AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
|
||||||
UseRiskInsights = organization.UseRiskInsights;
|
UseRiskInsights = organization.UseRiskInsights;
|
||||||
|
UseOrganizationDomains = organization.UseOrganizationDomains;
|
||||||
UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies;
|
UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,6 +112,7 @@ public class OrganizationResponseModel : ResponseModel
|
|||||||
public bool LimitItemDeletion { get; set; }
|
public bool LimitItemDeletion { get; set; }
|
||||||
public bool AllowAdminAccessToAllCollectionItems { get; set; }
|
public bool AllowAdminAccessToAllCollectionItems { get; set; }
|
||||||
public bool UseRiskInsights { get; set; }
|
public bool UseRiskInsights { get; set; }
|
||||||
|
public bool UseOrganizationDomains { get; set; }
|
||||||
public bool UseAdminSponsoredFamilies { get; set; }
|
public bool UseAdminSponsoredFamilies { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,8 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
|||||||
ProviderName = organization.ProviderName;
|
ProviderName = organization.ProviderName;
|
||||||
ProviderType = organization.ProviderType;
|
ProviderType = organization.ProviderType;
|
||||||
FamilySponsorshipFriendlyName = organization.FamilySponsorshipFriendlyName;
|
FamilySponsorshipFriendlyName = organization.FamilySponsorshipFriendlyName;
|
||||||
FamilySponsorshipAvailable = FamilySponsorshipFriendlyName == null &&
|
IsAdminInitiated = organization.IsAdminInitiated ?? false;
|
||||||
|
FamilySponsorshipAvailable = (FamilySponsorshipFriendlyName == null || IsAdminInitiated) &&
|
||||||
StaticStore.GetSponsoredPlan(PlanSponsorshipType.FamiliesForEnterprise)
|
StaticStore.GetSponsoredPlan(PlanSponsorshipType.FamiliesForEnterprise)
|
||||||
.UsersCanSponsor(organization);
|
.UsersCanSponsor(organization);
|
||||||
ProductTierType = organization.PlanType.GetProductTier();
|
ProductTierType = organization.PlanType.GetProductTier();
|
||||||
@ -72,6 +73,7 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
|||||||
AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
|
AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
|
||||||
UserIsClaimedByOrganization = organizationIdsClaimingUser.Contains(organization.OrganizationId);
|
UserIsClaimedByOrganization = organizationIdsClaimingUser.Contains(organization.OrganizationId);
|
||||||
UseRiskInsights = organization.UseRiskInsights;
|
UseRiskInsights = organization.UseRiskInsights;
|
||||||
|
UseOrganizationDomains = organization.UseOrganizationDomains;
|
||||||
UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies;
|
UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies;
|
||||||
|
|
||||||
if (organization.SsoConfig != null)
|
if (organization.SsoConfig != null)
|
||||||
@ -135,7 +137,6 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
|||||||
public bool AllowAdminAccessToAllCollectionItems { get; set; }
|
public bool AllowAdminAccessToAllCollectionItems { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Obsolete.
|
/// Obsolete.
|
||||||
///
|
|
||||||
/// See <see cref="UserIsClaimedByOrganization"/>
|
/// See <see cref="UserIsClaimedByOrganization"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("Please use UserIsClaimedByOrganization instead. This property will be removed in a future version.")]
|
[Obsolete("Please use UserIsClaimedByOrganization instead. This property will be removed in a future version.")]
|
||||||
@ -145,16 +146,15 @@ public class ProfileOrganizationResponseModel : ResponseModel
|
|||||||
set => UserIsClaimedByOrganization = value;
|
set => UserIsClaimedByOrganization = value;
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates if the organization claims the user.
|
/// Indicates if the user is claimed by the organization.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// An organization claims a user if the user's email domain is verified by the organization and the user is a member of it.
|
/// A user is claimed by an organization if the user's email domain is verified by the organization and the user is a member.
|
||||||
/// The organization must be enabled and able to have verified domains.
|
/// The organization must be enabled and able to have verified domains.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <returns>
|
|
||||||
/// False if the Account Deprovisioning feature flag is disabled.
|
|
||||||
/// </returns>
|
|
||||||
public bool UserIsClaimedByOrganization { get; set; }
|
public bool UserIsClaimedByOrganization { get; set; }
|
||||||
public bool UseRiskInsights { get; set; }
|
public bool UseRiskInsights { get; set; }
|
||||||
|
public bool UseOrganizationDomains { get; set; }
|
||||||
public bool UseAdminSponsoredFamilies { get; set; }
|
public bool UseAdminSponsoredFamilies { get; set; }
|
||||||
|
public bool IsAdminInitiated { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ public class ProfileProviderOrganizationResponseModel : ProfileOrganizationRespo
|
|||||||
LimitItemDeletion = organization.LimitItemDeletion;
|
LimitItemDeletion = organization.LimitItemDeletion;
|
||||||
AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
|
AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
|
||||||
UseRiskInsights = organization.UseRiskInsights;
|
UseRiskInsights = organization.UseRiskInsights;
|
||||||
|
UseOrganizationDomains = organization.UseOrganizationDomains;
|
||||||
UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies;
|
UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ public class MembersController : Controller
|
|||||||
{
|
{
|
||||||
return new NotFoundResult();
|
return new NotFoundResult();
|
||||||
}
|
}
|
||||||
var response = new MemberResponseModel(orgUser, await _userService.TwoFactorIsEnabledAsync(orgUser),
|
var response = new MemberResponseModel(orgUser, await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(orgUser),
|
||||||
collections);
|
collections);
|
||||||
return new JsonResult(response);
|
return new JsonResult(response);
|
||||||
}
|
}
|
||||||
@ -185,7 +185,7 @@ public class MembersController : Controller
|
|||||||
{
|
{
|
||||||
var existingUserDetails = await _organizationUserRepository.GetDetailsByIdAsync(id);
|
var existingUserDetails = await _organizationUserRepository.GetDetailsByIdAsync(id);
|
||||||
response = new MemberResponseModel(existingUserDetails,
|
response = new MemberResponseModel(existingUserDetails,
|
||||||
await _userService.TwoFactorIsEnabledAsync(existingUserDetails), associations);
|
await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(existingUserDetails), associations);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -4,8 +4,6 @@
|
|||||||
<MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>
|
<MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>
|
||||||
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
|
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
|
||||||
<ANCMPreConfiguredForIIS>true</ANCMPreConfiguredForIIS>
|
<ANCMPreConfiguredForIIS>true</ANCMPreConfiguredForIIS>
|
||||||
<!-- Temp exclusions until warnings are fixed -->
|
|
||||||
<WarningsNotAsErrors>$(WarningsNotAsErrors);CS8604</WarningsNotAsErrors>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
@ -36,7 +34,7 @@
|
|||||||
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="8.0.2" />
|
<PackageReference Include="AspNetCore.HealthChecks.SqlServer" Version="8.0.2" />
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="8.0.1" />
|
<PackageReference Include="AspNetCore.HealthChecks.Uris" Version="8.0.1" />
|
||||||
<PackageReference Include="Azure.Messaging.EventGrid" Version="4.25.0" />
|
<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>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -16,6 +16,7 @@ using Bit.Core.Auth.Entities;
|
|||||||
using Bit.Core.Auth.Models.Api.Request.Accounts;
|
using Bit.Core.Auth.Models.Api.Request.Accounts;
|
||||||
using Bit.Core.Auth.Models.Data;
|
using Bit.Core.Auth.Models.Data;
|
||||||
using Bit.Core.Auth.UserFeatures.TdeOffboardingPassword.Interfaces;
|
using Bit.Core.Auth.UserFeatures.TdeOffboardingPassword.Interfaces;
|
||||||
|
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||||
using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces;
|
using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -45,6 +46,7 @@ public class AccountsController : Controller
|
|||||||
private readonly ISetInitialMasterPasswordCommand _setInitialMasterPasswordCommand;
|
private readonly ISetInitialMasterPasswordCommand _setInitialMasterPasswordCommand;
|
||||||
private readonly ITdeOffboardingPasswordCommand _tdeOffboardingPasswordCommand;
|
private readonly ITdeOffboardingPasswordCommand _tdeOffboardingPasswordCommand;
|
||||||
private readonly IRotateUserKeyCommand _rotateUserKeyCommand;
|
private readonly IRotateUserKeyCommand _rotateUserKeyCommand;
|
||||||
|
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
|
||||||
private readonly IFeatureService _featureService;
|
private readonly IFeatureService _featureService;
|
||||||
|
|
||||||
private readonly IRotationValidator<IEnumerable<CipherWithIdRequestModel>, IEnumerable<Cipher>> _cipherValidator;
|
private readonly IRotationValidator<IEnumerable<CipherWithIdRequestModel>, IEnumerable<Cipher>> _cipherValidator;
|
||||||
@ -68,6 +70,7 @@ public class AccountsController : Controller
|
|||||||
ISetInitialMasterPasswordCommand setInitialMasterPasswordCommand,
|
ISetInitialMasterPasswordCommand setInitialMasterPasswordCommand,
|
||||||
ITdeOffboardingPasswordCommand tdeOffboardingPasswordCommand,
|
ITdeOffboardingPasswordCommand tdeOffboardingPasswordCommand,
|
||||||
IRotateUserKeyCommand rotateUserKeyCommand,
|
IRotateUserKeyCommand rotateUserKeyCommand,
|
||||||
|
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||||
IFeatureService featureService,
|
IFeatureService featureService,
|
||||||
IRotationValidator<IEnumerable<CipherWithIdRequestModel>, IEnumerable<Cipher>> cipherValidator,
|
IRotationValidator<IEnumerable<CipherWithIdRequestModel>, IEnumerable<Cipher>> cipherValidator,
|
||||||
IRotationValidator<IEnumerable<FolderWithIdRequestModel>, IEnumerable<Folder>> folderValidator,
|
IRotationValidator<IEnumerable<FolderWithIdRequestModel>, IEnumerable<Folder>> folderValidator,
|
||||||
@ -87,6 +90,7 @@ public class AccountsController : Controller
|
|||||||
_setInitialMasterPasswordCommand = setInitialMasterPasswordCommand;
|
_setInitialMasterPasswordCommand = setInitialMasterPasswordCommand;
|
||||||
_tdeOffboardingPasswordCommand = tdeOffboardingPasswordCommand;
|
_tdeOffboardingPasswordCommand = tdeOffboardingPasswordCommand;
|
||||||
_rotateUserKeyCommand = rotateUserKeyCommand;
|
_rotateUserKeyCommand = rotateUserKeyCommand;
|
||||||
|
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
||||||
_featureService = featureService;
|
_featureService = featureService;
|
||||||
_cipherValidator = cipherValidator;
|
_cipherValidator = cipherValidator;
|
||||||
_folderValidator = folderValidator;
|
_folderValidator = folderValidator;
|
||||||
@ -389,7 +393,7 @@ public class AccountsController : Controller
|
|||||||
await _providerUserRepository.GetManyOrganizationDetailsByUserAsync(user.Id,
|
await _providerUserRepository.GetManyOrganizationDetailsByUserAsync(user.Id,
|
||||||
ProviderUserStatusType.Confirmed);
|
ProviderUserStatusType.Confirmed);
|
||||||
|
|
||||||
var twoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
var twoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
||||||
var hasPremiumFromOrg = await _userService.HasPremiumFromOrganization(user);
|
var hasPremiumFromOrg = await _userService.HasPremiumFromOrganization(user);
|
||||||
var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
|
var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
|
||||||
|
|
||||||
@ -423,7 +427,7 @@ public class AccountsController : Controller
|
|||||||
|
|
||||||
await _userService.SaveUserAsync(model.ToUser(user));
|
await _userService.SaveUserAsync(model.ToUser(user));
|
||||||
|
|
||||||
var twoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
var twoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
||||||
var hasPremiumFromOrg = await _userService.HasPremiumFromOrganization(user);
|
var hasPremiumFromOrg = await _userService.HasPremiumFromOrganization(user);
|
||||||
var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
|
var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
|
||||||
|
|
||||||
@ -442,7 +446,7 @@ public class AccountsController : Controller
|
|||||||
}
|
}
|
||||||
await _userService.SaveUserAsync(model.ToUser(user), true);
|
await _userService.SaveUserAsync(model.ToUser(user), true);
|
||||||
|
|
||||||
var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
|
var userTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
||||||
var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user);
|
var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user);
|
||||||
var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
|
var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
|
||||||
|
|
||||||
@ -514,9 +518,8 @@ public class AccountsController : Controller
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If Account Deprovisioning is enabled, we need to check if the user is claimed by any organization.
|
// Check if the user is claimed by any organization.
|
||||||
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
|
if (await _userService.IsClaimedByAnyOrganizationAsync(user.Id))
|
||||||
&& await _userService.IsClaimedByAnyOrganizationAsync(user.Id))
|
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Cannot delete accounts owned by an organization. Contact your organization administrator for additional details.");
|
throw new BadRequestException("Cannot delete accounts owned by an organization. Contact your organization administrator for additional details.");
|
||||||
}
|
}
|
||||||
@ -693,7 +696,6 @@ public class AccountsController : Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[RequireFeature(FeatureFlagKeys.NewDeviceVerification)]
|
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpPost("resend-new-device-otp")]
|
[HttpPost("resend-new-device-otp")]
|
||||||
public async Task ResendNewDeviceOtpAsync([FromBody] UnauthenticatedSecretVerificationRequestModel request)
|
public async Task ResendNewDeviceOtpAsync([FromBody] UnauthenticatedSecretVerificationRequestModel request)
|
||||||
|
@ -25,7 +25,7 @@ public class UpdateTwoFactorAuthenticatorRequestModel : SecretVerificationReques
|
|||||||
{
|
{
|
||||||
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
||||||
}
|
}
|
||||||
else if (providers.ContainsKey(TwoFactorProviderType.Authenticator))
|
else
|
||||||
{
|
{
|
||||||
providers.Remove(TwoFactorProviderType.Authenticator);
|
providers.Remove(TwoFactorProviderType.Authenticator);
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ public class UpdateTwoFactorDuoRequestModel : SecretVerificationRequestModel, IV
|
|||||||
{
|
{
|
||||||
providers = [];
|
providers = [];
|
||||||
}
|
}
|
||||||
else if (providers.ContainsKey(TwoFactorProviderType.Duo))
|
else
|
||||||
{
|
{
|
||||||
providers.Remove(TwoFactorProviderType.Duo);
|
providers.Remove(TwoFactorProviderType.Duo);
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ public class UpdateTwoFactorDuoRequestModel : SecretVerificationRequestModel, IV
|
|||||||
{
|
{
|
||||||
providers = [];
|
providers = [];
|
||||||
}
|
}
|
||||||
else if (providers.ContainsKey(TwoFactorProviderType.OrganizationDuo))
|
else
|
||||||
{
|
{
|
||||||
providers.Remove(TwoFactorProviderType.OrganizationDuo);
|
providers.Remove(TwoFactorProviderType.OrganizationDuo);
|
||||||
}
|
}
|
||||||
@ -145,7 +145,7 @@ public class UpdateTwoFactorYubicoOtpRequestModel : SecretVerificationRequestMod
|
|||||||
{
|
{
|
||||||
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
||||||
}
|
}
|
||||||
else if (providers.ContainsKey(TwoFactorProviderType.YubiKey))
|
else
|
||||||
{
|
{
|
||||||
providers.Remove(TwoFactorProviderType.YubiKey);
|
providers.Remove(TwoFactorProviderType.YubiKey);
|
||||||
}
|
}
|
||||||
@ -228,7 +228,7 @@ public class TwoFactorEmailRequestModel : SecretVerificationRequestModel
|
|||||||
{
|
{
|
||||||
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
||||||
}
|
}
|
||||||
else if (providers.ContainsKey(TwoFactorProviderType.Email))
|
else
|
||||||
{
|
{
|
||||||
providers.Remove(TwoFactorProviderType.Email);
|
providers.Remove(TwoFactorProviderType.Email);
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,13 @@ public class EmergencyAccessGrantorDetailsResponseModel : EmergencyAccessRespons
|
|||||||
|
|
||||||
public class EmergencyAccessTakeoverResponseModel : ResponseModel
|
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)
|
public EmergencyAccessTakeoverResponseModel(EmergencyAccess emergencyAccess, User grantor, string obj = "emergencyAccessTakeover") : base(obj)
|
||||||
{
|
{
|
||||||
if (emergencyAccess == null)
|
if (emergencyAccess == null)
|
||||||
|
@ -13,9 +13,9 @@ public class TwoFactorAuthenticatorResponseModel : ResponseModel
|
|||||||
ArgumentNullException.ThrowIfNull(user);
|
ArgumentNullException.ThrowIfNull(user);
|
||||||
|
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Authenticator);
|
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;
|
Enabled = provider.Enabled;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -15,9 +15,9 @@ public class TwoFactorEmailResponseModel : ResponseModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.Email);
|
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;
|
Enabled = provider.Enabled;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -19,29 +19,29 @@ public class TwoFactorYubiKeyResponseModel : ResponseModel
|
|||||||
{
|
{
|
||||||
Enabled = provider.Enabled;
|
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
|
else
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
using Bit.Api.Billing.Models.Responses;
|
using Bit.Api.Billing.Models.Responses;
|
||||||
using Bit.Core.Billing.Models.Api.Requests.Accounts;
|
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Services;
|
||||||
|
using Bit.Core.Billing.Tax.Requests;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
@ -3,16 +3,13 @@ using Bit.Api.Models.Request;
|
|||||||
using Bit.Api.Models.Request.Accounts;
|
using Bit.Api.Models.Request.Accounts;
|
||||||
using Bit.Api.Models.Response;
|
using Bit.Api.Models.Response;
|
||||||
using Bit.Api.Utilities;
|
using Bit.Api.Utilities;
|
||||||
|
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||||
using Bit.Core.Billing.Models;
|
using Bit.Core.Billing.Models;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Services;
|
||||||
using Bit.Core.Context;
|
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
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.Utilities;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@ -22,7 +19,8 @@ namespace Bit.Api.Billing.Controllers;
|
|||||||
[Route("accounts")]
|
[Route("accounts")]
|
||||||
[Authorize("Application")]
|
[Authorize("Application")]
|
||||||
public class AccountsController(
|
public class AccountsController(
|
||||||
IUserService userService) : Controller
|
IUserService userService,
|
||||||
|
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery) : Controller
|
||||||
{
|
{
|
||||||
[HttpPost("premium")]
|
[HttpPost("premium")]
|
||||||
public async Task<PaymentResponseModel> PostPremiumAsync(
|
public async Task<PaymentResponseModel> PostPremiumAsync(
|
||||||
@ -56,7 +54,7 @@ public class AccountsController(
|
|||||||
model.PaymentMethodType!.Value, model.AdditionalStorageGb.GetValueOrDefault(0), license,
|
model.PaymentMethodType!.Value, model.AdditionalStorageGb.GetValueOrDefault(0), license,
|
||||||
new TaxInfo { BillingAddressCountry = model.Country, BillingAddressPostalCode = model.PostalCode });
|
new TaxInfo { BillingAddressCountry = model.Country, BillingAddressPostalCode = model.PostalCode });
|
||||||
|
|
||||||
var userTwoFactorEnabled = await userService.TwoFactorIsEnabledAsync(user);
|
var userTwoFactorEnabled = await twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(user);
|
||||||
var userHasPremiumFromOrganization = await userService.HasPremiumFromOrganization(user);
|
var userHasPremiumFromOrganization = await userService.HasPremiumFromOrganization(user);
|
||||||
var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
|
var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
|
||||||
|
|
||||||
@ -159,8 +157,6 @@ public class AccountsController(
|
|||||||
[HttpPost("cancel")]
|
[HttpPost("cancel")]
|
||||||
public async Task PostCancelAsync(
|
public async Task PostCancelAsync(
|
||||||
[FromBody] SubscriptionCancellationRequestModel request,
|
[FromBody] SubscriptionCancellationRequestModel request,
|
||||||
[FromServices] ICurrentContext currentContext,
|
|
||||||
[FromServices] IReferenceEventService referenceEventService,
|
|
||||||
[FromServices] ISubscriberService subscriberService)
|
[FromServices] ISubscriberService subscriberService)
|
||||||
{
|
{
|
||||||
var user = await userService.GetUserByPrincipalAsync(User);
|
var user = await userService.GetUserByPrincipalAsync(User);
|
||||||
@ -173,12 +169,6 @@ public class AccountsController(
|
|||||||
await subscriberService.CancelSubscription(user,
|
await subscriberService.CancelSubscription(user,
|
||||||
new OffboardingSurveyResponse { UserId = user.Id, Reason = request.Reason, Feedback = request.Feedback },
|
new OffboardingSurveyResponse { UserId = user.Id, Reason = request.Reason, Feedback = request.Feedback },
|
||||||
user.IsExpired());
|
user.IsExpired());
|
||||||
|
|
||||||
await referenceEventService.RaiseEventAsync(new ReferenceEvent(
|
|
||||||
ReferenceEventType.CancelSubscription,
|
|
||||||
user,
|
|
||||||
currentContext)
|
|
||||||
{ EndOfPeriod = user.IsExpired() });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("reinstate-premium")]
|
[HttpPost("reinstate-premium")]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.Billing.Models.Api.Requests.Organizations;
|
using Bit.Core.Billing.Tax.Requests;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
|
using System.Diagnostics;
|
||||||
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
||||||
using Bit.Api.Billing.Models.Requests;
|
using Bit.Api.Billing.Models.Requests;
|
||||||
using Bit.Api.Billing.Models.Responses;
|
using Bit.Api.Billing.Models.Responses;
|
||||||
using Bit.Api.Billing.Queries.Organizations;
|
using Bit.Api.Billing.Queries.Organizations;
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Billing.Models;
|
using Bit.Core.Billing.Models;
|
||||||
using Bit.Core.Billing.Models.Sales;
|
using Bit.Core.Billing.Models.Sales;
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
|
using Bit.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Services;
|
||||||
|
using Bit.Core.Billing.Tax.Models;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
@ -22,7 +24,6 @@ namespace Bit.Api.Billing.Controllers;
|
|||||||
public class OrganizationBillingController(
|
public class OrganizationBillingController(
|
||||||
IBusinessUnitConverter businessUnitConverter,
|
IBusinessUnitConverter businessUnitConverter,
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
IFeatureService featureService,
|
|
||||||
IOrganizationBillingService organizationBillingService,
|
IOrganizationBillingService organizationBillingService,
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
IOrganizationWarningsQuery organizationWarningsQuery,
|
IOrganizationWarningsQuery organizationWarningsQuery,
|
||||||
@ -290,13 +291,20 @@ public class OrganizationBillingController(
|
|||||||
sale.Organization.PlanType = plan.Type;
|
sale.Organization.PlanType = plan.Type;
|
||||||
sale.Organization.Plan = plan.Name;
|
sale.Organization.Plan = plan.Name;
|
||||||
sale.SubscriptionSetup.SkipTrial = true;
|
sale.SubscriptionSetup.SkipTrial = true;
|
||||||
await organizationBillingService.Finalize(sale);
|
|
||||||
var org = await organizationRepository.GetByIdAsync(organizationId);
|
if (organizationSignup.PaymentMethodType == null || string.IsNullOrEmpty(organizationSignup.PaymentToken))
|
||||||
if (organizationSignup.PaymentMethodType != null)
|
|
||||||
{
|
{
|
||||||
|
return Error.BadRequest("A payment method is required to restart the subscription.");
|
||||||
|
}
|
||||||
|
var org = await organizationRepository.GetByIdAsync(organizationId);
|
||||||
|
Debug.Assert(org is not null, "This organization has already been found via this same ID, this should be fine.");
|
||||||
var paymentSource = new TokenizedPaymentSource(organizationSignup.PaymentMethodType.Value, organizationSignup.PaymentToken);
|
var paymentSource = new TokenizedPaymentSource(organizationSignup.PaymentMethodType.Value, organizationSignup.PaymentToken);
|
||||||
var taxInformation = TaxInformation.From(organizationSignup.TaxInfo);
|
var taxInformation = TaxInformation.From(organizationSignup.TaxInfo);
|
||||||
await organizationBillingService.UpdatePaymentMethod(org, paymentSource, taxInformation);
|
await organizationBillingService.Finalize(sale);
|
||||||
|
var updatedOrg = await organizationRepository.GetByIdAsync(organizationId);
|
||||||
|
if (updatedOrg != null)
|
||||||
|
{
|
||||||
|
await organizationBillingService.UpdatePaymentMethod(updatedOrg, paymentSource, taxInformation);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TypedResults.Ok();
|
return TypedResults.Ok();
|
||||||
@ -308,14 +316,6 @@ public class OrganizationBillingController(
|
|||||||
[FromRoute] Guid organizationId,
|
[FromRoute] Guid organizationId,
|
||||||
[FromBody] SetupBusinessUnitRequestBody requestBody)
|
[FromBody] SetupBusinessUnitRequestBody requestBody)
|
||||||
{
|
{
|
||||||
var enableOrganizationBusinessUnitConversion =
|
|
||||||
featureService.IsEnabled(FeatureFlagKeys.PM18770_EnableOrganizationBusinessUnitConversion);
|
|
||||||
|
|
||||||
if (!enableOrganizationBusinessUnitConversion)
|
|
||||||
{
|
|
||||||
return Error.NotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
var organization = await organizationRepository.GetByIdAsync(organizationId);
|
var organization = await organizationRepository.GetByIdAsync(organizationId);
|
||||||
|
|
||||||
if (organization == null)
|
if (organization == null)
|
||||||
|
@ -222,6 +222,20 @@ public class OrganizationSponsorshipsController : Controller
|
|||||||
await _revokeSponsorshipCommand.RevokeSponsorshipAsync(existingOrgSponsorship);
|
await _revokeSponsorshipCommand.RevokeSponsorshipAsync(existingOrgSponsorship);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Authorize("Application")]
|
||||||
|
[HttpDelete("{sponsoringOrgId}/{sponsoredFriendlyName}/revoke")]
|
||||||
|
[SelfHosted(NotSelfHostedOnly = true)]
|
||||||
|
public async Task AdminInitiatedRevokeSponsorshipAsync(Guid sponsoringOrgId, string sponsoredFriendlyName)
|
||||||
|
{
|
||||||
|
var sponsorships = await _organizationSponsorshipRepository.GetManyBySponsoringOrganizationAsync(sponsoringOrgId);
|
||||||
|
var existingOrgSponsorship = sponsorships.FirstOrDefault(s => s.FriendlyName != null && s.FriendlyName.Equals(sponsoredFriendlyName, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (existingOrgSponsorship == null)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("The specified sponsored organization could not be found under the given sponsoring organization.");
|
||||||
|
}
|
||||||
|
await _revokeSponsorshipCommand.RevokeSponsorshipAsync(existingOrgSponsorship);
|
||||||
|
}
|
||||||
|
|
||||||
[Authorize("Application")]
|
[Authorize("Application")]
|
||||||
[HttpDelete("sponsored/{sponsoredOrgId}")]
|
[HttpDelete("sponsored/{sponsoredOrgId}")]
|
||||||
[HttpPost("sponsored/{sponsoredOrgId}/remove")]
|
[HttpPost("sponsored/{sponsoredOrgId}/remove")]
|
||||||
@ -271,8 +285,11 @@ public class OrganizationSponsorshipsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var sponsorships = await _organizationSponsorshipRepository.GetManyBySponsoringOrganizationAsync(sponsoringOrgId);
|
var sponsorships = await _organizationSponsorshipRepository.GetManyBySponsoringOrganizationAsync(sponsoringOrgId);
|
||||||
return new ListResponseModel<OrganizationSponsorshipInvitesResponseModel>(sponsorships.Select(s =>
|
return new ListResponseModel<OrganizationSponsorshipInvitesResponseModel>(
|
||||||
new OrganizationSponsorshipInvitesResponseModel(new OrganizationSponsorshipData(s))));
|
sponsorships
|
||||||
|
.Where(s => s.IsAdminInitiated)
|
||||||
|
.Select(s => new OrganizationSponsorshipInvitesResponseModel(new OrganizationSponsorshipData(s)))
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,9 +20,6 @@ using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
|||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
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.Utilities;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@ -44,7 +41,6 @@ public class OrganizationsController(
|
|||||||
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
|
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
|
||||||
IUpgradeOrganizationPlanCommand upgradeOrganizationPlanCommand,
|
IUpgradeOrganizationPlanCommand upgradeOrganizationPlanCommand,
|
||||||
IAddSecretsManagerSubscriptionCommand addSecretsManagerSubscriptionCommand,
|
IAddSecretsManagerSubscriptionCommand addSecretsManagerSubscriptionCommand,
|
||||||
IReferenceEventService referenceEventService,
|
|
||||||
ISubscriberService subscriberService,
|
ISubscriberService subscriberService,
|
||||||
IOrganizationInstallationRepository organizationInstallationRepository,
|
IOrganizationInstallationRepository organizationInstallationRepository,
|
||||||
IPricingClient pricingClient)
|
IPricingClient pricingClient)
|
||||||
@ -109,28 +105,6 @@ public class OrganizationsController(
|
|||||||
return license;
|
return license;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id:guid}/payment")]
|
|
||||||
[SelfHosted(NotSelfHostedOnly = true)]
|
|
||||||
public async Task PostPayment(Guid id, [FromBody] PaymentRequestModel model)
|
|
||||||
{
|
|
||||||
if (!await currentContext.EditPaymentMethods(id))
|
|
||||||
{
|
|
||||||
throw new NotFoundException();
|
|
||||||
}
|
|
||||||
|
|
||||||
await organizationService.ReplacePaymentMethodAsync(id, model.PaymentToken,
|
|
||||||
model.PaymentMethodType.Value, new TaxInfo
|
|
||||||
{
|
|
||||||
BillingAddressLine1 = model.Line1,
|
|
||||||
BillingAddressLine2 = model.Line2,
|
|
||||||
BillingAddressState = model.State,
|
|
||||||
BillingAddressCity = model.City,
|
|
||||||
BillingAddressPostalCode = model.PostalCode,
|
|
||||||
BillingAddressCountry = model.Country,
|
|
||||||
TaxIdNumber = model.TaxId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("{id:guid}/upgrade")]
|
[HttpPost("{id:guid}/upgrade")]
|
||||||
[SelfHosted(NotSelfHostedOnly = true)]
|
[SelfHosted(NotSelfHostedOnly = true)]
|
||||||
public async Task<PaymentResponseModel> PostUpgrade(Guid id, [FromBody] OrganizationUpgradeRequestModel model)
|
public async Task<PaymentResponseModel> PostUpgrade(Guid id, [FromBody] OrganizationUpgradeRequestModel model)
|
||||||
@ -268,14 +242,6 @@ public class OrganizationsController(
|
|||||||
Feedback = request.Feedback
|
Feedback = request.Feedback
|
||||||
},
|
},
|
||||||
organization.IsExpired());
|
organization.IsExpired());
|
||||||
|
|
||||||
await referenceEventService.RaiseEventAsync(new ReferenceEvent(
|
|
||||||
ReferenceEventType.CancelSubscription,
|
|
||||||
organization,
|
|
||||||
currentContext)
|
|
||||||
{
|
|
||||||
EndOfPeriod = organization.IsExpired()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id:guid}/reinstate")]
|
[HttpPost("{id:guid}/reinstate")]
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
using Bit.Api.Billing.Models.Requests;
|
using Bit.Api.Billing.Models.Requests;
|
||||||
using Bit.Api.Billing.Models.Responses;
|
using Bit.Api.Billing.Models.Responses;
|
||||||
|
using Bit.Commercial.Core.Billing.Providers.Services;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.Billing.Models;
|
|
||||||
using Bit.Core.Billing.Pricing;
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Billing.Repositories;
|
using Bit.Core.Billing.Providers.Models;
|
||||||
|
using Bit.Core.Billing.Providers.Repositories;
|
||||||
|
using Bit.Core.Billing.Providers.Services;
|
||||||
using Bit.Core.Billing.Services;
|
using Bit.Core.Billing.Services;
|
||||||
|
using Bit.Core.Billing.Tax.Models;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Models.BitStripe;
|
using Bit.Core.Models.BitStripe;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
@ -78,13 +81,6 @@ public class ProviderBillingController(
|
|||||||
[FromRoute] Guid providerId,
|
[FromRoute] Guid providerId,
|
||||||
[FromBody] UpdatePaymentMethodRequestBody requestBody)
|
[FromBody] UpdatePaymentMethodRequestBody requestBody)
|
||||||
{
|
{
|
||||||
var allowProviderPaymentMethod = featureService.IsEnabled(FeatureFlagKeys.PM18794_ProviderPaymentMethod);
|
|
||||||
|
|
||||||
if (!allowProviderPaymentMethod)
|
|
||||||
{
|
|
||||||
return TypedResults.NotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
var (provider, result) = await TryGetBillableProviderForAdminOperation(providerId);
|
var (provider, result) = await TryGetBillableProviderForAdminOperation(providerId);
|
||||||
|
|
||||||
if (provider == null)
|
if (provider == null)
|
||||||
@ -108,13 +104,6 @@ public class ProviderBillingController(
|
|||||||
[FromRoute] Guid providerId,
|
[FromRoute] Guid providerId,
|
||||||
[FromBody] VerifyBankAccountRequestBody requestBody)
|
[FromBody] VerifyBankAccountRequestBody requestBody)
|
||||||
{
|
{
|
||||||
var allowProviderPaymentMethod = featureService.IsEnabled(FeatureFlagKeys.PM18794_ProviderPaymentMethod);
|
|
||||||
|
|
||||||
if (!allowProviderPaymentMethod)
|
|
||||||
{
|
|
||||||
return TypedResults.NotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
var (provider, result) = await TryGetBillableProviderForAdminOperation(providerId);
|
var (provider, result) = await TryGetBillableProviderForAdminOperation(providerId);
|
||||||
|
|
||||||
if (provider == null)
|
if (provider == null)
|
||||||
@ -147,13 +136,33 @@ public class ProviderBillingController(
|
|||||||
|
|
||||||
var providerPlans = await providerPlanRepository.GetByProviderId(provider.Id);
|
var providerPlans = await providerPlanRepository.GetByProviderId(provider.Id);
|
||||||
|
|
||||||
|
var getProviderPriceFromStripe = featureService.IsEnabled(FeatureFlagKeys.PM21383_GetProviderPriceFromStripe);
|
||||||
|
|
||||||
var configuredProviderPlans = await Task.WhenAll(providerPlans.Select(async providerPlan =>
|
var configuredProviderPlans = await Task.WhenAll(providerPlans.Select(async providerPlan =>
|
||||||
{
|
{
|
||||||
var plan = await pricingClient.GetPlanOrThrow(providerPlan.PlanType);
|
var plan = await pricingClient.GetPlanOrThrow(providerPlan.PlanType);
|
||||||
|
|
||||||
|
decimal unitAmount;
|
||||||
|
|
||||||
|
if (getProviderPriceFromStripe)
|
||||||
|
{
|
||||||
|
var priceId = ProviderPriceAdapter.GetPriceId(provider, subscription, plan.Type);
|
||||||
|
var price = await stripeAdapter.PriceGetAsync(priceId);
|
||||||
|
|
||||||
|
unitAmount = price.UnitAmountDecimal.HasValue
|
||||||
|
? price.UnitAmountDecimal.Value / 100M
|
||||||
|
: plan.PasswordManager.ProviderPortalSeatPrice;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unitAmount = plan.PasswordManager.ProviderPortalSeatPrice;
|
||||||
|
}
|
||||||
|
|
||||||
return new ConfiguredProviderPlan(
|
return new ConfiguredProviderPlan(
|
||||||
providerPlan.Id,
|
providerPlan.Id,
|
||||||
providerPlan.ProviderId,
|
providerPlan.ProviderId,
|
||||||
plan,
|
plan,
|
||||||
|
unitAmount,
|
||||||
providerPlan.SeatMinimum ?? 0,
|
providerPlan.SeatMinimum ?? 0,
|
||||||
providerPlan.PurchasedSeats ?? 0,
|
providerPlan.PurchasedSeats ?? 0,
|
||||||
providerPlan.AllocatedSeats ?? 0);
|
providerPlan.AllocatedSeats ?? 0);
|
||||||
|
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