mirror of
https://github.com/bitwarden/server.git
synced 2026-02-06 17:13:10 +08:00
Merge remote-tracking branch 'origin/master' into feature/self-hosted-families-for-enterprise
This commit is contained in:
219
.github/workflows/build.yml
vendored
219
.github/workflows/build.yml
vendored
@@ -29,6 +29,8 @@ jobs:
|
||||
testing:
|
||||
name: Testing
|
||||
runs-on: windows-2019
|
||||
env:
|
||||
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
|
||||
steps:
|
||||
- name: Set up NuGet
|
||||
uses: nuget/setup-nuget@04b0c2b8d1b97922f67eca497d7cf0bf17b8ffe1
|
||||
@@ -38,29 +40,26 @@ jobs:
|
||||
- name: Set up MSBuild
|
||||
uses: microsoft/setup-msbuild@c26a08ba26249b81327e26f6ef381897b6a8754d
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@46071b5c7a2e0c34e49c3cb8a0e792e86e18d5ea
|
||||
with:
|
||||
node-version: '14'
|
||||
|
||||
- name: Update NPM
|
||||
run: npm install -g npm@7
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
nuget help | grep Version
|
||||
msbuild -version
|
||||
dotnet --info
|
||||
node --version
|
||||
npm --version
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
echo "GitHub event: $GITHUB_EVENT"
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed # 2.1.7
|
||||
with:
|
||||
path: ${{ github.workspace }}/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-nuget-
|
||||
|
||||
- name: Restore
|
||||
run: msbuild /t:restore
|
||||
run: msbuild /t:restore /p:RestoreLockedMode=true
|
||||
shell: pwsh
|
||||
|
||||
- name: Build solution
|
||||
@@ -190,91 +189,61 @@ jobs:
|
||||
include:
|
||||
- service_name: Admin
|
||||
base_path: ./src
|
||||
docker_repo: bitwarden
|
||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
||||
dotnet: true
|
||||
- service_name: Api
|
||||
base_path: ./src
|
||||
docker_repo: bitwarden
|
||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
||||
dotnet: true
|
||||
- service_name: Attachments
|
||||
base_path: ./util
|
||||
docker_repo: bitwarden
|
||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
||||
- service_name: Events
|
||||
base_path: ./src
|
||||
docker_repo: bitwarden
|
||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
||||
dotnet: true
|
||||
- service_name: EventsProcessor
|
||||
base_path: ./src
|
||||
docker_repo: bitwardenqa.azurecr.io
|
||||
docker_repos: [bitwardenqa.azurecr.io]
|
||||
dotnet: true
|
||||
- service_name: Icons
|
||||
base_path: ./src
|
||||
docker_repo: bitwarden
|
||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
||||
dotnet: true
|
||||
- service_name: Identity
|
||||
base_path: ./src
|
||||
docker_repo: bitwarden
|
||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
||||
dotnet: true
|
||||
- service_name: K8S-Proxy
|
||||
base_path: ./util
|
||||
docker_repo: bitwarden
|
||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
||||
- service_name: MsSql
|
||||
base_path: ./util
|
||||
docker_repo: bitwarden
|
||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
||||
- service_name: Nginx
|
||||
base_path: ./util
|
||||
docker_repo: bitwarden
|
||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
||||
- service_name: Notifications
|
||||
base_path: ./src
|
||||
docker_repo: bitwarden
|
||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
||||
dotnet: true
|
||||
- service_name: Server
|
||||
base_path: ./util
|
||||
docker_repo: bitwarden
|
||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
||||
dotnet: true
|
||||
- service_name: Setup
|
||||
base_path: ./util
|
||||
docker_repo: bitwarden
|
||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
||||
dotnet: true
|
||||
- service_name: Sso
|
||||
base_path: ./bitwarden_license/src
|
||||
docker_repo: bitwarden
|
||||
docker_repos: [bitwarden, bitwardenqa.azurecr.io]
|
||||
dotnet: true
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
- name: Login to Azure - Prod Subscription
|
||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||
|
||||
- name: Retrieve secrets
|
||||
id: retrieve-secrets
|
||||
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
|
||||
with:
|
||||
keyvault: "bitwarden-prod-kv"
|
||||
secrets: "docker-password,
|
||||
docker-username,
|
||||
dct-delegate-2-repo-passphrase,
|
||||
dct-delegate-2-key"
|
||||
|
||||
- name: Log into Docker
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ steps.retrieve-secrets.outputs.docker-username }}
|
||||
DOCKER_PASSWORD: ${{ steps.retrieve-secrets.outputs.docker-password }}
|
||||
run: echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
||||
|
||||
- name: Setup Docker Trust
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
|
||||
env:
|
||||
DCT_DELEGATION_KEY_ID: "c9bde8ec820701516491e5e03d3a6354e7bd66d05fa3df2b0062f68b116dc59c"
|
||||
DCT_DELEGATE_KEY: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-key }}
|
||||
run: |
|
||||
mkdir -p ~/.docker/trust/private
|
||||
echo "$DCT_DELEGATE_KEY" > ~/.docker/trust/private/$DCT_DELEGATION_KEY_ID.key
|
||||
|
||||
########## Build Docker Image ##########
|
||||
- name: Setup service name
|
||||
id: setup
|
||||
run: |
|
||||
@@ -306,43 +275,7 @@ jobs:
|
||||
${{ matrix.base_path }}/${{ matrix.service_name }}
|
||||
fi
|
||||
|
||||
- name: Docker Trust setup
|
||||
if: |
|
||||
matrix.docker_repo == 'bitwarden'
|
||||
&& (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix')
|
||||
env:
|
||||
DCT_REPO_PASSPHRASE: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-repo-passphrase }}
|
||||
run: |
|
||||
echo "DOCKER_CONTENT_TRUST=1" >> $GITHUB_ENV
|
||||
echo "DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=$DCT_REPO_PASSPHRASE" >> $GITHUB_ENV
|
||||
|
||||
- name: Tag and Push RC to Docker Hub
|
||||
if: (github.ref == 'refs/heads/rc' && matrix.docker_repo == 'bitwarden')
|
||||
run: |
|
||||
docker tag ${{ steps.setup.outputs.service_name }} \
|
||||
${{ matrix.docker_repo }}/${{ steps.setup.outputs.service_name }}:rc
|
||||
docker push ${{ matrix.docker_repo }}/${{ steps.setup.outputs.service_name }}:rc
|
||||
|
||||
- name: Tag and Push Hotfix to Docker Hub
|
||||
if: (github.ref == 'refs/heads/hotfix' && matrix.docker_repo == 'bitwarden')
|
||||
run: |
|
||||
docker tag ${{ steps.setup.outputs.service_name }} \
|
||||
${{ matrix.docker_repo }}/${{ steps.setup.outputs.service_name }}:hotfix
|
||||
docker push ${{ matrix.docker_repo }}/${{ steps.setup.outputs.service_name }}:hotfix
|
||||
|
||||
- name: Tag and Push Dev to Docker Hub
|
||||
if: (github.ref == 'refs/heads/master' && matrix.docker_repo == 'bitwarden')
|
||||
run: |
|
||||
docker tag ${{ steps.setup.outputs.service_name }} \
|
||||
${{ matrix.docker_repo }}/${{ steps.setup.outputs.service_name }}:dev
|
||||
docker push ${{ matrix.docker_repo }}/${{ steps.setup.outputs.service_name }}:dev
|
||||
|
||||
- name: Log out of Docker and disable Docker Notary
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
|
||||
run: |
|
||||
docker logout
|
||||
echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
|
||||
|
||||
########## ACR ##########
|
||||
- name: Login to Azure - QA Subscription
|
||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||
with:
|
||||
@@ -352,31 +285,89 @@ jobs:
|
||||
run: az acr login -n bitwardenqa
|
||||
|
||||
- name: Tag and Push RC to Azure ACR QA registry
|
||||
if: github.ref == 'refs/heads/rc'
|
||||
env:
|
||||
REGISTRY: bitwardenqa.azurecr.io
|
||||
run: |
|
||||
docker tag ${{ steps.setup.outputs.service_name }} \
|
||||
$REGISTRY/${{ steps.setup.outputs.service_name }}:rc
|
||||
docker push $REGISTRY/${{ steps.setup.outputs.service_name }}:rc
|
||||
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
|
||||
if [[ "$IMAGE_TAG" == "master" ]]; then
|
||||
IMAGE_TAG=dev
|
||||
fi
|
||||
|
||||
- name: Tag and Push Hotfix to Azure ACR QA registry
|
||||
if: github.ref == 'refs/heads/hotfix'
|
||||
env:
|
||||
REGISTRY: bitwardenqa.azurecr.io
|
||||
run: |
|
||||
docker tag ${{ steps.setup.outputs.service_name }} \
|
||||
$REGISTRY/${{ steps.setup.outputs.service_name }}:hotfix
|
||||
docker push $REGISTRY/${{ steps.setup.outputs.service_name }}:hotfix
|
||||
$REGISTRY/${{ steps.setup.outputs.service_name }}:$IMAGE_TAG
|
||||
docker push $REGISTRY/${{ steps.setup.outputs.service_name }}:$IMAGE_TAG
|
||||
|
||||
- name: Tag and Push Dev to Azure ACR QA registry
|
||||
if: github.ref == 'refs/heads/master'
|
||||
- name: Log out of Docker
|
||||
run: docker logout
|
||||
|
||||
########## DockerHub ##########
|
||||
- name: Login to Azure - Prod Subscription
|
||||
if: |
|
||||
contains(matrix.docker_repos, 'bitwarden')
|
||||
&& (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
|
||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||
|
||||
- name: Retrieve secrets
|
||||
if: |
|
||||
contains(matrix.docker_repos, 'bitwarden')
|
||||
&& (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
|
||||
id: retrieve-secrets
|
||||
uses: Azure/get-keyvault-secrets@80ccd3fafe5662407cc2e55f202ee34bfff8c403
|
||||
with:
|
||||
keyvault: "bitwarden-prod-kv"
|
||||
secrets: "docker-password,
|
||||
docker-username,
|
||||
dct-delegate-2-repo-passphrase,
|
||||
dct-delegate-2-key"
|
||||
|
||||
- name: Log into Docker
|
||||
if: |
|
||||
contains(matrix.docker_repos, 'bitwarden')
|
||||
&& (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
|
||||
env:
|
||||
REGISTRY: bitwardenqa.azurecr.io
|
||||
DOCKER_USERNAME: ${{ steps.retrieve-secrets.outputs.docker-username }}
|
||||
DOCKER_PASSWORD: ${{ steps.retrieve-secrets.outputs.docker-password }}
|
||||
run: echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
||||
|
||||
- name: Setup Docker Trust
|
||||
if: |
|
||||
contains(matrix.docker_repos, 'bitwarden')
|
||||
&& (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
|
||||
env:
|
||||
DCT_DELEGATION_KEY_ID: "c9bde8ec820701516491e5e03d3a6354e7bd66d05fa3df2b0062f68b116dc59c"
|
||||
DCT_DELEGATE_KEY: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-key }}
|
||||
DCT_REPO_PASSPHRASE: ${{ steps.retrieve-secrets.outputs.dct-delegate-2-repo-passphrase }}
|
||||
run: |
|
||||
mkdir -p ~/.docker/trust/private
|
||||
echo "$DCT_DELEGATE_KEY" > ~/.docker/trust/private/$DCT_DELEGATION_KEY_ID.key
|
||||
echo "DOCKER_CONTENT_TRUST=1" >> $GITHUB_ENV
|
||||
echo "DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=$DCT_REPO_PASSPHRASE" >> $GITHUB_ENV
|
||||
|
||||
- name: Tag and Push RC to Docker Hub
|
||||
if: |
|
||||
contains(matrix.docker_repos, 'bitwarden')
|
||||
&& (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
|
||||
env:
|
||||
REGISTRY: bitwarden
|
||||
run: |
|
||||
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
|
||||
if [[ "$IMAGE_TAG" == "master" ]]; then
|
||||
IMAGE_TAG=dev
|
||||
fi
|
||||
|
||||
docker tag ${{ steps.setup.outputs.service_name }} \
|
||||
$REGISTRY/${{ steps.setup.outputs.service_name }}:dev
|
||||
docker push $REGISTRY/${{ steps.setup.outputs.service_name }}:dev
|
||||
$REGISTRY/${{ steps.setup.outputs.service_name }}:$IMAGE_TAG
|
||||
docker push $REGISTRY/${{ steps.setup.outputs.service_name }}:$IMAGE_TAG
|
||||
|
||||
- name: Log out of Docker and disable Docker Notary
|
||||
if: |
|
||||
contains(matrix.docker_repos, 'bitwarden')
|
||||
&& (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc')
|
||||
run: |
|
||||
docker logout
|
||||
echo "DOCKER_CONTENT_TRUST=0" >> $GITHUB_ENV
|
||||
|
||||
|
||||
upload:
|
||||
@@ -391,12 +382,12 @@ jobs:
|
||||
run: dotnet tool restore
|
||||
|
||||
- name: Make Docker stub
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
||||
run: |
|
||||
if [[ "${{ github.ref }}" == "rc" ]]; then
|
||||
SETUP_IMAGE="bitwarden/setup:rc"
|
||||
elif [[ "${{ github.ref }}" == "hotfix" ]]; then
|
||||
SETUP_IMAGE="bitwarden/setup:hotfix"
|
||||
elif [[ "${{ github.ref }}" == "hotfix-rc" ]]; then
|
||||
SETUP_IMAGE="bitwarden/setup:hotfix-rc"
|
||||
else
|
||||
SETUP_IMAGE="bitwarden/setup:dev"
|
||||
fi
|
||||
@@ -411,7 +402,7 @@ jobs:
|
||||
cd docker-stub; zip -r ../docker-stub.zip *; cd ..
|
||||
|
||||
- name: Upload Docker stub artifact
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix'
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc'
|
||||
uses: actions/upload-artifact@ee69f02b3dfdecd58bb31b4d133da38ba6fe3700
|
||||
with:
|
||||
name: docker-stub.zip
|
||||
@@ -458,7 +449,7 @@ jobs:
|
||||
if: |
|
||||
github.ref == 'refs/heads/master'
|
||||
|| github.ref == 'refs/heads/rc'
|
||||
|| github.ref == 'refs/heads/hotfix'
|
||||
|| github.ref == 'refs/heads/hotfix-rc'
|
||||
env:
|
||||
CLOC_STATUS: ${{ needs.cloc.result }}
|
||||
TESTING_STATUS: ${{ needs.testing.result }}
|
||||
|
||||
66
.github/workflows/cleanup-after-pr.yml
vendored
Normal file
66
.github/workflows/cleanup-after-pr.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
---
|
||||
name: Clean After PR
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
build-docker:
|
||||
name: Remove feature branch docker images
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
|
||||
|
||||
########## ACR ##########
|
||||
- name: Login to Azure - QA Subscription
|
||||
uses: Azure/login@77f1b2e3fb80c0e8645114159d17008b8a2e475a
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
|
||||
|
||||
- name: Login to Azure ACR
|
||||
run: az acr login -n bitwardenqa
|
||||
|
||||
########## Remove Docker images ##########
|
||||
- name: Remove the docker image from ACR
|
||||
env:
|
||||
REGISTRY_NAME: bitwardenqa
|
||||
SERVICES: |
|
||||
services:
|
||||
- Admin
|
||||
- Api
|
||||
- Attachments
|
||||
- Events
|
||||
- EventsProcessor
|
||||
- Icons
|
||||
- Identity
|
||||
- K8S-Proxy
|
||||
- MsSql
|
||||
- Nginx
|
||||
- Notifications
|
||||
- Server
|
||||
- Setup
|
||||
- Sso
|
||||
run: |
|
||||
for SERVICE in $(echo "${{ env.SERVICES }}" | yq e ".services[]" - )
|
||||
do
|
||||
SERVICE_NAME=$(echo $SERVICE | awk '{print tolower($0)}')
|
||||
IMAGE_TAG=$(echo "${GITHUB_REF:11}" | sed "s#/#-#g") # slash safe branch name
|
||||
|
||||
echo "[*] Checking if remote exists: $REGISTRY_NAME.azurecr.io/$SERVICE_NAME:$IMAGE_TAG"
|
||||
TAG_EXISTS=$(
|
||||
az acr repository show-tags --name $REGISTRY_NAME --repository $SERVICE_NAME \
|
||||
| jq --arg $TAG "$IMAGE_TAG" -e '. | any(. == "$TAG")'
|
||||
)
|
||||
|
||||
if [[ "$TAG_EXISTS" == "true" ]]; then
|
||||
echo "[*] Tag exists. Removing tag"
|
||||
az acr repository delete --name $REGISTRY_NAME --image $SERVICE_NAME:$IMAGE_TAG --yes
|
||||
else
|
||||
echo "[*] Tag does not exist. No action needed"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Log out of Docker
|
||||
run: docker logout
|
||||
96
.github/workflows/container-registry-purge.yml
vendored
Normal file
96
.github/workflows/container-registry-purge.yml
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
---
|
||||
name: Container Registry Purge
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * SUN'
|
||||
workflow_dispatch:
|
||||
inputs: {}
|
||||
|
||||
jobs:
|
||||
purge:
|
||||
name: Purge old images
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: bitwardenqa
|
||||
- name: bitwardenprod
|
||||
steps:
|
||||
- name: Login to Azure
|
||||
if: matrix.name == 'bitwardenprod'
|
||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||
|
||||
- name: Login to Azure
|
||||
if: matrix.name == 'bitwardenqa'
|
||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_QA_KV_CREDENTIALS }}
|
||||
|
||||
- name: Purge images
|
||||
env:
|
||||
REGISTRY: ${{ matrix.name }}
|
||||
AGO_DUR_VER: "180d"
|
||||
AGO_DUR: "30d"
|
||||
run: |
|
||||
REPO_LIST=$(az acr repository list -n $REGISTRY -o tsv)
|
||||
for REPO in $REPO_LIST
|
||||
do
|
||||
TAG_LIST=$(az acr repository show-tags -n $REGISTRY --repository $REPO -o tsv)
|
||||
for TAG in $TAG_LIST
|
||||
do
|
||||
if [ $TAG = "latest" ]; then
|
||||
PURGE_CMD="acr purge --filter '$REPO:$TAG' --ago $AGO_DUR_VER --untagged --keep 1"
|
||||
elif [[ $TAG =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
|
||||
PURGE_CMD="acr purge --filter '$REPO:$TAG' --ago $AGO_DUR_VER --untagged"
|
||||
else
|
||||
PURGE_CMD="acr purge --filter '$REPO:$TAG' --ago $AGO_DUR --untagged"
|
||||
fi
|
||||
az acr run --cmd "$PURGE_CMD" --registry $REGISTRY /dev/null
|
||||
done
|
||||
done
|
||||
|
||||
|
||||
check-failures:
|
||||
name: Check for failures
|
||||
if: always()
|
||||
runs-on: ubuntu-20.04
|
||||
needs:
|
||||
- purge
|
||||
steps:
|
||||
- name: Check if any job failed
|
||||
if: |
|
||||
github.ref == 'refs/heads/master'
|
||||
|| github.ref == 'refs/heads/rc'
|
||||
|| github.ref == 'refs/heads/hotfix-rc'
|
||||
env:
|
||||
PURGE_STATUS: ${{ needs.purge.result }}
|
||||
run: |
|
||||
if [ "$PURGE_STATUS" = "failure" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Login to Azure - Prod Subscription
|
||||
uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
|
||||
if: failure()
|
||||
with:
|
||||
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
|
||||
|
||||
- name: Retrieve secrets
|
||||
id: retrieve-secrets
|
||||
uses: Azure/get-keyvault-secrets@470858ec5d16542d1a87e1998c0988130ef304dd
|
||||
if: failure()
|
||||
with:
|
||||
keyvault: "bitwarden-prod-kv"
|
||||
secrets: "devops-alerts-slack-webhook-url"
|
||||
|
||||
- name: Notify Slack on failure
|
||||
uses: act10ns/slack@186ef8b81dc1f91522af6998d8019e2e886da2ca # v1.2.2
|
||||
if: failure()
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
16
.github/workflows/enforce-labels.yml
vendored
Normal file
16
.github/workflows/enforce-labels.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: Enforce PR labels
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled, unlabeled, opened, edited, synchronize]
|
||||
jobs:
|
||||
enforce-label:
|
||||
name: EnforceLabel
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Enforce Label
|
||||
uses: yogevbd/enforce-label-action@8d1e1709b1011e6d90400a0e6cf7c0b77aa5efeb
|
||||
with:
|
||||
BANNED_LABELS: "hold"
|
||||
BANNED_LABELS_DESCRIPTION: "PRs on hold cannot be merged"
|
||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -24,9 +24,9 @@ jobs:
|
||||
steps:
|
||||
- name: Branch check
|
||||
run: |
|
||||
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix" ]]; then
|
||||
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
|
||||
echo "==================================="
|
||||
echo "[!] Can only release from the 'rc' or 'hotfix' branches"
|
||||
echo "[!] Can only release from the 'rc' or 'hotfix-rc' branches"
|
||||
echo "==================================="
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<Version>1.45.3</Version>
|
||||
<Version>1.46.1</Version>
|
||||
<RootNamespace>Bit.$(MSBuildProjectName)</RootNamespace>
|
||||
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
3204
bitwarden_license/src/CommCore/packages.lock.json
Normal file
3204
bitwarden_license/src/CommCore/packages.lock.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Entities;
|
||||
@@ -25,7 +24,6 @@ using Microsoft.IdentityModel.Tokens;
|
||||
using Sustainsys.Saml2.AspNetCore2;
|
||||
using Sustainsys.Saml2.Configuration;
|
||||
using Sustainsys.Saml2.Saml2P;
|
||||
using U2F.Core.Utils;
|
||||
|
||||
namespace Bit.Core.Business.Sso
|
||||
{
|
||||
@@ -412,7 +410,7 @@ namespace Bit.Core.Business.Sso
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(config.IdpX509PublicCert))
|
||||
{
|
||||
var cert = config.IdpX509PublicCert.Base64StringToByteArray();
|
||||
var cert = CoreHelpers.Base64UrlDecode(config.IdpX509PublicCert);
|
||||
idp.SigningKeys.AddConfiguredKey(new X509Certificate2(cert));
|
||||
}
|
||||
// This must happen last since it calls Validate() internally.
|
||||
|
||||
3412
bitwarden_license/src/Sso/packages.lock.json
Normal file
3412
bitwarden_license/src/Sso/packages.lock.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
3628
bitwarden_license/test/CmmCore.Test/packages.lock.json
Normal file
3628
bitwarden_license/test/CmmCore.Test/packages.lock.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -26,9 +26,9 @@ $scriptsDir = "${output}\scripts"
|
||||
$githubBaseUrl = "https://raw.githubusercontent.com/bitwarden/server/master"
|
||||
|
||||
# Please do not create pull requests modifying the version numbers.
|
||||
$coreVersion = "1.45.2"
|
||||
$webVersion = "2.25.0"
|
||||
$keyConnectorVersion = "1.0.0"
|
||||
$coreVersion = "1.46.1"
|
||||
$webVersion = "2.26.1"
|
||||
$keyConnectorVersion = "1.0.1"
|
||||
|
||||
# Functions
|
||||
|
||||
|
||||
@@ -34,9 +34,9 @@ SCRIPTS_DIR="$OUTPUT/scripts"
|
||||
GITHUB_BASE_URL="https://raw.githubusercontent.com/bitwarden/server/master"
|
||||
|
||||
# Please do not create pull requests modifying the version numbers.
|
||||
COREVERSION="1.45.2"
|
||||
WEBVERSION="2.25.0"
|
||||
KEYCONNECTORVERSION="1.0.0"
|
||||
COREVERSION="1.46.1"
|
||||
WEBVERSION="2.26.1"
|
||||
KEYCONNECTORVERSION="1.0.1"
|
||||
|
||||
echo "bitwarden.sh version $COREVERSION"
|
||||
docker --version
|
||||
|
||||
@@ -191,7 +191,7 @@ function updatebw() {
|
||||
KEY_CONNECTOR_ENABLED=$(grep -A3 'enable_key_connector:' $OUTPUT_DIR/config.yml | tail -n1 | awk '{ print $2}')
|
||||
CORE_ID=$(docker-compose ps -q admin)
|
||||
WEB_ID=$(docker-compose ps -q web)
|
||||
if [ $KEY_CONNECTOR_ENABLED = true ];
|
||||
if [ "$KEY_CONNECTOR_ENABLED" = true ];
|
||||
then
|
||||
KEYCONNECTOR_ID=$(docker-compose ps -q key-connector)
|
||||
fi
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Admin.Models;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Bit.Admin.Controllers
|
||||
{
|
||||
@@ -37,20 +38,21 @@ namespace Bit.Admin.Controllers
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IActionResult> GetLatestDockerHubVersion(string repository)
|
||||
public async Task<IActionResult> GetLatestDockerHubVersion(string repository, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.GetAsync(
|
||||
$"https://hub.docker.com/v2/repositories/bitwarden/{repository}/tags/");
|
||||
$"https://hub.docker.com/v2/repositories/bitwarden/{repository}/tags/", cancellationToken);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
var data = JObject.Parse(json);
|
||||
var results = data["results"] as JArray;
|
||||
foreach (var result in results)
|
||||
using var jsonDocument = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync(cancellationToken), cancellationToken: cancellationToken);
|
||||
var root = jsonDocument.RootElement;
|
||||
|
||||
var results = root.GetProperty("results");
|
||||
foreach (var result in results.EnumerateArray())
|
||||
{
|
||||
var name = result["name"].ToString();
|
||||
var name = result.GetProperty("name").GetString();
|
||||
if (!string.IsNullOrWhiteSpace(name) && name.Length > 0 && char.IsNumber(name[0]))
|
||||
{
|
||||
return new JsonResult(name);
|
||||
@@ -63,17 +65,17 @@ namespace Bit.Admin.Controllers
|
||||
return new JsonResult("-");
|
||||
}
|
||||
|
||||
public async Task<IActionResult> GetInstalledWebVersion()
|
||||
public async Task<IActionResult> GetInstalledWebVersion(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.GetAsync(
|
||||
$"{_globalSettings.BaseServiceUri.InternalVault}/version.json");
|
||||
$"{_globalSettings.BaseServiceUri.InternalVault}/version.json", cancellationToken);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
var data = JObject.Parse(json);
|
||||
return new JsonResult(data["version"].ToString());
|
||||
using var jsonDocument = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync(cancellationToken), cancellationToken: cancellationToken);
|
||||
var root = jsonDocument.RootElement;
|
||||
return new JsonResult(root.GetProperty("version").GetString());
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException) { }
|
||||
|
||||
@@ -3,9 +3,9 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Admin.Models;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@@ -14,7 +14,6 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Admin.Controllers
|
||||
{
|
||||
@@ -264,14 +263,16 @@ namespace Bit.Admin.Controllers
|
||||
{
|
||||
var license = await _organizationService.GenerateLicenseAsync(organization,
|
||||
model.InstallationId.Value, model.Version);
|
||||
return File(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(license, Formatting.Indented)),
|
||||
"text/plain", "bitwarden_organization_license.json");
|
||||
var ms = new MemoryStream();
|
||||
await JsonSerializer.SerializeAsync(ms, license, JsonHelpers.Indented);
|
||||
return File(ms, "text/plain", "bitwarden_organization_license.json");
|
||||
}
|
||||
else if (user != null)
|
||||
{
|
||||
var license = await _userService.GenerateLicenseAsync(user, null, model.Version);
|
||||
return File(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(license, Formatting.Indented)),
|
||||
"text/plain", "bitwarden_premium_license.json");
|
||||
var ms = new MemoryStream();
|
||||
await JsonSerializer.SerializeAsync(ms, license, JsonHelpers.Indented);
|
||||
return File(ms, "text/plain", "bitwarden_premium_license.json");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.Storage.Queues;
|
||||
@@ -11,8 +12,6 @@ using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Bit.Admin.HostedServices
|
||||
{
|
||||
@@ -70,17 +69,19 @@ namespace Bit.Admin.HostedServices
|
||||
{
|
||||
try
|
||||
{
|
||||
var token = JToken.Parse(message.DecodeMessageText());
|
||||
if (token is JArray)
|
||||
using var document = JsonDocument.Parse(message.DecodeMessageText());
|
||||
var root = document.RootElement;
|
||||
|
||||
if (root.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
foreach (var mailQueueMessage in token.ToObject<List<MailQueueMessage>>())
|
||||
foreach (var mailQueueMessage in root.ToObject<List<MailQueueMessage>>())
|
||||
{
|
||||
await _mailService.SendEnqueuedMailMessageAsync(mailQueueMessage);
|
||||
}
|
||||
}
|
||||
else if (token is JObject)
|
||||
else if (root.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
var mailQueueMessage = token.ToObject<MailQueueMessage>();
|
||||
var mailQueueMessage = root.ToObject<MailQueueMessage>();
|
||||
await _mailService.SendEnqueuedMailMessageAsync(mailQueueMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Admin.HostedServices
|
||||
{
|
||||
@@ -65,7 +64,7 @@ namespace Bit.Admin.HostedServices
|
||||
request.RequestUri = new Uri("https://api.cloudflare.com/" +
|
||||
$"client/v4/zones/{_adminSettings.Cloudflare.ZoneId}/firewall/access_rules/rules");
|
||||
|
||||
var bodyContent = JsonConvert.SerializeObject(new
|
||||
request.Content = JsonContent.Create(new
|
||||
{
|
||||
mode = "block",
|
||||
configuration = new
|
||||
@@ -75,7 +74,6 @@ namespace Bit.Admin.HostedServices
|
||||
},
|
||||
notes = $"Rate limit abuse on {DateTime.UtcNow.ToString()}."
|
||||
});
|
||||
request.Content = new StringContent(bodyContent, Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _httpClient.SendAsync(request, cancellationToken);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
@@ -83,8 +81,7 @@ namespace Bit.Admin.HostedServices
|
||||
return;
|
||||
}
|
||||
|
||||
var responseString = await response.Content.ReadAsStringAsync();
|
||||
var accessRuleResponse = JsonConvert.DeserializeObject<AccessRuleResponse>(responseString);
|
||||
var accessRuleResponse = await response.Content.ReadFromJsonAsync<AccessRuleResponse>(cancellationToken: cancellationToken);
|
||||
if (!accessRuleResponse.Success)
|
||||
{
|
||||
return;
|
||||
@@ -118,8 +115,7 @@ namespace Bit.Admin.HostedServices
|
||||
return;
|
||||
}
|
||||
|
||||
var responseString = await response.Content.ReadAsStringAsync();
|
||||
var listResponse = JsonConvert.DeserializeObject<ListResponse>(responseString);
|
||||
var listResponse = await response.Content.ReadFromJsonAsync<ListResponse>(cancellationToken: cancellationToken);
|
||||
if (!listResponse.Success)
|
||||
{
|
||||
return;
|
||||
|
||||
3489
src/Admin/packages.lock.json
Normal file
3489
src/Admin/packages.lock.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.Messaging.EventGrid;
|
||||
using Bit.Api.Models.Request;
|
||||
@@ -21,7 +22,6 @@ using Core.Models.Data;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
{
|
||||
@@ -717,7 +717,7 @@ namespace Bit.Api.Controllers
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id), userId);
|
||||
var result = await _cipherService.GetAttachmentDownloadDataAsync(cipher, attachmentId);
|
||||
return new AttachmentResponseModel(result.Id, result.Data, result.Cipher, _globalSettings);
|
||||
return new AttachmentResponseModel(result);
|
||||
}
|
||||
|
||||
[HttpPost("{id}/attachment/{attachmentId}/share")]
|
||||
@@ -802,7 +802,7 @@ namespace Bit.Api.Controllers
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, $"Uncaught exception occurred while handling event grid event: {JsonConvert.SerializeObject(eventGridEvent)}");
|
||||
_logger.LogError(e, $"Uncaught exception occurred while handling event grid event: {JsonSerializer.Serialize(eventGridEvent)}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +148,11 @@ namespace Bit.Api.Controllers
|
||||
[HttpPut("{id}/users")]
|
||||
public async Task PutUsers(string orgId, string id, [FromBody] IEnumerable<SelectionReadOnlyRequestModel> model)
|
||||
{
|
||||
if (!await CanEditCollectionAsync(orgId, id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var collection = await GetCollectionAsync(new Guid(id), new Guid(orgId));
|
||||
await _collectionRepository.UpdateUsersAsync(collection.Id, model?.Select(g => g.ToSelectionReadOnly()));
|
||||
}
|
||||
@@ -220,7 +225,7 @@ namespace Bit.Api.Controllers
|
||||
|
||||
if (await _currentContext.EditAssignedCollections(orgId))
|
||||
{
|
||||
return null != _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
|
||||
return null != await _collectionRepository.GetByIdAsync(collectionId, _currentContext.UserId.Value);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace Bit.Api.Controllers
|
||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||
var result =
|
||||
await _emergencyAccessService.GetAttachmentDownloadAsync(new Guid(id), cipherId, attachmentId, user);
|
||||
return new AttachmentResponseModel(result.Id, result.Data, result.Cipher, _globalSettings);
|
||||
return new AttachmentResponseModel(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,14 +85,16 @@ namespace Bit.Api.Controllers
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<bool> PreValidateSponsorshipToken([FromQuery] string sponsorshipToken)
|
||||
{
|
||||
return await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email);
|
||||
return (await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email)).valid;
|
||||
}
|
||||
|
||||
[HttpPost("redeem")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task RedeemSponsorship([FromQuery] string sponsorshipToken, [FromBody] OrganizationSponsorshipRedeemRequestModel model)
|
||||
{
|
||||
if (!await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email))
|
||||
var (valid, sponsorship) = await _validateRedemptionTokenCommand.ValidateRedemptionTokenAsync(sponsorshipToken, (await CurrentUser).Email);
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
throw new BadRequestException("Failed to parse sponsorship token.");
|
||||
}
|
||||
@@ -103,8 +105,7 @@ namespace Bit.Api.Controllers
|
||||
}
|
||||
|
||||
await _setUpSponsorshipCommand.SetUpSponsorshipAsync(
|
||||
await _organizationSponsorshipRepository
|
||||
.GetByOfferedToEmailAsync((await CurrentUser).Email),
|
||||
sponsorship,
|
||||
await _organizationRepository.GetByIdAsync(model.SponsoredOrganizationId));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Api.Models.Request;
|
||||
using Bit.Api.Models.Request.Accounts;
|
||||
@@ -17,7 +18,6 @@ using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
{
|
||||
@@ -185,7 +185,7 @@ namespace Bit.Api.Controllers
|
||||
return new OrganizationAutoEnrollStatusResponseModel(organization.Id, false);
|
||||
}
|
||||
|
||||
var data = JsonConvert.DeserializeObject<ResetPasswordDataModel>(resetPasswordPolicy.Data);
|
||||
var data = JsonSerializer.Deserialize<ResetPasswordDataModel>(resetPasswordPolicy.Data);
|
||||
return new OrganizationAutoEnrollStatusResponseModel(organization.Id, data?.AutoEnrollEnabled ?? false);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.Messaging.EventGrid;
|
||||
using Bit.Api.Models.Request;
|
||||
@@ -19,7 +20,6 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Api.Controllers
|
||||
{
|
||||
@@ -227,7 +227,7 @@ namespace Bit.Api.Controllers
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var sendId = new Guid(id);
|
||||
var send = await _sendRepository.GetByIdAsync(sendId);
|
||||
var fileData = JsonConvert.DeserializeObject<SendFileData>(send?.Data);
|
||||
var fileData = JsonSerializer.Deserialize<SendFileData>(send?.Data);
|
||||
|
||||
if (send == null || send.Type != SendType.File || (send.UserId.HasValue && send.UserId.Value != userId) ||
|
||||
!send.UserId.HasValue || fileData.Id != fileId || fileData.Validated)
|
||||
@@ -289,7 +289,7 @@ namespace Bit.Api.Controllers
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, $"Uncaught exception occurred while handling event grid event: {JsonConvert.SerializeObject(eventGridEvent)}");
|
||||
_logger.LogError(e, $"Uncaught exception occurred while handling event grid event: {JsonSerializer.Serialize(eventGridEvent)}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Entities;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Api.Models.Public.Request
|
||||
{
|
||||
@@ -17,7 +17,7 @@ namespace Bit.Api.Models.Public.Request
|
||||
public virtual Policy ToPolicy(Policy existingPolicy)
|
||||
{
|
||||
existingPolicy.Enabled = Enabled.GetValueOrDefault();
|
||||
existingPolicy.Data = Data != null ? JsonConvert.SerializeObject(Data) : null;
|
||||
existingPolicy.Data = Data != null ? JsonSerializer.Serialize(Data) : null;
|
||||
return existingPolicy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Newtonsoft.Json;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Api.Models.Public.Response
|
||||
{
|
||||
@@ -24,7 +25,7 @@ namespace Bit.Api.Models.Public.Response
|
||||
Enabled = policy.Enabled;
|
||||
if (!string.IsNullOrWhiteSpace(policy.Data))
|
||||
{
|
||||
Data = JsonConvert.DeserializeObject<Dictionary<string, object>>(policy.Data);
|
||||
Data = JsonSerializer.Deserialize<Dictionary<string, object>>(policy.Data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
using Core.Models.Data;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NS = Newtonsoft.Json;
|
||||
using NSL = Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Bit.Api.Models.Request
|
||||
{
|
||||
@@ -69,22 +70,20 @@ namespace Bit.Api.Models.Request
|
||||
switch (existingCipher.Type)
|
||||
{
|
||||
case CipherType.Login:
|
||||
var loginObj = JObject.FromObject(ToCipherLoginData(),
|
||||
new JsonSerializer { NullValueHandling = NullValueHandling.Ignore });
|
||||
var loginObj = NSL.JObject.FromObject(ToCipherLoginData(),
|
||||
new NS.JsonSerializer { NullValueHandling = NS.NullValueHandling.Ignore });
|
||||
// TODO: Switch to JsonNode in .NET 6 https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter?pivots=dotnet-6-0
|
||||
loginObj[nameof(CipherLoginData.Uri)]?.Parent?.Remove();
|
||||
existingCipher.Data = loginObj.ToString(Formatting.None);
|
||||
existingCipher.Data = loginObj.ToString(NS.Formatting.None);
|
||||
break;
|
||||
case CipherType.Card:
|
||||
existingCipher.Data = JsonConvert.SerializeObject(ToCipherCardData(),
|
||||
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||
existingCipher.Data = JsonSerializer.Serialize(ToCipherCardData(), JsonHelpers.IgnoreWritingNull);
|
||||
break;
|
||||
case CipherType.Identity:
|
||||
existingCipher.Data = JsonConvert.SerializeObject(ToCipherIdentityData(),
|
||||
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||
existingCipher.Data = JsonSerializer.Serialize(ToCipherIdentityData(), JsonHelpers.IgnoreWritingNull);
|
||||
break;
|
||||
case CipherType.SecureNote:
|
||||
existingCipher.Data = JsonConvert.SerializeObject(ToCipherSecureNoteData(),
|
||||
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||
existingCipher.Data = JsonSerializer.Serialize(ToCipherSecureNoteData(), JsonHelpers.IgnoreWritingNull);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Unsupported type: " + nameof(Type) + ".");
|
||||
|
||||
@@ -9,8 +9,8 @@ using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Sso;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
using U2F.Core.Utils;
|
||||
|
||||
namespace Bit.Api.Models.Request.Organizations
|
||||
{
|
||||
@@ -147,7 +147,7 @@ namespace Bit.Api.Models.Request.Organizations
|
||||
ValidationResult failedResult = null;
|
||||
try
|
||||
{
|
||||
var certData = StripPemCertificateElements(IdpX509PublicCert).Base64StringToByteArray();
|
||||
var certData = CoreHelpers.Base64UrlDecode(StripPemCertificateElements(IdpX509PublicCert));
|
||||
new X509Certificate2(certData);
|
||||
}
|
||||
catch (FormatException)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Api.Models.Request
|
||||
{
|
||||
@@ -65,15 +65,13 @@ namespace Bit.Api.Models.Request
|
||||
switch (existingSend.Type)
|
||||
{
|
||||
case SendType.File:
|
||||
var fileData = JsonConvert.DeserializeObject<SendFileData>(existingSend.Data);
|
||||
var fileData = JsonSerializer.Deserialize<SendFileData>(existingSend.Data);
|
||||
fileData.Name = Name;
|
||||
fileData.Notes = Notes;
|
||||
existingSend.Data = JsonConvert.SerializeObject(fileData,
|
||||
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||
existingSend.Data = JsonSerializer.Serialize(fileData, JsonHelpers.IgnoreWritingNull);
|
||||
break;
|
||||
case SendType.Text:
|
||||
existingSend.Data = JsonConvert.SerializeObject(ToSendData(),
|
||||
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||
existingSend.Data = JsonSerializer.Serialize(ToSendTextData(), JsonHelpers.IgnoreWritingNull);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Unsupported type: " + nameof(Type) + ".");
|
||||
@@ -130,7 +128,7 @@ namespace Bit.Api.Models.Request
|
||||
return existingSend;
|
||||
}
|
||||
|
||||
private SendData ToSendData()
|
||||
private SendTextData ToSendTextData()
|
||||
{
|
||||
return new SendTextData(Name, Notes, Text.Text, Text.Hidden);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Api.Models.Request
|
||||
{
|
||||
@@ -12,9 +12,9 @@ namespace Bit.Api.Models.Request
|
||||
|
||||
public User ToUser(User existingUser)
|
||||
{
|
||||
existingUser.EquivalentDomains = EquivalentDomains != null ? JsonConvert.SerializeObject(EquivalentDomains) : null;
|
||||
existingUser.EquivalentDomains = EquivalentDomains != null ? JsonSerializer.Serialize(EquivalentDomains) : null;
|
||||
existingUser.ExcludedGlobalEquivalentDomains = ExcludedGlobalEquivalentDomains != null ?
|
||||
JsonConvert.SerializeObject(ExcludedGlobalEquivalentDomains) : null;
|
||||
JsonSerializer.Serialize(ExcludedGlobalEquivalentDomains) : null;
|
||||
return existingUser;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Models.Data;
|
||||
@@ -10,6 +11,16 @@ namespace Bit.Api.Models.Response
|
||||
{
|
||||
public class AttachmentResponseModel : ResponseModel
|
||||
{
|
||||
public AttachmentResponseModel(AttachmentResponseData data) : base("attachment")
|
||||
{
|
||||
Id = data.Id;
|
||||
Url = data.Url;
|
||||
FileName = data.Data.FileName;
|
||||
Key = data.Data.Key;
|
||||
Size = data.Data.Size;
|
||||
SizeName = CoreHelpers.ReadableBytesSize(data.Data.Size);
|
||||
}
|
||||
|
||||
public AttachmentResponseModel(string id, CipherAttachment.MetaData data, Cipher cipher,
|
||||
IGlobalSettings globalSettings)
|
||||
: base("attachment")
|
||||
@@ -18,7 +29,7 @@ namespace Bit.Api.Models.Response
|
||||
Url = $"{globalSettings.Attachment.BaseUrl}/{cipher.Id}/{id}";
|
||||
FileName = data.FileName;
|
||||
Key = data.Key;
|
||||
Size = data.SizeString;
|
||||
Size = data.Size;
|
||||
SizeName = CoreHelpers.ReadableBytesSize(data.Size);
|
||||
}
|
||||
|
||||
@@ -26,7 +37,8 @@ namespace Bit.Api.Models.Response
|
||||
public string Url { get; set; }
|
||||
public string FileName { get; set; }
|
||||
public string Key { get; set; }
|
||||
public string Size { get; set; }
|
||||
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
|
||||
public long Size { get; set; }
|
||||
public string SizeName { get; set; }
|
||||
|
||||
public static IEnumerable<AttachmentResponseModel> FromCipher(Cipher cipher, IGlobalSettings globalSettings)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Settings;
|
||||
using Core.Models.Data;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Api.Models.Response
|
||||
{
|
||||
@@ -28,25 +28,25 @@ namespace Bit.Api.Models.Response
|
||||
switch (cipher.Type)
|
||||
{
|
||||
case CipherType.Login:
|
||||
var loginData = JsonConvert.DeserializeObject<CipherLoginData>(cipher.Data);
|
||||
var loginData = JsonSerializer.Deserialize<CipherLoginData>(cipher.Data);
|
||||
cipherData = loginData;
|
||||
Data = loginData;
|
||||
Login = new CipherLoginModel(loginData);
|
||||
break;
|
||||
case CipherType.SecureNote:
|
||||
var secureNoteData = JsonConvert.DeserializeObject<CipherSecureNoteData>(cipher.Data);
|
||||
var secureNoteData = JsonSerializer.Deserialize<CipherSecureNoteData>(cipher.Data);
|
||||
Data = secureNoteData;
|
||||
cipherData = secureNoteData;
|
||||
SecureNote = new CipherSecureNoteModel(secureNoteData);
|
||||
break;
|
||||
case CipherType.Card:
|
||||
var cardData = JsonConvert.DeserializeObject<CipherCardData>(cipher.Data);
|
||||
var cardData = JsonSerializer.Deserialize<CipherCardData>(cipher.Data);
|
||||
Data = cardData;
|
||||
cipherData = cardData;
|
||||
Card = new CipherCardModel(cardData);
|
||||
break;
|
||||
case CipherType.Identity:
|
||||
var identityData = JsonConvert.DeserializeObject<CipherIdentityData>(cipher.Data);
|
||||
var identityData = JsonSerializer.Deserialize<CipherIdentityData>(cipher.Data);
|
||||
Data = identityData;
|
||||
cipherData = identityData;
|
||||
Identity = new CipherIdentityModel(identityData);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Api;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Api.Models.Response
|
||||
{
|
||||
@@ -19,10 +19,10 @@ namespace Bit.Api.Models.Response
|
||||
}
|
||||
|
||||
EquivalentDomains = user.EquivalentDomains != null ?
|
||||
JsonConvert.DeserializeObject<List<List<string>>>(user.EquivalentDomains) : null;
|
||||
JsonSerializer.Deserialize<List<List<string>>>(user.EquivalentDomains) : null;
|
||||
|
||||
var excludedGlobalEquivalentDomains = user.ExcludedGlobalEquivalentDomains != null ?
|
||||
JsonConvert.DeserializeObject<List<GlobalEquivalentDomainsType>>(user.ExcludedGlobalEquivalentDomains) :
|
||||
JsonSerializer.Deserialize<List<GlobalEquivalentDomainsType>>(user.ExcludedGlobalEquivalentDomains) :
|
||||
new List<GlobalEquivalentDomainsType>();
|
||||
var globalDomains = new List<GlobalDomains>();
|
||||
var domainsToInclude = excluded ? Core.Utilities.StaticStore.GlobalDomains :
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Api;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Api.Models.Response
|
||||
{
|
||||
@@ -23,7 +23,7 @@ namespace Bit.Api.Models.Response
|
||||
Enabled = policy.Enabled;
|
||||
if (!string.IsNullOrWhiteSpace(policy.Data))
|
||||
{
|
||||
Data = JsonConvert.DeserializeObject<Dictionary<string, object>>(policy.Data);
|
||||
Data = JsonSerializer.Deserialize<Dictionary<string, object>>(policy.Data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Api.Models.Response
|
||||
{
|
||||
@@ -26,12 +26,12 @@ namespace Bit.Api.Models.Response
|
||||
switch (send.Type)
|
||||
{
|
||||
case SendType.File:
|
||||
var fileData = JsonConvert.DeserializeObject<SendFileData>(send.Data);
|
||||
var fileData = JsonSerializer.Deserialize<SendFileData>(send.Data);
|
||||
sendData = fileData;
|
||||
File = new SendFileModel(fileData);
|
||||
break;
|
||||
case SendType.Text:
|
||||
var textData = JsonConvert.DeserializeObject<SendTextData>(send.Data);
|
||||
var textData = JsonSerializer.Deserialize<SendTextData>(send.Data);
|
||||
sendData = textData;
|
||||
Text = new SendTextModel(textData);
|
||||
break;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Api.Models.Response
|
||||
{
|
||||
@@ -36,12 +36,12 @@ namespace Bit.Api.Models.Response
|
||||
switch (send.Type)
|
||||
{
|
||||
case SendType.File:
|
||||
var fileData = JsonConvert.DeserializeObject<SendFileData>(send.Data);
|
||||
var fileData = JsonSerializer.Deserialize<SendFileData>(send.Data);
|
||||
sendData = fileData;
|
||||
File = new SendFileModel(fileData);
|
||||
break;
|
||||
case SendType.Text:
|
||||
var textData = JsonConvert.DeserializeObject<SendTextData>(send.Data);
|
||||
var textData = JsonSerializer.Deserialize<SendTextData>(send.Data);
|
||||
sendData = textData;
|
||||
Text = new SendTextModel(textData);
|
||||
break;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Bit.Core.Models.Data;
|
||||
using System.Text.Json.Serialization;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Api.Models
|
||||
@@ -11,7 +12,7 @@ namespace Bit.Api.Models
|
||||
{
|
||||
Id = data.Id;
|
||||
FileName = data.FileName;
|
||||
Size = data.SizeString;
|
||||
Size = data.Size;
|
||||
SizeName = CoreHelpers.ReadableBytesSize(data.Size);
|
||||
}
|
||||
|
||||
@@ -19,7 +20,8 @@ namespace Bit.Api.Models
|
||||
[EncryptedString]
|
||||
[EncryptedStringLength(1000)]
|
||||
public string FileName { get; set; }
|
||||
public string Size { get; set; }
|
||||
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
|
||||
public long? Size { get; set; }
|
||||
public string SizeName { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace Bit.Api
|
||||
services.AddCoreLocalizationServices();
|
||||
|
||||
#if OSS
|
||||
services.AddOosServices();
|
||||
services.AddOosServices();
|
||||
#else
|
||||
services.AddCommCoreServices();
|
||||
#endif
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.Messaging.EventGrid;
|
||||
using Azure.Messaging.EventGrid.SystemEvents;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Api.Utilities
|
||||
{
|
||||
@@ -21,15 +21,8 @@ namespace Bit.Api.Utilities
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var stream = file.OpenReadStream())
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
var s = await reader.ReadToEndAsync();
|
||||
if (!string.IsNullOrWhiteSpace(s))
|
||||
{
|
||||
obj = JsonConvert.DeserializeObject<T>(s);
|
||||
}
|
||||
}
|
||||
using var stream = file.OpenReadStream();
|
||||
obj = await JsonSerializer.DeserializeAsync<T>(stream, JsonHelpers.IgnoreCase);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using InternalApi = Bit.Core.Models.Api;
|
||||
|
||||
namespace Bit.Api.Utilities
|
||||
{
|
||||
public class ModelStateValidationFilterAttribute : ActionFilterAttribute
|
||||
public class ModelStateValidationFilterAttribute : SharedWeb.Utilities.ModelStateValidationFilterAttribute
|
||||
{
|
||||
private readonly bool _publicApi;
|
||||
|
||||
@@ -15,24 +15,15 @@ namespace Bit.Api.Utilities
|
||||
_publicApi = publicApi;
|
||||
}
|
||||
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
protected override void OnModelStateInvalid(ActionExecutingContext context)
|
||||
{
|
||||
var model = context.ActionArguments.FirstOrDefault(a => a.Key == "model");
|
||||
if (model.Key == "model" && model.Value == null)
|
||||
if (_publicApi)
|
||||
{
|
||||
context.ModelState.AddModelError(string.Empty, "Body is empty.");
|
||||
context.Result = new BadRequestObjectResult(new ErrorResponseModel(context.ModelState));
|
||||
}
|
||||
|
||||
if (!context.ModelState.IsValid)
|
||||
else
|
||||
{
|
||||
if (_publicApi)
|
||||
{
|
||||
context.Result = new BadRequestObjectResult(new ErrorResponseModel(context.ModelState));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Result = new BadRequestObjectResult(new InternalApi.ErrorResponseModel(context.ModelState));
|
||||
}
|
||||
context.Result = new BadRequestObjectResult(new InternalApi.ErrorResponseModel(context.ModelState));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Api.Models.Request;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@@ -7,7 +8,6 @@ using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Api.Utilities
|
||||
{
|
||||
@@ -78,13 +78,6 @@ namespace Bit.Api.Utilities
|
||||
{
|
||||
if (ContentDispositionHeaderValue.TryParse(firstSection.ContentDisposition, out _))
|
||||
{
|
||||
// Request model json, then data
|
||||
string requestModelJson = null;
|
||||
using (var sr = new StreamReader(firstSection.Body))
|
||||
{
|
||||
requestModelJson = await sr.ReadToEndAsync();
|
||||
}
|
||||
|
||||
var secondSection = await reader.ReadNextSectionAsync();
|
||||
if (secondSection != null)
|
||||
{
|
||||
@@ -94,7 +87,7 @@ namespace Bit.Api.Utilities
|
||||
var fileName = HeaderUtilities.RemoveQuotes(secondContent.FileName).ToString();
|
||||
using (secondSection.Body)
|
||||
{
|
||||
var model = JsonConvert.DeserializeObject<SendRequestModel>(requestModelJson);
|
||||
var model = await JsonSerializer.DeserializeAsync<SendRequestModel>(firstSection.Body);
|
||||
await callback(secondSection.Body, fileName, model);
|
||||
}
|
||||
}
|
||||
|
||||
3420
src/Api/packages.lock.json
Normal file
3420
src/Api/packages.lock.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Billing.Controllers
|
||||
{
|
||||
@@ -53,7 +53,7 @@ namespace Bit.Billing.Controllers
|
||||
|
||||
try
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(JsonConvert.DeserializeObject(body), Formatting.Indented);
|
||||
var json = JsonSerializer.Serialize(JsonSerializer.Deserialize<JsonDocument>(body), JsonHelpers.Indented);
|
||||
_logger.LogInformation(Constants.BypassFiltersEventId, "Apple IAP Notification:\n\n{0}", json);
|
||||
return new OkResult();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
@@ -13,7 +14,6 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Billing.Controllers
|
||||
{
|
||||
@@ -62,23 +62,18 @@ namespace Bit.Billing.Controllers
|
||||
return new BadRequestResult();
|
||||
}
|
||||
|
||||
string body = null;
|
||||
using (var reader = new StreamReader(HttpContext.Request.Body, Encoding.UTF8))
|
||||
{
|
||||
body = await reader.ReadToEndAsync();
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(body))
|
||||
using var body = await JsonSerializer.DeserializeAsync<JsonDocument>(HttpContext.Request.Body);
|
||||
var root = body.RootElement;
|
||||
if (root.ValueKind != JsonValueKind.Object)
|
||||
{
|
||||
return new BadRequestResult();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
dynamic data = JsonConvert.DeserializeObject(body);
|
||||
string ticketId = data.ticket_id;
|
||||
string ticketContactEmail = data.ticket_contact_email;
|
||||
string ticketTags = data.ticket_tags;
|
||||
var ticketId = root.GetProperty("ticket_id").GetString();
|
||||
var ticketContactEmail = root.GetProperty("ticket_contact_email").GetString();
|
||||
var ticketTags = root.GetProperty("ticket_tags").GetString();
|
||||
if (string.IsNullOrWhiteSpace(ticketId) || string.IsNullOrWhiteSpace(ticketContactEmail))
|
||||
{
|
||||
return new BadRequestResult();
|
||||
@@ -120,9 +115,11 @@ namespace Bit.Billing.Controllers
|
||||
updateBody.Add("tags", tagsToUpdate);
|
||||
}
|
||||
var updateRequest = new HttpRequestMessage(HttpMethod.Put,
|
||||
string.Format("https://bitwarden.freshdesk.com/api/v2/tickets/{0}", ticketId));
|
||||
updateRequest.Content = new StringContent(JsonConvert.SerializeObject(updateBody),
|
||||
Encoding.UTF8, "application/json");
|
||||
string.Format("https://bitwarden.freshdesk.com/api/v2/tickets/{0}", ticketId))
|
||||
{
|
||||
Content = JsonContent.Create(updateBody),
|
||||
};
|
||||
|
||||
await CallFreshdeskApiAsync(updateRequest);
|
||||
|
||||
|
||||
@@ -132,9 +129,10 @@ namespace Bit.Billing.Controllers
|
||||
{ "private", true }
|
||||
};
|
||||
var noteRequest = new HttpRequestMessage(HttpMethod.Post,
|
||||
string.Format("https://bitwarden.freshdesk.com/api/v2/tickets/{0}/notes", ticketId));
|
||||
noteRequest.Content = new StringContent(JsonConvert.SerializeObject(noteBody),
|
||||
Encoding.UTF8, "application/json");
|
||||
string.Format("https://bitwarden.freshdesk.com/api/v2/tickets/{0}/notes", ticketId))
|
||||
{
|
||||
Content = JsonContent.Create(noteBody),
|
||||
};
|
||||
await CallFreshdeskApiAsync(noteRequest);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
using System;
|
||||
using Bit.Core.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace Bit.Billing.Models
|
||||
{
|
||||
public class AppleReceiptNotification
|
||||
{
|
||||
[JsonProperty("notification_type")]
|
||||
public string NotificationType { get; set; }
|
||||
[JsonProperty("environment")]
|
||||
public string Environment { get; set; }
|
||||
[JsonProperty("auto_renew_status")]
|
||||
public string AutoRenewStatus { get; set; }
|
||||
[JsonProperty("auto_renew_product_id")]
|
||||
public string AutoRenewProductId { get; set; }
|
||||
[JsonProperty("auto_renew_status_change_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime? AutoRenewStatusChangeDate { get; set; }
|
||||
[JsonProperty("latest_receipt")]
|
||||
public string LatestReceipt { get; set; }
|
||||
[JsonProperty("latest_receipt_info")]
|
||||
public AppleReceiptNotificationInfo LatestReceiptInfo { get; set; }
|
||||
[JsonProperty("latest_expired_receipt")]
|
||||
public string LatestExpiredReceipt { get; set; }
|
||||
[JsonProperty("latest_expired_receipt_info")]
|
||||
public AppleReceiptNotificationInfo LatestExpiredReceiptInfo { get; set; }
|
||||
|
||||
public string GetOriginalTransactionId()
|
||||
{
|
||||
if (LatestReceiptInfo != null)
|
||||
{
|
||||
return LatestReceiptInfo.OriginalTransactionId;
|
||||
}
|
||||
return LatestExpiredReceiptInfo?.OriginalTransactionId;
|
||||
}
|
||||
|
||||
public string GetTransactionId()
|
||||
{
|
||||
if (LatestReceiptInfo != null)
|
||||
{
|
||||
return LatestReceiptInfo.TransactionId;
|
||||
}
|
||||
return LatestExpiredReceiptInfo?.TransactionId;
|
||||
}
|
||||
|
||||
public DateTime? GetExpiresDate()
|
||||
{
|
||||
if (LatestReceiptInfo != null)
|
||||
{
|
||||
return LatestReceiptInfo.ExpiresDate;
|
||||
}
|
||||
return LatestExpiredReceiptInfo?.ExpiresDate;
|
||||
}
|
||||
|
||||
public string GetReceiptData()
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(LatestReceipt) ? LatestExpiredReceipt : LatestReceipt;
|
||||
}
|
||||
|
||||
public class AppleReceiptNotificationInfo
|
||||
{
|
||||
[JsonProperty("bid")]
|
||||
public string Bid { get; set; }
|
||||
public string ProductId { get; set; }
|
||||
[JsonProperty("original_purchase_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime? OriginalPurchaseDate { get; set; }
|
||||
[JsonProperty("expires_date")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime? ExpiresDate { get; set; }
|
||||
[JsonProperty("purchase_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime? PurchaseDate { get; set; }
|
||||
[JsonProperty("subscription_group_identifier")]
|
||||
public string SubscriptionGroupIdentifier { get; set; }
|
||||
[JsonProperty("unique_identifier")]
|
||||
public string UniqueIdentifier { get; set; }
|
||||
[JsonProperty("original_transaction_id")]
|
||||
public string OriginalTransactionId { get; set; }
|
||||
[JsonProperty("transaction_id")]
|
||||
public string TransactionId { get; set; }
|
||||
[JsonProperty("quantity")]
|
||||
public string Quantity { get; set; }
|
||||
[JsonProperty("web_order_line_item_id")]
|
||||
public string WebOrderLineItemId { get; set; }
|
||||
[JsonProperty("item_id")]
|
||||
public string ItemId { get; set; }
|
||||
}
|
||||
|
||||
public class MsEpochConverter : DateTimeConverterBase
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
writer.WriteRawValue(CoreHelpers.ToEpocMilliseconds((DateTime)value).ToString());
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
return CoreHelpers.FromEpocMilliseconds(long.Parse(reader.Value.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3444
src/Billing/packages.lock.json
Normal file
3444
src/Billing/packages.lock.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,7 @@
|
||||
<PackageReference Include="Azure.Storage.Blobs" Version="12.10.0" />
|
||||
<PackageReference Include="Azure.Storage.Queues" Version="12.3.2" />
|
||||
<PackageReference Include="BitPay.Light" Version="1.0.1907" />
|
||||
<PackageReference Include="Fido2.AspNet" Version="1.1.0" />
|
||||
<PackageReference Include="Fido2.AspNet" Version="3.0.0-beta2" />
|
||||
<PackageReference Include="Handlebars.Net" Version="1.10.1" />
|
||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
|
||||
<PackageReference Include="MailKit" Version="2.8.0" />
|
||||
@@ -33,7 +33,6 @@
|
||||
<PackageReference Include="Microsoft.Azure.Cosmos.Table" Version="1.0.7" />
|
||||
<PackageReference Include="Microsoft.Azure.NotificationHubs" Version="3.3.0" />
|
||||
<PackageReference Include="Microsoft.Azure.ServiceBus" Version="5.1.3" />
|
||||
<PackageReference Include="Microsoft.Azure.Storage.Blob" Version="11.1.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="5.0.9" />
|
||||
@@ -50,7 +49,6 @@
|
||||
<PackageReference Include="AspNetCoreRateLimit" Version="2.1.0" />
|
||||
<PackageReference Include="Braintree" Version="4.18.0" />
|
||||
<PackageReference Include="Stripe.net" Version="37.26.0" />
|
||||
<PackageReference Include="U2F.Core" Version="1.0.4" />
|
||||
<PackageReference Include="Otp.NET" Version="1.2.2" />
|
||||
<PackageReference Include="YubicoDotNetClient" Version="1.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Redis" Version="2.2.0" />
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Entities
|
||||
{
|
||||
@@ -42,7 +42,7 @@ namespace Bit.Core.Entities
|
||||
|
||||
try
|
||||
{
|
||||
_attachmentData = JsonConvert.DeserializeObject<Dictionary<string, CipherAttachment.MetaData>>(Attachments);
|
||||
_attachmentData = JsonSerializer.Deserialize<Dictionary<string, CipherAttachment.MetaData>>(Attachments);
|
||||
foreach (var kvp in _attachmentData)
|
||||
{
|
||||
kvp.Value.AttachmentId = kvp.Key;
|
||||
@@ -65,7 +65,7 @@ namespace Bit.Core.Entities
|
||||
}
|
||||
|
||||
_attachmentData = data;
|
||||
Attachments = JsonConvert.SerializeObject(_attachmentData);
|
||||
Attachments = JsonSerializer.Serialize(_attachmentData);
|
||||
}
|
||||
|
||||
public void AddAttachment(string id, CipherAttachment.MetaData data)
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Entities
|
||||
{
|
||||
@@ -142,13 +142,13 @@ namespace Bit.Core.Entities
|
||||
if (_twoFactorProviders == null)
|
||||
{
|
||||
_twoFactorProviders =
|
||||
JsonConvert.DeserializeObject<Dictionary<TwoFactorProviderType, TwoFactorProvider>>(
|
||||
JsonHelpers.LegacyDeserialize<Dictionary<TwoFactorProviderType, TwoFactorProvider>>(
|
||||
TwoFactorProviders);
|
||||
}
|
||||
|
||||
return _twoFactorProviders;
|
||||
}
|
||||
catch (JsonSerializationException)
|
||||
catch (JsonException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -163,10 +163,7 @@ namespace Bit.Core.Entities
|
||||
return;
|
||||
}
|
||||
|
||||
TwoFactorProviders = JsonConvert.SerializeObject(providers, new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new EnumKeyResolver<byte>()
|
||||
});
|
||||
TwoFactorProviders = JsonHelpers.LegacySerialize(providers, JsonHelpers.LegacyEnumKeyResolver);
|
||||
_twoFactorProviders = providers;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Core.Entities
|
||||
{
|
||||
public class U2f : ITableObject<int>
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public Guid UserId { get; set; }
|
||||
[MaxLength(200)]
|
||||
public string KeyHandle { get; set; }
|
||||
[MaxLength(200)]
|
||||
public string Challenge { get; set; }
|
||||
[MaxLength(50)]
|
||||
public string AppId { get; set; }
|
||||
[MaxLength(20)]
|
||||
public string Version { get; set; }
|
||||
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
||||
|
||||
public void SetNewId()
|
||||
{
|
||||
// int will be auto-populated
|
||||
Id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Entities
|
||||
{
|
||||
@@ -108,13 +108,13 @@ namespace Bit.Core.Entities
|
||||
if (_twoFactorProviders == null)
|
||||
{
|
||||
_twoFactorProviders =
|
||||
JsonConvert.DeserializeObject<Dictionary<TwoFactorProviderType, TwoFactorProvider>>(
|
||||
JsonHelpers.LegacyDeserialize<Dictionary<TwoFactorProviderType, TwoFactorProvider>>(
|
||||
TwoFactorProviders);
|
||||
}
|
||||
|
||||
return _twoFactorProviders;
|
||||
}
|
||||
catch (JsonSerializationException)
|
||||
catch (JsonException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -132,10 +132,8 @@ namespace Bit.Core.Entities
|
||||
|
||||
public void SetTwoFactorProviders(Dictionary<TwoFactorProviderType, TwoFactorProvider> providers)
|
||||
{
|
||||
TwoFactorProviders = JsonConvert.SerializeObject(providers, new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new EnumKeyResolver<byte>()
|
||||
});
|
||||
// When replacing with system.text remember to remove the extra serialization in WebAuthnTokenProvider.
|
||||
TwoFactorProviders = JsonHelpers.LegacySerialize(providers, JsonHelpers.LegacyEnumKeyResolver);
|
||||
_twoFactorProviders = providers;
|
||||
}
|
||||
|
||||
|
||||
8
src/Core/Enums/LicenseType.cs
Normal file
8
src/Core/Enums/LicenseType.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Bit.Core.Enums
|
||||
{
|
||||
public enum LicenseType : byte
|
||||
{
|
||||
User = 0,
|
||||
Organization = 1,
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
Email = 1,
|
||||
Duo = 2,
|
||||
YubiKey = 3,
|
||||
U2f = 4,
|
||||
U2f = 4, // Deprecated
|
||||
Remember = 5,
|
||||
OrganizationDuo = 6,
|
||||
WebAuthn = 7,
|
||||
|
||||
@@ -44,8 +44,8 @@ namespace Bit.Core.Identity
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
|
||||
var signatureRequest = DuoWeb.SignRequest((string)provider.MetaData["IKey"],
|
||||
(string)provider.MetaData["SKey"], _globalSettings.Duo.AKey, user.Email);
|
||||
var signatureRequest = DuoWeb.SignRequest(provider.MetaData["IKey"].ToString(),
|
||||
provider.MetaData["SKey"].ToString(), _globalSettings.Duo.AKey, user.Email);
|
||||
return Task.FromResult(signatureRequest);
|
||||
}
|
||||
|
||||
@@ -62,8 +62,8 @@ namespace Bit.Core.Identity
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
var response = DuoWeb.VerifyResponse((string)provider.MetaData["IKey"],
|
||||
(string)provider.MetaData["SKey"], _globalSettings.Duo.AKey, token);
|
||||
var response = DuoWeb.VerifyResponse(provider.MetaData["IKey"].ToString(),
|
||||
provider.MetaData["SKey"].ToString(), _globalSettings.Duo.AKey, token);
|
||||
|
||||
return Task.FromResult(response == user.Email);
|
||||
}
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using U2F.Core.Exceptions;
|
||||
using U2F.Core.Models;
|
||||
using U2F.Core.Utils;
|
||||
using U2fLib = U2F.Core.Crypto.U2F;
|
||||
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
public class U2fTokenProvider : IUserTwoFactorTokenProvider<User>
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IU2fRepository _u2fRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public U2fTokenProvider(
|
||||
IServiceProvider serviceProvider,
|
||||
IU2fRepository u2fRepository,
|
||||
GlobalSettings globalSettings)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_u2fRepository = u2fRepository;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
public async Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||
if (!HasProperMetaData(provider))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return await userService.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType.U2f, user);
|
||||
}
|
||||
|
||||
public async Task<string> GenerateAsync(string purpose, UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||
var keys = LoadKeys(provider);
|
||||
if (keys.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
|
||||
|
||||
try
|
||||
{
|
||||
var challengeBytes = U2fLib.Crypto.GenerateChallenge();
|
||||
var appId = Utilities.CoreHelpers.U2fAppIdUrl(_globalSettings);
|
||||
var oldChallenges = new List<object>();
|
||||
var challengeKeys = new List<object>();
|
||||
foreach (var key in keys)
|
||||
{
|
||||
var registration = new DeviceRegistration(key.Item2.KeyHandleBytes, key.Item2.PublicKeyBytes,
|
||||
key.Item2.CertificateBytes, key.Item2.Counter);
|
||||
var auth = U2fLib.StartAuthentication(appId, registration, challengeBytes);
|
||||
|
||||
// TODO: Maybe move this to a bulk create?
|
||||
await _u2fRepository.CreateAsync(new U2f
|
||||
{
|
||||
AppId = auth.AppId,
|
||||
Challenge = auth.Challenge,
|
||||
KeyHandle = auth.KeyHandle,
|
||||
Version = auth.Version,
|
||||
UserId = user.Id,
|
||||
CreationDate = DateTime.UtcNow
|
||||
});
|
||||
|
||||
challengeKeys.Add(new
|
||||
{
|
||||
keyHandle = auth.KeyHandle,
|
||||
version = auth.Version
|
||||
});
|
||||
|
||||
// TODO: Old challenges array is here for backwards compat. Remove in the future.
|
||||
oldChallenges.Add(new
|
||||
{
|
||||
appId = auth.AppId,
|
||||
challenge = auth.Challenge,
|
||||
keyHandle = auth.KeyHandle,
|
||||
version = auth.Version
|
||||
});
|
||||
}
|
||||
|
||||
var oldToken = JsonConvert.SerializeObject(oldChallenges);
|
||||
var token = JsonConvert.SerializeObject(new
|
||||
{
|
||||
appId = appId,
|
||||
challenge = challengeBytes.ByteArrayToBase64String(),
|
||||
keys = challengeKeys
|
||||
});
|
||||
return $"{token}|{oldToken}";
|
||||
}
|
||||
catch (U2fException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<User> manager, User user)
|
||||
{
|
||||
var userService = _serviceProvider.GetRequiredService<IUserService>();
|
||||
if (!(await userService.CanAccessPremium(user)) || string.IsNullOrWhiteSpace(token))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||
var keys = LoadKeys(provider);
|
||||
if (keys.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var authenticateResponse = BaseModel.FromJson<AuthenticateResponse>(token);
|
||||
var key = keys.FirstOrDefault(f => f.Item2.KeyHandle == authenticateResponse.KeyHandle);
|
||||
|
||||
if (key == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var challenges = await _u2fRepository.GetManyByUserIdAsync(user.Id);
|
||||
if (challenges.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// User will have a authentication request for each device they have registered so get the one that matches
|
||||
// the device key handle
|
||||
var challenge = challenges.FirstOrDefault(c => c.KeyHandle == authenticateResponse.KeyHandle);
|
||||
if (challenge == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var success = true;
|
||||
var registration = new DeviceRegistration(key.Item2.KeyHandleBytes, key.Item2.PublicKeyBytes,
|
||||
key.Item2.CertificateBytes, key.Item2.Counter);
|
||||
try
|
||||
{
|
||||
var auth = new StartedAuthentication(challenge.Challenge, challenge.AppId, challenge.KeyHandle);
|
||||
U2fLib.FinishAuthentication(auth, authenticateResponse, registration);
|
||||
}
|
||||
catch (U2fException)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
|
||||
// Update database
|
||||
await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
|
||||
key.Item2.Counter = registration.Counter;
|
||||
if (key.Item2.Counter > 0)
|
||||
{
|
||||
key.Item2.Compromised = registration.IsCompromised;
|
||||
}
|
||||
|
||||
var providers = user.GetTwoFactorProviders();
|
||||
providers[TwoFactorProviderType.U2f].MetaData[key.Item1] = key.Item2;
|
||||
user.SetTwoFactorProviders(providers);
|
||||
await manager.UpdateAsync(user);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool HasProperMetaData(TwoFactorProvider provider)
|
||||
{
|
||||
return (provider?.MetaData?.Count ?? 0) > 0;
|
||||
}
|
||||
|
||||
private List<Tuple<string, TwoFactorProvider.U2fMetaData>> LoadKeys(TwoFactorProvider provider)
|
||||
{
|
||||
var keys = new List<Tuple<string, TwoFactorProvider.U2fMetaData>>();
|
||||
if (!HasProperMetaData(provider))
|
||||
{
|
||||
return keys;
|
||||
}
|
||||
|
||||
// Support up to 5 keys
|
||||
for (var i = 1; i <= 5; i++)
|
||||
{
|
||||
var keyName = $"Key{i}";
|
||||
if (provider.MetaData.ContainsKey(keyName))
|
||||
{
|
||||
var key = new TwoFactorProvider.U2fMetaData((dynamic)provider.MetaData[keyName]);
|
||||
if (!key?.Compromised ?? false)
|
||||
{
|
||||
keys.Add(new Tuple<string, TwoFactorProvider.U2fMetaData>(keyName, key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
@@ -12,7 +13,6 @@ using Fido2NetLib;
|
||||
using Fido2NetLib.Objects;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Identity
|
||||
{
|
||||
@@ -65,14 +65,14 @@ namespace Bit.Core.Identity
|
||||
|
||||
var exts = new AuthenticationExtensionsClientInputs()
|
||||
{
|
||||
UserVerificationIndex = true,
|
||||
UserVerificationMethod = true,
|
||||
AppID = CoreHelpers.U2fAppIdUrl(_globalSettings),
|
||||
};
|
||||
|
||||
var options = _fido2.GetAssertionOptions(existingCredentials, UserVerificationRequirement.Discouraged, exts);
|
||||
|
||||
provider.MetaData["login"] = options;
|
||||
// TODO: Remove this when newtonsoft legacy converters are gone
|
||||
provider.MetaData["login"] = JsonSerializer.Serialize(options);
|
||||
|
||||
var providers = user.GetTwoFactorProviders();
|
||||
providers[TwoFactorProviderType.WebAuthn] = provider;
|
||||
@@ -98,7 +98,8 @@ namespace Bit.Core.Identity
|
||||
return false;
|
||||
}
|
||||
|
||||
var clientResponse = JsonConvert.DeserializeObject<AuthenticatorAssertionRawResponse>(token);
|
||||
var clientResponse = JsonSerializer.Deserialize<AuthenticatorAssertionRawResponse>(token,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
|
||||
var jsonOptions = provider.MetaData["login"].ToString();
|
||||
var options = AssertionOptions.FromJson(jsonOptions);
|
||||
|
||||
@@ -375,7 +375,6 @@ namespace Bit.Core.IdentityServer
|
||||
case TwoFactorProviderType.Email:
|
||||
case TwoFactorProviderType.Duo:
|
||||
case TwoFactorProviderType.YubiKey:
|
||||
case TwoFactorProviderType.U2f:
|
||||
case TwoFactorProviderType.WebAuthn:
|
||||
case TwoFactorProviderType.Remember:
|
||||
if (type != TwoFactorProviderType.Remember &&
|
||||
@@ -403,7 +402,6 @@ namespace Bit.Core.IdentityServer
|
||||
switch (type)
|
||||
{
|
||||
case TwoFactorProviderType.Duo:
|
||||
case TwoFactorProviderType.U2f:
|
||||
case TwoFactorProviderType.WebAuthn:
|
||||
case TwoFactorProviderType.Email:
|
||||
case TwoFactorProviderType.YubiKey:
|
||||
@@ -422,16 +420,6 @@ namespace Bit.Core.IdentityServer
|
||||
["Signature"] = token
|
||||
};
|
||||
}
|
||||
else if (type == TwoFactorProviderType.U2f)
|
||||
{
|
||||
// TODO: Remove "Challenges" in a future update. Deprecated.
|
||||
var tokens = token?.Split('|');
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
["Challenge"] = tokens != null && tokens.Length > 0 ? tokens[0] : null,
|
||||
["Challenges"] = tokens != null && tokens.Length > 1 ? tokens[1] : null
|
||||
};
|
||||
}
|
||||
else if (type == TwoFactorProviderType.WebAuthn)
|
||||
{
|
||||
if (token == null)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<tr style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||
<td class="content-block last" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; margin: 0; -webkit-font-smoothing: antialiased; padding: 0; -webkit-text-size-adjust: none; text-align: center;" valign="top" align="center">
|
||||
If you do not wish to join this organization, you can safely ignore this email.
|
||||
{{#if OrganizationCanSponsor}}
|
||||
{{#jsonIf OrganizationCanSponsor}}
|
||||
<p style="margin-top:10px">
|
||||
<b
|
||||
style="margin: 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; color: #333; line-height: 25px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none;">
|
||||
@@ -24,7 +24,7 @@
|
||||
Members of {{OrganizationName}} receive a complimentary Families subscription. Learn more at the
|
||||
following link: https://bitwarden.com/help/article/families-for-enterprise/
|
||||
</p>
|
||||
{{/if}}
|
||||
{{/jsonIf}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -6,7 +6,7 @@ You have been invited to join the {{OrganizationName}} organization. To accept t
|
||||
This link expires on {{ExpirationDate}}.
|
||||
|
||||
If you do not wish to join this organization, you can safely ignore this email.
|
||||
{{#if OrganizationCanSponsor}}
|
||||
{{#jsonIf OrganizationCanSponsor}}
|
||||
Did you know? Members of {{OrganizationName}} receive a complimentary Families subscription. Learn more here: https://bitwarden.com/help/article/families-for-enterprise/
|
||||
{{/if}}
|
||||
{{/jsonIf}}
|
||||
{{/BasicTextLayout}}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Models.Api.Request.Accounts
|
||||
{
|
||||
@@ -43,7 +43,7 @@ namespace Bit.Core.Models.Api.Request.Accounts
|
||||
|
||||
if (ReferenceData != null)
|
||||
{
|
||||
user.ReferenceData = JsonConvert.SerializeObject(ReferenceData);
|
||||
user.ReferenceData = JsonSerializer.Serialize(ReferenceData);
|
||||
}
|
||||
|
||||
if (Key != null)
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace Bit.Billing.Models
|
||||
{
|
||||
public class AppleReceiptStatus
|
||||
{
|
||||
[JsonProperty("status")]
|
||||
[JsonPropertyName("status")]
|
||||
public int? Status { get; set; }
|
||||
[JsonProperty("environment")]
|
||||
[JsonPropertyName("environment")]
|
||||
public string Environment { get; set; }
|
||||
[JsonProperty("latest_receipt")]
|
||||
[JsonPropertyName("latest_receipt")]
|
||||
public string LatestReceipt { get; set; }
|
||||
[JsonProperty("receipt")]
|
||||
[JsonPropertyName("receipt")]
|
||||
public AppleReceipt Receipt { get; set; }
|
||||
[JsonProperty("latest_receipt_info")]
|
||||
[JsonPropertyName("latest_receipt_info")]
|
||||
public List<AppleTransaction> LatestReceiptInfo { get; set; }
|
||||
[JsonProperty("pending_renewal_info")]
|
||||
[JsonPropertyName("pending_renewal_info")]
|
||||
public List<AppleRenewalInfo> PendingRenewalInfo { get; set; }
|
||||
|
||||
public string GetOriginalTransactionId()
|
||||
@@ -81,72 +82,59 @@ namespace Bit.Billing.Models
|
||||
|
||||
public class AppleReceipt
|
||||
{
|
||||
[JsonProperty("receipt_type")]
|
||||
[JsonPropertyName("receipt_type")]
|
||||
public string ReceiptType { get; set; }
|
||||
[JsonProperty("bundle_id")]
|
||||
[JsonPropertyName("bundle_id")]
|
||||
public string BundleId { get; set; }
|
||||
[JsonProperty("receipt_creation_date_ms")]
|
||||
[JsonPropertyName("receipt_creation_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime ReceiptCreationDate { get; set; }
|
||||
[JsonProperty("in_app")]
|
||||
[JsonPropertyName("in_app")]
|
||||
public List<AppleTransaction> InApp { get; set; }
|
||||
}
|
||||
|
||||
public class AppleRenewalInfo
|
||||
{
|
||||
[JsonProperty("expiration_intent")]
|
||||
[JsonPropertyName("expiration_intent")]
|
||||
public string ExpirationIntent { get; set; }
|
||||
[JsonProperty("auto_renew_product_id")]
|
||||
[JsonPropertyName("auto_renew_product_id")]
|
||||
public string AutoRenewProductId { get; set; }
|
||||
[JsonProperty("original_transaction_id")]
|
||||
[JsonPropertyName("original_transaction_id")]
|
||||
public string OriginalTransactionId { get; set; }
|
||||
[JsonProperty("is_in_billing_retry_period")]
|
||||
[JsonPropertyName("is_in_billing_retry_period")]
|
||||
public string IsInBillingRetryPeriod { get; set; }
|
||||
[JsonProperty("product_id")]
|
||||
[JsonPropertyName("product_id")]
|
||||
public string ProductId { get; set; }
|
||||
[JsonProperty("auto_renew_status")]
|
||||
[JsonPropertyName("auto_renew_status")]
|
||||
public string AutoRenewStatus { get; set; }
|
||||
}
|
||||
|
||||
public class AppleTransaction
|
||||
{
|
||||
[JsonProperty("quantity")]
|
||||
[JsonPropertyName("quantity")]
|
||||
public string Quantity { get; set; }
|
||||
[JsonProperty("product_id")]
|
||||
[JsonPropertyName("product_id")]
|
||||
public string ProductId { get; set; }
|
||||
[JsonProperty("transaction_id")]
|
||||
[JsonPropertyName("transaction_id")]
|
||||
public string TransactionId { get; set; }
|
||||
[JsonProperty("original_transaction_id")]
|
||||
[JsonPropertyName("original_transaction_id")]
|
||||
public string OriginalTransactionId { get; set; }
|
||||
[JsonProperty("purchase_date_ms")]
|
||||
[JsonPropertyName("purchase_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime PurchaseDate { get; set; }
|
||||
[JsonProperty("original_purchase_date_ms")]
|
||||
[JsonPropertyName("original_purchase_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime OriginalPurchaseDate { get; set; }
|
||||
[JsonProperty("expires_date_ms")]
|
||||
[JsonPropertyName("expires_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime ExpiresDate { get; set; }
|
||||
[JsonProperty("cancellation_date_ms")]
|
||||
[JsonPropertyName("cancellation_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime? CancellationDate { get; set; }
|
||||
[JsonProperty("web_order_line_item_id")]
|
||||
[JsonPropertyName("web_order_line_item_id")]
|
||||
public string WebOrderLineItemId { get; set; }
|
||||
[JsonProperty("cancellation_reason")]
|
||||
[JsonPropertyName("cancellation_reason")]
|
||||
public string CancellationReason { get; set; }
|
||||
}
|
||||
|
||||
public class MsEpochConverter : DateTimeConverterBase
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
writer.WriteRawValue(CoreHelpers.ToEpocMilliseconds((DateTime)value).ToString());
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
return CoreHelpers.FromEpocMilliseconds(long.Parse(reader.Value.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
@@ -21,6 +21,7 @@ namespace Bit.Core.Models.Business
|
||||
ILicensingService licenseService, int? version = null)
|
||||
{
|
||||
Version = version.GetValueOrDefault(CURRENT_LICENSE_FILE_VERSION); // TODO: Remember to change the constant
|
||||
LicenseType = Enums.LicenseType.Organization;
|
||||
LicenseKey = org.LicenseKey;
|
||||
InstallationId = installationId;
|
||||
Id = org.Id;
|
||||
@@ -121,6 +122,7 @@ namespace Bit.Core.Models.Business
|
||||
public DateTime? Refresh { get; set; }
|
||||
public DateTime? Expires { get; set; }
|
||||
public bool Trial { get; set; }
|
||||
public LicenseType? LicenseType { get; set; }
|
||||
public string Hash { get; set; }
|
||||
public string Signature { get; set; }
|
||||
[JsonIgnore]
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
|
||||
public class ReferenceEvent
|
||||
{
|
||||
public ReferenceEvent() { }
|
||||
@@ -23,10 +20,10 @@ namespace Bit.Core.Models.Business
|
||||
}
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public ReferenceEventType Type { get; set; }
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public ReferenceEventSource Source { get; set; }
|
||||
|
||||
public Guid Id { get; set; }
|
||||
@@ -52,7 +49,7 @@ namespace Bit.Core.Models.Business
|
||||
|
||||
public short? Storage { get; set; }
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public SendType? SendType { get; set; }
|
||||
|
||||
public int? MaxAccessCount { get; set; }
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
|
||||
public class ReferenceEventData
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Layout { get; set; }
|
||||
|
||||
public string Flow { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,10 @@ using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Services;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
@@ -18,6 +19,7 @@ namespace Bit.Core.Models.Business
|
||||
public UserLicense(User user, SubscriptionInfo subscriptionInfo, ILicensingService licenseService,
|
||||
int? version = null)
|
||||
{
|
||||
LicenseType = Enums.LicenseType.User;
|
||||
LicenseKey = user.LicenseKey;
|
||||
Id = user.Id;
|
||||
Name = user.Name;
|
||||
@@ -39,6 +41,7 @@ namespace Bit.Core.Models.Business
|
||||
|
||||
public UserLicense(User user, ILicensingService licenseService, int? version = null)
|
||||
{
|
||||
LicenseType = Enums.LicenseType.User;
|
||||
LicenseKey = user.LicenseKey;
|
||||
Id = user.Id;
|
||||
Name = user.Name;
|
||||
@@ -66,6 +69,7 @@ namespace Bit.Core.Models.Business
|
||||
public DateTime? Refresh { get; set; }
|
||||
public DateTime? Expires { get; set; }
|
||||
public bool Trial { get; set; }
|
||||
public LicenseType? LicenseType { get; set; }
|
||||
public string Hash { get; set; }
|
||||
public string Signature { get; set; }
|
||||
[JsonIgnore]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Bit.Core.Models.Data
|
||||
{
|
||||
@@ -15,21 +15,14 @@ namespace Bit.Core.Models.Data
|
||||
{
|
||||
private long _size;
|
||||
|
||||
[JsonIgnore]
|
||||
// We serialize Size as a string since JSON (or Javascript) doesn't support full precision for long numbers
|
||||
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
|
||||
public long Size
|
||||
{
|
||||
get { return _size; }
|
||||
set { _size = value; }
|
||||
}
|
||||
|
||||
// We serialize Size as a string since JSON (or Javascript) doesn't support full precision for long numbers
|
||||
[JsonProperty("Size")]
|
||||
public string SizeString
|
||||
{
|
||||
get { return _size.ToString(); }
|
||||
set { _size = Convert.ToInt64(value); }
|
||||
}
|
||||
|
||||
public string FileName { get; set; }
|
||||
public string Key { get; set; }
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Models.Data
|
||||
{
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Enums;
|
||||
using Newtonsoft.Json;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Models.Data
|
||||
{
|
||||
@@ -37,13 +38,13 @@ namespace Bit.Core.Models.Data
|
||||
if (_twoFactorProviders == null)
|
||||
{
|
||||
_twoFactorProviders =
|
||||
JsonConvert.DeserializeObject<Dictionary<TwoFactorProviderType, TwoFactorProvider>>(
|
||||
JsonHelpers.LegacyDeserialize<Dictionary<TwoFactorProviderType, TwoFactorProvider>>(
|
||||
TwoFactorProviders);
|
||||
}
|
||||
|
||||
return _twoFactorProviders;
|
||||
}
|
||||
catch (JsonSerializationException)
|
||||
catch (Newtonsoft.Json.JsonException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Bit.Core.Models.Data
|
||||
{
|
||||
@@ -25,7 +25,6 @@ namespace Bit.Core.Models.Data
|
||||
public bool ManageResetPassword { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
public List<(bool Permission, string ClaimName)> ClaimsMap => new()
|
||||
{
|
||||
(AccessEventLogs, "accesseventlogs"),
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Bit.Core.Models.Data
|
||||
{
|
||||
public class SendFileData : SendData
|
||||
{
|
||||
private long _size;
|
||||
|
||||
public SendFileData() { }
|
||||
|
||||
public SendFileData(string name, string notes, string fileName)
|
||||
@@ -15,20 +13,9 @@ namespace Bit.Core.Models.Data
|
||||
FileName = fileName;
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public long Size
|
||||
{
|
||||
get { return _size; }
|
||||
set { _size = value; }
|
||||
}
|
||||
|
||||
// We serialize Size as a string since JSON (or Javascript) doesn't support full precision for long numbers
|
||||
[JsonProperty("Size")]
|
||||
public string SizeString
|
||||
{
|
||||
get { return _size.ToString(); }
|
||||
set { _size = Convert.ToInt64(value); }
|
||||
}
|
||||
[JsonNumberHandling(JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowReadingFromString)]
|
||||
public long Size { get; set; }
|
||||
|
||||
public string Id { get; set; }
|
||||
public string FileName { get; set; }
|
||||
|
||||
@@ -9,6 +9,6 @@ namespace Bit.Core.Models.Mail
|
||||
IEnumerable<string> BccEmails { get; set; }
|
||||
string Category { get; set; }
|
||||
string TemplateName { get; set; }
|
||||
dynamic Model { get; set; }
|
||||
object Model { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json.Serialization;
|
||||
using Bit.Core.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Models.Mail
|
||||
{
|
||||
@@ -11,12 +11,13 @@ namespace Bit.Core.Models.Mail
|
||||
public IEnumerable<string> BccEmails { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string TemplateName { get; set; }
|
||||
[JsonConverter(typeof(ExpandoObjectJsonConverter))]
|
||||
public dynamic Model { get; set; }
|
||||
|
||||
[JsonConverter(typeof(HandlebarsObjectJsonConverter))]
|
||||
public object Model { get; set; }
|
||||
|
||||
public MailQueueMessage() { }
|
||||
|
||||
public MailQueueMessage(MailMessage message, string templateName, dynamic model)
|
||||
public MailQueueMessage(MailMessage message, string templateName, object model)
|
||||
{
|
||||
Subject = message.Subject;
|
||||
ToEmails = message.ToEmails;
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Enums;
|
||||
using Fido2NetLib.Objects;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using PeterO.Cbor;
|
||||
using U2F.Core.Utils;
|
||||
|
||||
namespace Bit.Core.Models
|
||||
{
|
||||
@@ -15,79 +11,6 @@ namespace Bit.Core.Models
|
||||
public bool Enabled { get; set; }
|
||||
public Dictionary<string, object> MetaData { get; set; } = new Dictionary<string, object>();
|
||||
|
||||
public class U2fMetaData
|
||||
{
|
||||
public U2fMetaData() { }
|
||||
|
||||
public U2fMetaData(dynamic o)
|
||||
{
|
||||
Name = o.Name;
|
||||
KeyHandle = o.KeyHandle;
|
||||
PublicKey = o.PublicKey;
|
||||
Certificate = o.Certificate;
|
||||
Counter = o.Counter;
|
||||
Compromised = o.Compromised;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public string KeyHandle { get; set; }
|
||||
[JsonIgnore]
|
||||
public byte[] KeyHandleBytes =>
|
||||
string.IsNullOrWhiteSpace(KeyHandle) ? null : Utils.Base64StringToByteArray(KeyHandle);
|
||||
public string PublicKey { get; set; }
|
||||
[JsonIgnore]
|
||||
public byte[] PublicKeyBytes =>
|
||||
string.IsNullOrWhiteSpace(PublicKey) ? null : Utils.Base64StringToByteArray(PublicKey);
|
||||
public string Certificate { get; set; }
|
||||
[JsonIgnore]
|
||||
public byte[] CertificateBytes =>
|
||||
string.IsNullOrWhiteSpace(Certificate) ? null : Utils.Base64StringToByteArray(Certificate);
|
||||
public uint Counter { get; set; }
|
||||
public bool Compromised { get; set; }
|
||||
|
||||
private static CBORObject CreatePublicKeyFromU2fRegistrationData(byte[] keyHandleData, byte[] publicKeyData)
|
||||
{
|
||||
var x = new byte[32];
|
||||
var y = new byte[32];
|
||||
Buffer.BlockCopy(publicKeyData, 1, x, 0, 32);
|
||||
Buffer.BlockCopy(publicKeyData, 33, y, 0, 32);
|
||||
|
||||
var point = new ECPoint
|
||||
{
|
||||
X = x,
|
||||
Y = y,
|
||||
};
|
||||
|
||||
var coseKey = CBORObject.NewMap();
|
||||
|
||||
coseKey.Add(COSE.KeyCommonParameter.KeyType, COSE.KeyType.EC2);
|
||||
coseKey.Add(COSE.KeyCommonParameter.Alg, -7);
|
||||
|
||||
coseKey.Add(COSE.KeyTypeParameter.Crv, COSE.EllipticCurve.P256);
|
||||
|
||||
coseKey.Add(COSE.KeyTypeParameter.X, point.X);
|
||||
coseKey.Add(COSE.KeyTypeParameter.Y, point.Y);
|
||||
|
||||
return coseKey;
|
||||
}
|
||||
|
||||
public WebAuthnData ToWebAuthnData()
|
||||
{
|
||||
return new WebAuthnData
|
||||
{
|
||||
Name = Name,
|
||||
Descriptor = new PublicKeyCredentialDescriptor
|
||||
{
|
||||
Id = KeyHandleBytes,
|
||||
Type = PublicKeyCredentialType.PublicKey
|
||||
},
|
||||
PublicKey = CreatePublicKeyFromU2fRegistrationData(KeyHandleBytes, PublicKeyBytes).EncodeToBytes(),
|
||||
SignatureCounter = Counter,
|
||||
Migrated = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class WebAuthnData
|
||||
{
|
||||
public WebAuthnData() { }
|
||||
@@ -101,8 +24,13 @@ namespace Bit.Core.Models
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Handle newtonsoft parsing
|
||||
Descriptor = JsonConvert.DeserializeObject<PublicKeyCredentialDescriptor>(o.Descriptor.ToString());
|
||||
// Fallback for older newtonsoft serialized tokens.
|
||||
if (o.Descriptor.Type == 0)
|
||||
{
|
||||
o.Descriptor.Type = "public-key";
|
||||
}
|
||||
Descriptor = JsonSerializer.Deserialize<PublicKeyCredentialDescriptor>(o.Descriptor.ToString(),
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
}
|
||||
PublicKey = o.PublicKey;
|
||||
UserHandle = o.UserHandle;
|
||||
@@ -130,7 +58,6 @@ namespace Bit.Core.Models
|
||||
{
|
||||
case TwoFactorProviderType.Duo:
|
||||
case TwoFactorProviderType.YubiKey:
|
||||
case TwoFactorProviderType.U2f:
|
||||
case TwoFactorProviderType.WebAuthn:
|
||||
return true;
|
||||
default:
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Entities;
|
||||
|
||||
namespace Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces
|
||||
{
|
||||
public interface IValidateRedemptionTokenCommand
|
||||
{
|
||||
Task<bool> ValidateRedemptionTokenAsync(string encryptedToken, string sponsoredUserEmail);
|
||||
Task<(bool valid, OrganizationSponsorship sponsorship)> ValidateRedemptionTokenAsync(string encryptedToken, string sponsoredUserEmail);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Models.Business.Tokenables;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
|
||||
using Bit.Core.Repositories;
|
||||
@@ -18,20 +19,20 @@ namespace Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnte
|
||||
_tokenFactory = tokenFactory;
|
||||
}
|
||||
|
||||
public async Task<bool> ValidateRedemptionTokenAsync(string encryptedToken, string sponsoredUserEmail)
|
||||
public async Task<(bool valid, OrganizationSponsorship sponsorship)> ValidateRedemptionTokenAsync(string encryptedToken, string sponsoredUserEmail)
|
||||
{
|
||||
|
||||
if (!_tokenFactory.TryUnprotect(encryptedToken, out var tokenable))
|
||||
{
|
||||
return false;
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
var sponsorship = await _organizationSponsorshipRepository.GetByIdAsync(tokenable.Id);
|
||||
if (!tokenable.IsValid(sponsorship, sponsoredUserEmail))
|
||||
{
|
||||
return false;
|
||||
return (false, sponsorship);
|
||||
}
|
||||
return true;
|
||||
return (true, sponsorship);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,5 @@ namespace Bit.Core.Repositories
|
||||
{
|
||||
Task<OrganizationSponsorship> GetBySponsoringOrganizationUserIdAsync(Guid sponsoringOrganizationUserId);
|
||||
Task<OrganizationSponsorship> GetBySponsoredOrganizationIdAsync(Guid sponsoredOrganizationId);
|
||||
Task<OrganizationSponsorship> GetByOfferedToEmailAsync(string email);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Entities;
|
||||
|
||||
namespace Bit.Core.Repositories
|
||||
{
|
||||
public interface IU2fRepository : IRepository<U2f, int>
|
||||
{
|
||||
Task<ICollection<U2f>> GetManyByUserIdAsync(Guid userId);
|
||||
Task DeleteManyByUserIdAsync(Guid userId);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
public interface IOrganizationSponsorshipService
|
||||
{
|
||||
Task<bool> ValidateRedemptionTokenAsync(string encryptedToken, string currentUserEmail);
|
||||
Task<(bool valid, OrganizationSponsorship sponsorship)> ValidateRedemptionTokenAsync(string encryptedToken, string currentUserEmail);
|
||||
Task OfferSponsorshipAsync(Organization sponsoringOrg, OrganizationUser sponsoringOrgUser,
|
||||
PlanSponsorshipType sponsorshipType, string sponsoredEmail, string friendlyName, string sponsoringUserEmail);
|
||||
Task ResendSponsorshipOfferAsync(Organization sponsoringOrg, OrganizationUser sponsoringOrgUser,
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Billing.Models;
|
||||
using Bit.Core.Repositories;
|
||||
@@ -9,8 +11,6 @@ using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -97,14 +97,16 @@ namespace Bit.Core.Services
|
||||
}
|
||||
|
||||
var url = string.Format("https://{0}.itunes.apple.com/verifyReceipt", prod ? "buy" : "sandbox");
|
||||
var json = new JObject(new JProperty("receipt-data", receiptData),
|
||||
new JProperty("password", _globalSettings.AppleIap.Password)).ToString();
|
||||
|
||||
var response = await _httpClient.PostAsync(url, new StringContent(json));
|
||||
var response = await _httpClient.PostAsJsonAsync(url, new AppleVerifyReceiptRequestModel
|
||||
{
|
||||
ReceiptData = receiptData,
|
||||
Password = _globalSettings.AppleIap.Password
|
||||
});
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var responseJson = await response.Content.ReadAsStringAsync();
|
||||
var receiptStatus = JsonConvert.DeserializeObject<AppleReceiptStatus>(responseJson);
|
||||
var receiptStatus = await response.Content.ReadFromJsonAsync<AppleReceiptStatus>();
|
||||
if (receiptStatus.Status == 21007)
|
||||
{
|
||||
return await GetReceiptStatusAsync(receiptData, false, attempt + 1, receiptStatus);
|
||||
@@ -124,4 +126,12 @@ namespace Bit.Core.Services
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class AppleVerifyReceiptRequestModel
|
||||
{
|
||||
[JsonPropertyName("receipt-data")]
|
||||
public string ReceiptData { get; set; }
|
||||
[JsonPropertyName("password")]
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
||||
using Azure.Storage.Queues;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Settings;
|
||||
using Newtonsoft.Json;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -13,7 +13,9 @@ namespace Bit.Core.Services
|
||||
{
|
||||
public AzureQueueEventWriteService(GlobalSettings globalSettings) : base(
|
||||
new QueueClient(globalSettings.Events.ConnectionString, "event"),
|
||||
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })
|
||||
JsonHelpers.IgnoreWritingNull)
|
||||
{ }
|
||||
|
||||
public Task CreateAsync(IEvent e) => CreateManyAsync(new[] { e });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
||||
using Azure.Storage.Queues;
|
||||
using Bit.Core.Models.Mail;
|
||||
using Bit.Core.Settings;
|
||||
using Newtonsoft.Json;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -13,11 +13,11 @@ namespace Bit.Core.Services
|
||||
{
|
||||
public AzureQueueMailService(GlobalSettings globalSettings) : base(
|
||||
new QueueClient(globalSettings.Mail.ConnectionString, "mail"),
|
||||
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })
|
||||
JsonHelpers.IgnoreWritingNull)
|
||||
{ }
|
||||
|
||||
public Task EnqueueAsync(IMailQueueMessage message, Func<IMailQueueMessage, Task> fallback) =>
|
||||
CreateAsync(message);
|
||||
CreateManyAsync(new[] { message });
|
||||
|
||||
public Task EnqueueManyAsync(IEnumerable<IMailQueueMessage> messages, Func<IMailQueueMessage, Task> fallback) =>
|
||||
CreateManyAsync(messages);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.Storage.Queues;
|
||||
using Bit.Core.Context;
|
||||
@@ -7,8 +8,8 @@ using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -18,11 +19,6 @@ namespace Bit.Core.Services
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
private JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
};
|
||||
|
||||
public AzureQueuePushNotificationService(
|
||||
GlobalSettings globalSettings,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
@@ -170,8 +166,8 @@ namespace Bit.Core.Services
|
||||
private async Task SendMessageAsync<T>(PushType type, T payload, bool excludeCurrentContext)
|
||||
{
|
||||
var contextId = GetContextIdentifier(excludeCurrentContext);
|
||||
var message = JsonConvert.SerializeObject(new PushNotificationData<T>(type, payload, contextId),
|
||||
_jsonSettings);
|
||||
var message = JsonSerializer.Serialize(new PushNotificationData<T>(type, payload, contextId),
|
||||
JsonHelpers.IgnoreWritingNull);
|
||||
await _queueClient.SendMessageAsync(message);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.Storage.Queues;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Settings;
|
||||
using Newtonsoft.Json;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -14,10 +15,6 @@ namespace Bit.Core.Services
|
||||
|
||||
private readonly QueueClient _queueClient;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly JsonSerializerSettings _jsonSerializerSettings = new JsonSerializerSettings
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
};
|
||||
|
||||
public AzureQueueReferenceEventService(
|
||||
GlobalSettings globalSettings)
|
||||
@@ -40,7 +37,7 @@ namespace Bit.Core.Services
|
||||
}
|
||||
try
|
||||
{
|
||||
var message = JsonConvert.SerializeObject(referenceEvent, _jsonSerializerSettings);
|
||||
var message = JsonSerializer.Serialize(referenceEvent, JsonHelpers.IgnoreWritingNullAndCamelCase);
|
||||
// Messages need to be base64 encoded
|
||||
var encodedMessage = Convert.ToBase64String(Encoding.UTF8.GetBytes(message));
|
||||
await _queueClient.SendMessageAsync(encodedMessage);
|
||||
|
||||
@@ -1,29 +1,22 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.Storage.Queues;
|
||||
using Bit.Core.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
public abstract class AzureQueueService<T>
|
||||
{
|
||||
protected QueueClient _queueClient;
|
||||
protected JsonSerializerSettings _jsonSettings;
|
||||
protected JsonSerializerOptions _jsonOptions;
|
||||
|
||||
protected AzureQueueService(QueueClient queueClient, JsonSerializerSettings jsonSettings)
|
||||
protected AzureQueueService(QueueClient queueClient, JsonSerializerOptions jsonOptions)
|
||||
{
|
||||
_queueClient = queueClient;
|
||||
_jsonSettings = jsonSettings;
|
||||
}
|
||||
|
||||
public async Task CreateAsync(T message)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(message, _jsonSettings);
|
||||
var base64 = CoreHelpers.Base64EncodeString(json);
|
||||
await _queueClient.SendMessageAsync(base64);
|
||||
_jsonOptions = jsonOptions;
|
||||
}
|
||||
|
||||
public async Task CreateManyAsync(IEnumerable<T> messages)
|
||||
@@ -33,19 +26,13 @@ namespace Bit.Core.Services
|
||||
return;
|
||||
}
|
||||
|
||||
if (!messages.Skip(1).Any())
|
||||
{
|
||||
await CreateAsync(messages.First());
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var json in SerializeMany(messages, _jsonSettings))
|
||||
foreach (var json in SerializeMany(messages, _jsonOptions))
|
||||
{
|
||||
await _queueClient.SendMessageAsync(json);
|
||||
}
|
||||
}
|
||||
|
||||
protected IEnumerable<string> SerializeMany(IEnumerable<T> messages, JsonSerializerSettings jsonSettings)
|
||||
protected IEnumerable<string> SerializeMany(IEnumerable<T> messages, JsonSerializerOptions jsonOptions)
|
||||
{
|
||||
// Calculate Base-64 encoded text with padding
|
||||
int getBase64Size(int byteCount) => ((4 * byteCount / 3) + 3) & ~3;
|
||||
@@ -69,7 +56,7 @@ namespace Bit.Core.Services
|
||||
}
|
||||
|
||||
var serializedMessages = messages.Select(message =>
|
||||
JsonConvert.SerializeObject(message, jsonSettings));
|
||||
JsonSerializer.Serialize(message, jsonOptions));
|
||||
|
||||
foreach (var message in serializedMessages)
|
||||
{
|
||||
|
||||
@@ -3,23 +3,23 @@ using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
public abstract class BaseIdentityClientService
|
||||
public abstract class BaseIdentityClientService : IDisposable
|
||||
{
|
||||
private readonly string _identityScope;
|
||||
private readonly string _identityClientId;
|
||||
private readonly string _identityClientSecret;
|
||||
private readonly ILogger<BaseIdentityClientService> _logger;
|
||||
|
||||
private dynamic _decodedToken;
|
||||
private JsonDocument _decodedToken;
|
||||
private DateTime? _nextAuthAttempt = null;
|
||||
|
||||
public BaseIdentityClientService(
|
||||
@@ -127,9 +127,9 @@ namespace Bit.Core.Services
|
||||
return false;
|
||||
}
|
||||
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
dynamic tokenResponse = JsonConvert.DeserializeObject(responseContent);
|
||||
AccessToken = (string)tokenResponse.access_token;
|
||||
using var jsonDocument = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync());
|
||||
|
||||
AccessToken = jsonDocument.RootElement.GetProperty("access_token").GetString();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -145,8 +145,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
if (requestObject != null)
|
||||
{
|
||||
var stringContent = JsonConvert.SerializeObject(requestObject);
|
||||
Content = new StringContent(stringContent, Encoding.UTF8, "application/json");
|
||||
Content = JsonContent.Create(requestObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,17 +153,16 @@ namespace Bit.Core.Services
|
||||
protected bool TokenNeedsRefresh(int minutes = 5)
|
||||
{
|
||||
var decoded = DecodeToken();
|
||||
var exp = decoded?["exp"];
|
||||
if (exp == null)
|
||||
if (!decoded.RootElement.TryGetProperty("exp", out var expProp))
|
||||
{
|
||||
throw new InvalidOperationException("No exp in token.");
|
||||
}
|
||||
|
||||
var expiration = CoreHelpers.FromEpocSeconds(exp.Value<long>());
|
||||
var expiration = CoreHelpers.FromEpocSeconds(expProp.GetInt64());
|
||||
return DateTime.UtcNow.AddMinutes(-1 * minutes) > expiration;
|
||||
}
|
||||
|
||||
protected JObject DecodeToken()
|
||||
protected JsonDocument DecodeToken()
|
||||
{
|
||||
if (_decodedToken != null)
|
||||
{
|
||||
@@ -188,8 +186,13 @@ namespace Bit.Core.Services
|
||||
throw new InvalidOperationException($"{nameof(AccessToken)} must have 3 parts");
|
||||
}
|
||||
|
||||
_decodedToken = JObject.Parse(Encoding.UTF8.GetString(decodedBytes, 0, decodedBytes.Length));
|
||||
_decodedToken = JsonDocument.Parse(decodedBytes);
|
||||
return _decodedToken;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_decodedToken.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
@@ -12,7 +13,6 @@ using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Core.Models.Data;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -208,7 +208,7 @@ namespace Bit.Core.Services
|
||||
UserId = cipher.UserId,
|
||||
OrganizationId = cipher.OrganizationId,
|
||||
AttachmentId = attachmentId,
|
||||
AttachmentData = JsonConvert.SerializeObject(data)
|
||||
AttachmentData = JsonSerializer.Serialize(data)
|
||||
});
|
||||
cipher.AddAttachment(attachmentId, data);
|
||||
await _pushService.PushSyncCipherUpdateAsync(cipher, null);
|
||||
@@ -241,7 +241,7 @@ namespace Bit.Core.Services
|
||||
UserId = cipher.UserId,
|
||||
OrganizationId = cipher.OrganizationId,
|
||||
AttachmentId = attachmentId,
|
||||
AttachmentData = JsonConvert.SerializeObject(data)
|
||||
AttachmentData = JsonSerializer.Serialize(data)
|
||||
};
|
||||
|
||||
await _cipherRepository.UpdateAttachmentAsync(attachment);
|
||||
@@ -312,7 +312,7 @@ namespace Bit.Core.Services
|
||||
UserId = cipher.UserId,
|
||||
OrganizationId = cipher.OrganizationId,
|
||||
AttachmentId = attachmentId,
|
||||
AttachmentData = JsonConvert.SerializeObject(attachments[attachmentId])
|
||||
AttachmentData = JsonSerializer.Serialize(attachments[attachmentId])
|
||||
};
|
||||
|
||||
await _cipherRepository.UpdateAttachmentAsync(updatedAttachment);
|
||||
@@ -347,7 +347,7 @@ namespace Bit.Core.Services
|
||||
UserId = cipher.UserId,
|
||||
OrganizationId = cipher.OrganizationId,
|
||||
AttachmentId = attachmentData.AttachmentId,
|
||||
AttachmentData = JsonConvert.SerializeObject(attachmentData)
|
||||
AttachmentData = JsonSerializer.Serialize(attachmentData)
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
@@ -8,7 +10,6 @@ using Bit.Core.Models.Business.Tokenables;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tokens;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -77,9 +78,9 @@ namespace Bit.Core.Services
|
||||
return false;
|
||||
}
|
||||
|
||||
var responseContent = await responseMessage.Content.ReadAsStringAsync();
|
||||
dynamic jsonResponse = JsonConvert.DeserializeObject(responseContent);
|
||||
return (bool)jsonResponse.success;
|
||||
using var jsonDocument = await responseMessage.Content.ReadFromJsonAsync<JsonDocument>();
|
||||
var root = jsonDocument.RootElement;
|
||||
return root.GetProperty("success").GetBoolean();
|
||||
}
|
||||
|
||||
public bool RequireCaptchaValidation(ICurrentContext currentContext) =>
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Entities.Provider;
|
||||
@@ -565,6 +566,35 @@ namespace Bit.Core.Services
|
||||
var clickTrackingText = (clickTrackingOff ? "clicktracking=off" : string.Empty);
|
||||
writer.WriteSafeString($"<a href=\"{href}\" target=\"_blank\" {clickTrackingText}>{text}</a>");
|
||||
});
|
||||
|
||||
Handlebars.RegisterHelper("jsonIf", (output, options, context, arguments) =>
|
||||
{
|
||||
// Special case for JsonElement
|
||||
if (arguments[0] is JsonElement jsonElement
|
||||
&& (jsonElement.ValueKind == JsonValueKind.True || jsonElement.ValueKind == JsonValueKind.False))
|
||||
{
|
||||
if (jsonElement.GetBoolean())
|
||||
{
|
||||
options.Template(output, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
options.Inverse(output, context);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback to normal
|
||||
if (HandlebarsUtils.IsTruthy(arguments[0]))
|
||||
{
|
||||
options.Template(output, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
options.Inverse(output, context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task SendEmergencyAccessInviteEmailAsync(EmergencyAccess emergencyAccess, string name, string token)
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Models.Business;
|
||||
@@ -13,7 +14,6 @@ using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -246,7 +246,7 @@ namespace Bit.Core.Services
|
||||
}
|
||||
|
||||
var data = File.ReadAllText(filePath, Encoding.UTF8);
|
||||
return JsonConvert.DeserializeObject<UserLicense>(data);
|
||||
return JsonSerializer.Deserialize<UserLicense>(data);
|
||||
}
|
||||
|
||||
private OrganizationLicense ReadOrganizationLicense(Organization organization)
|
||||
@@ -258,7 +258,7 @@ namespace Bit.Core.Services
|
||||
}
|
||||
|
||||
var data = File.ReadAllText(filePath, Encoding.UTF8);
|
||||
return JsonConvert.DeserializeObject<OrganizationLicense>(data);
|
||||
return JsonSerializer.Deserialize<OrganizationLicense>(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Context;
|
||||
@@ -11,7 +12,6 @@ using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Azure.NotificationHubs;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -229,7 +229,7 @@ namespace Bit.Core.Services
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{ "type", ((byte)type).ToString() },
|
||||
{ "payload", JsonConvert.SerializeObject(payload) }
|
||||
{ "payload", JsonSerializer.Serialize(payload) }
|
||||
}, tag);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ using Bit.Core.Models;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -18,11 +17,6 @@ namespace Bit.Core.Services
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
private JsonSerializerSettings _jsonSettings = new JsonSerializerSettings
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
};
|
||||
|
||||
public NotificationsApiPushNotificationService(
|
||||
GlobalSettings globalSettings,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
|
||||
@@ -15,7 +15,6 @@ using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Stripe;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
@@ -662,6 +661,12 @@ namespace Bit.Core.Services
|
||||
OrganizationLicense license, User owner, string ownerKey, string collectionName, string publicKey,
|
||||
string privateKey)
|
||||
{
|
||||
if (license?.LicenseType != null && license.LicenseType != LicenseType.Organization)
|
||||
{
|
||||
throw new BadRequestException("Premium licenses cannot be applied to an organization. "
|
||||
+ "Upload this license from your personal account settings page.");
|
||||
}
|
||||
|
||||
if (license == null || !_licensingService.VerifyLicense(license))
|
||||
{
|
||||
throw new BadRequestException("Invalid license.");
|
||||
@@ -727,8 +732,8 @@ namespace Bit.Core.Services
|
||||
|
||||
var dir = $"{_globalSettings.LicenseDirectory}/organization";
|
||||
Directory.CreateDirectory(dir);
|
||||
System.IO.File.WriteAllText($"{dir}/{organization.Id}.json",
|
||||
JsonConvert.SerializeObject(license, Formatting.Indented));
|
||||
using var fs = System.IO.File.OpenWrite(Path.Combine(dir, $"{organization.Id}.json"));
|
||||
await JsonSerializer.SerializeAsync(fs, license, JsonHelpers.Indented);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -807,6 +812,12 @@ namespace Bit.Core.Services
|
||||
throw new InvalidOperationException("Licenses require self hosting.");
|
||||
}
|
||||
|
||||
if (license?.LicenseType != null && license.LicenseType != LicenseType.Organization)
|
||||
{
|
||||
throw new BadRequestException("Premium licenses cannot be applied to an organization. "
|
||||
+ "Upload this license from your personal account settings page.");
|
||||
}
|
||||
|
||||
if (license == null || !_licensingService.VerifyLicense(license))
|
||||
{
|
||||
throw new BadRequestException("Invalid license.");
|
||||
@@ -900,8 +911,8 @@ namespace Bit.Core.Services
|
||||
|
||||
var dir = $"{_globalSettings.LicenseDirectory}/organization";
|
||||
Directory.CreateDirectory(dir);
|
||||
System.IO.File.WriteAllText($"{dir}/{organization.Id}.json",
|
||||
JsonConvert.SerializeObject(license, Formatting.Indented));
|
||||
using var fs = System.IO.File.OpenWrite(Path.Combine(dir, $"{organization.Id}.json"));
|
||||
await JsonSerializer.SerializeAsync(fs, license, JsonHelpers.Indented);
|
||||
|
||||
organization.Name = license.Name;
|
||||
organization.BusinessName = license.BusinessName;
|
||||
@@ -1146,10 +1157,7 @@ namespace Bit.Core.Services
|
||||
|
||||
if (invite.Permissions != null)
|
||||
{
|
||||
orgUser.Permissions = System.Text.Json.JsonSerializer.Serialize(invite.Permissions, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
});
|
||||
orgUser.Permissions = JsonSerializer.Serialize(invite.Permissions, JsonHelpers.CamelCase);
|
||||
}
|
||||
|
||||
if (!orgUser.AccessAll && invite.Collections.Any())
|
||||
@@ -1765,7 +1773,7 @@ namespace Bit.Core.Services
|
||||
// Block the user from withdrawal if auto enrollment is enabled
|
||||
if (resetPasswordKey == null && resetPasswordPolicy.Data != null)
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<ResetPasswordDataModel>(resetPasswordPolicy.Data);
|
||||
var data = JsonSerializer.Deserialize<ResetPasswordDataModel>(resetPasswordPolicy.Data);
|
||||
|
||||
if (data?.AutoEnrollEnabled ?? false)
|
||||
{
|
||||
|
||||
@@ -37,11 +37,11 @@ namespace Bit.Core.Services
|
||||
_dataProtector = dataProtectionProvider.CreateProtector("OrganizationSponsorshipServiceDataProtector");
|
||||
}
|
||||
|
||||
public async Task<bool> ValidateRedemptionTokenAsync(string encryptedToken, string sponsoredUserEmail)
|
||||
public async Task<(bool valid, OrganizationSponsorship sponsorship)> ValidateRedemptionTokenAsync(string encryptedToken, string sponsoredUserEmail)
|
||||
{
|
||||
if (!encryptedToken.StartsWith(TokenClearTextPrefix) || sponsoredUserEmail == null)
|
||||
{
|
||||
return false;
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
var decryptedToken = _dataProtector.Unprotect(encryptedToken[TokenClearTextPrefix.Length..]);
|
||||
@@ -49,7 +49,7 @@ namespace Bit.Core.Services
|
||||
|
||||
if (dataParts.Length != 3)
|
||||
{
|
||||
return false;
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
if (dataParts[0].Equals(FamiliesForEnterpriseTokenName))
|
||||
@@ -57,7 +57,7 @@ namespace Bit.Core.Services
|
||||
if (!Guid.TryParse(dataParts[1], out Guid sponsorshipId) ||
|
||||
!Enum.TryParse<PlanSponsorshipType>(dataParts[2], true, out var sponsorshipType))
|
||||
{
|
||||
return false;
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
var sponsorship = await _organizationSponsorshipRepository.GetByIdAsync(sponsorshipId);
|
||||
@@ -65,13 +65,13 @@ namespace Bit.Core.Services
|
||||
sponsorship.PlanSponsorshipType != sponsorshipType ||
|
||||
sponsorship.OfferedToEmail != sponsoredUserEmail)
|
||||
{
|
||||
return false;
|
||||
return (false, sponsorship);
|
||||
}
|
||||
|
||||
return true;
|
||||
return (true, sponsorship);
|
||||
}
|
||||
|
||||
return false;
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
private string RedemptionToken(Guid sponsorshipId, PlanSponsorshipType sponsorshipType) =>
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Net.Http.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -76,15 +75,13 @@ namespace Bit.Core.Services
|
||||
request.tag = string.Concat(request.tag, "-Cat_", message.Category);
|
||||
}
|
||||
|
||||
var reqJson = JsonConvert.SerializeObject(request);
|
||||
var responseMessage = await httpClient.PostAsync(
|
||||
var responseMessage = await httpClient.PostAsJsonAsync(
|
||||
$"https://{_globalSettings.Mail.PostalDomain}/api/v1/send/message",
|
||||
new StringContent(reqJson, Encoding.UTF8, "application/json"));
|
||||
request);
|
||||
|
||||
if (responseMessage.IsSuccessStatusCode)
|
||||
{
|
||||
var json = await responseMessage.Content.ReadAsStringAsync();
|
||||
var response = JsonConvert.DeserializeObject<PostalResponse>(json);
|
||||
var response = await responseMessage.Content.ReadFromJsonAsync<PostalResponse>();
|
||||
if (response.status != "success")
|
||||
{
|
||||
_logger.LogError("Postal send status was not successful: {0}, {1}",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
@@ -12,7 +13,6 @@ using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -104,8 +104,8 @@ namespace Bit.Core.Services
|
||||
data.Id = fileId;
|
||||
data.Size = fileLength;
|
||||
data.Validated = false;
|
||||
send.Data = JsonConvert.SerializeObject(data,
|
||||
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||
send.Data = JsonSerializer.Serialize(data,
|
||||
JsonHelpers.IgnoreWritingNull);
|
||||
await SaveSendAsync(send);
|
||||
return await _sendFileStorageService.GetSendFileUploadUrlAsync(send, fileId);
|
||||
}
|
||||
@@ -129,7 +129,7 @@ namespace Bit.Core.Services
|
||||
throw new BadRequestException("Not a File Type Send.");
|
||||
}
|
||||
|
||||
var data = JsonConvert.DeserializeObject<SendFileData>(send.Data);
|
||||
var data = JsonSerializer.Deserialize<SendFileData>(send.Data);
|
||||
|
||||
if (data.Validated)
|
||||
{
|
||||
@@ -146,7 +146,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<bool> ValidateSendFile(Send send)
|
||||
{
|
||||
var fileData = JsonConvert.DeserializeObject<SendFileData>(send.Data);
|
||||
var fileData = JsonSerializer.Deserialize<SendFileData>(send.Data);
|
||||
|
||||
var (valid, realSize) = await _sendFileStorageService.ValidateFileAsync(send, fileData.Id, fileData.Size, _fileSizeLeeway);
|
||||
|
||||
@@ -163,8 +163,8 @@ namespace Bit.Core.Services
|
||||
fileData.Size = realSize.Value;
|
||||
}
|
||||
fileData.Validated = true;
|
||||
send.Data = JsonConvert.SerializeObject(fileData,
|
||||
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||
send.Data = JsonSerializer.Serialize(fileData,
|
||||
JsonHelpers.IgnoreWritingNull);
|
||||
await SaveSendAsync(send);
|
||||
|
||||
return valid;
|
||||
@@ -175,7 +175,7 @@ namespace Bit.Core.Services
|
||||
await _sendRepository.DeleteAsync(send);
|
||||
if (send.Type == Enums.SendType.File)
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<SendFileData>(send.Data);
|
||||
var data = JsonSerializer.Deserialize<SendFileData>(send.Data);
|
||||
await _sendFileStorageService.DeleteFileAsync(send, data.Id);
|
||||
}
|
||||
await _pushService.PushSyncSendDeleteAsync(send);
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
@@ -19,9 +20,7 @@ using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using File = System.IO.File;
|
||||
using U2fLib = U2F.Core.Crypto.U2F;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
@@ -481,25 +480,6 @@ namespace Bit.Core.Services
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete U2F token is this is a migrated WebAuthn token.
|
||||
var entry = new TwoFactorProvider.WebAuthnData(provider.MetaData[keyName]);
|
||||
if (entry?.Migrated ?? false)
|
||||
{
|
||||
var u2fProvider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
|
||||
if (u2fProvider?.MetaData?.ContainsKey(keyName) ?? false)
|
||||
{
|
||||
u2fProvider.MetaData.Remove(keyName);
|
||||
if (u2fProvider.MetaData.Count > 0)
|
||||
{
|
||||
providers[TwoFactorProviderType.U2f] = u2fProvider;
|
||||
}
|
||||
else
|
||||
{
|
||||
providers.Remove(TwoFactorProviderType.U2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider.MetaData.Remove(keyName);
|
||||
providers[TwoFactorProviderType.WebAuthn] = provider;
|
||||
user.SetTwoFactorProviders(providers);
|
||||
@@ -899,13 +879,6 @@ namespace Bit.Core.Services
|
||||
return;
|
||||
}
|
||||
|
||||
// Since the user can no longer directly manipulate U2F tokens, we should
|
||||
// disable them when the user disables WebAuthn.
|
||||
if (type == TwoFactorProviderType.WebAuthn)
|
||||
{
|
||||
providers.Remove(TwoFactorProviderType.U2f);
|
||||
}
|
||||
|
||||
providers.Remove(type);
|
||||
user.SetTwoFactorProviders(providers);
|
||||
await SaveUserAsync(user);
|
||||
@@ -983,7 +956,8 @@ namespace Bit.Core.Services
|
||||
|
||||
var dir = $"{_globalSettings.LicenseDirectory}/user";
|
||||
Directory.CreateDirectory(dir);
|
||||
File.WriteAllText($"{dir}/{user.Id}.json", JsonConvert.SerializeObject(license, Formatting.Indented));
|
||||
using var fs = File.OpenWrite(Path.Combine(dir, $"{user.Id}.json"));
|
||||
await JsonSerializer.SerializeAsync(fs, license, JsonHelpers.Indented);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1056,6 +1030,12 @@ namespace Bit.Core.Services
|
||||
throw new InvalidOperationException("Licenses require self hosting.");
|
||||
}
|
||||
|
||||
if (license?.LicenseType != null && license.LicenseType != LicenseType.User)
|
||||
{
|
||||
throw new BadRequestException("Organization licenses cannot be applied to a user. "
|
||||
+ "Upload this license from the Organization settings page.");
|
||||
}
|
||||
|
||||
if (license == null || !_licenseService.VerifyLicense(license))
|
||||
{
|
||||
throw new BadRequestException("Invalid license.");
|
||||
@@ -1068,7 +1048,8 @@ namespace Bit.Core.Services
|
||||
|
||||
var dir = $"{_globalSettings.LicenseDirectory}/user";
|
||||
Directory.CreateDirectory(dir);
|
||||
File.WriteAllText($"{dir}/{user.Id}.json", JsonConvert.SerializeObject(license, Formatting.Indented));
|
||||
using var fs = File.OpenWrite(Path.Combine(dir, $"{user.Id}.json"));
|
||||
await JsonSerializer.SerializeAsync(fs, license, JsonHelpers.Indented);
|
||||
|
||||
user.Premium = license.Premium;
|
||||
user.RevisionDate = DateTime.UtcNow;
|
||||
|
||||
@@ -23,7 +23,6 @@ using Bit.Core.Settings;
|
||||
using IdentityModel;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using MimeKit;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Utilities
|
||||
{
|
||||
@@ -332,12 +331,12 @@ namespace Bit.Core.Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Creates a clone of the given object through serializing to json and deserializing.
|
||||
/// This method is subject to the limitations of Newstonsoft. For example, properties with
|
||||
/// This method is subject to the limitations of System.Text.Json. For example, properties with
|
||||
/// inaccessible setters will not be set.
|
||||
/// </summary>
|
||||
public static T CloneObject<T>(T obj)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
|
||||
return JsonSerializer.Deserialize<T>(JsonSerializer.Serialize(obj));
|
||||
}
|
||||
|
||||
public static bool SettingHasValue(string setting)
|
||||
|
||||
@@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Utilities
|
||||
{
|
||||
@@ -41,10 +40,8 @@ namespace Bit.Core.Utilities
|
||||
$"Slow down! Too many requests. Try again in {rule.Period}." : _options.QuotaExceededMessage;
|
||||
httpContext.Response.Headers["Retry-After"] = retryAfter;
|
||||
httpContext.Response.StatusCode = _options.HttpStatusCode;
|
||||
|
||||
httpContext.Response.ContentType = "application/json";
|
||||
var errorModel = new ErrorResponseModel { Message = message };
|
||||
return httpContext.Response.WriteAsync(JsonConvert.SerializeObject(errorModel));
|
||||
return httpContext.Response.WriteAsJsonAsync(errorModel, cancellationToken: httpContext.RequestAborted);
|
||||
}
|
||||
|
||||
public override void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity,
|
||||
|
||||
@@ -15,9 +15,9 @@ using System.IO;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Bit.Core.Utilities.Duo
|
||||
{
|
||||
@@ -175,22 +175,21 @@ namespace Bit.Core.Utilities.Duo
|
||||
var res = ApiCall(method, path, parameters, timeout, out var statusCode);
|
||||
try
|
||||
{
|
||||
var dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(res);
|
||||
if (dict["stat"] as string == "OK")
|
||||
// TODO: We should deserialize this into our own DTO and not work on dictionaries.
|
||||
var dict = JsonSerializer.Deserialize<Dictionary<string, object>>(res);
|
||||
if (dict["stat"].ToString() == "OK")
|
||||
{
|
||||
return dict["response"] as T;
|
||||
return JsonSerializer.Deserialize<T>(dict["response"].ToString());
|
||||
}
|
||||
else
|
||||
|
||||
var check = ToNullableInt(dict["code"].ToString());
|
||||
var code = check.GetValueOrDefault(0);
|
||||
var messageDetail = string.Empty;
|
||||
if (dict.ContainsKey("message_detail"))
|
||||
{
|
||||
var check = dict["code"] as int?;
|
||||
var code = check.GetValueOrDefault(0);
|
||||
var messageDetail = string.Empty;
|
||||
if (dict.ContainsKey("message_detail"))
|
||||
{
|
||||
messageDetail = dict["message_detail"] as string;
|
||||
}
|
||||
throw new ApiException(code, (int)statusCode, dict["message"] as string, messageDetail);
|
||||
messageDetail = dict["message_detail"].ToString();
|
||||
}
|
||||
throw new ApiException(code, (int)statusCode, dict["message"].ToString(), messageDetail);
|
||||
}
|
||||
catch (ApiException)
|
||||
{
|
||||
@@ -202,6 +201,16 @@ namespace Bit.Core.Utilities.Duo
|
||||
}
|
||||
}
|
||||
|
||||
private int? ToNullableInt(string s)
|
||||
{
|
||||
int i;
|
||||
if (int.TryParse(s, out i))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private string HmacSign(string data)
|
||||
{
|
||||
var keyBytes = Encoding.ASCII.GetBytes(_skey);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user